0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: * Cagatay Kavukcuoglu <cagatayk@acm.org>
0011: * - Fix for bug 10025 - Resizing views should not use height ratios
0012: * Chris Gross chris.gross@us.ibm.com Bug 107443
0013: *******************************************************************************/package org.eclipse.ui.internal;
0014:
0015: import java.util.ArrayList;
0016:
0017: import org.eclipse.core.runtime.Assert;
0018: import org.eclipse.jface.util.Geometry;
0019: import org.eclipse.swt.SWT;
0020: import org.eclipse.swt.events.ControlAdapter;
0021: import org.eclipse.swt.events.ControlEvent;
0022: import org.eclipse.swt.events.ControlListener;
0023: import org.eclipse.swt.graphics.Cursor;
0024: import org.eclipse.swt.graphics.Point;
0025: import org.eclipse.swt.graphics.Rectangle;
0026: import org.eclipse.swt.widgets.Composite;
0027: import org.eclipse.swt.widgets.Control;
0028: import org.eclipse.ui.IPageLayout;
0029: import org.eclipse.ui.IViewReference;
0030: import org.eclipse.ui.internal.dnd.AbstractDropTarget;
0031: import org.eclipse.ui.internal.dnd.DragUtil;
0032: import org.eclipse.ui.internal.dnd.IDragOverListener;
0033: import org.eclipse.ui.internal.dnd.IDropTarget;
0034: import org.eclipse.ui.internal.dnd.SwtUtil;
0035:
0036: /**
0037: * Abstract container that groups various layout
0038: * parts (possibly other containers) together as
0039: * a unit. Manages the placement and size of these
0040: * layout part based on the location of sashes within
0041: * the container.
0042: */
0043: public abstract class PartSashContainer extends LayoutPart implements
0044: ILayoutContainer, IDragOverListener {
0045:
0046: protected Composite parent;
0047:
0048: protected ControlListener resizeListener;
0049:
0050: protected LayoutTree root;
0051:
0052: private Composite parentWidget;
0053:
0054: private LayoutPart zoomedPart;
0055:
0056: protected WorkbenchPage page;
0057:
0058: boolean active = false;
0059: boolean layoutDirty = false;
0060:
0061: /* Array of LayoutPart */
0062: protected ArrayList children = new ArrayList();
0063:
0064: private SashContainerDropTarget dropTarget;
0065:
0066: protected static class RelationshipInfo {
0067: protected LayoutPart part;
0068:
0069: protected LayoutPart relative;
0070:
0071: protected int relationship;
0072:
0073: /**
0074: * Preferred size for the left child (this would be the size, in pixels of the child
0075: * at the time the sash was last moved)
0076: */
0077: protected int left;
0078:
0079: /**
0080: * Preferred size for the right child (this would be the size, in pixels of the child
0081: * at the time the sash was last moved)
0082: */
0083: protected int right;
0084:
0085: /**
0086: * Computes the "ratio" for this container. That is, the ratio of the left side over
0087: * the sum of left + right. This is only used for serializing PartSashContainers in
0088: * a form that can be read by old versions of Eclipse. This can be removed if this
0089: * is no longer required.
0090: *
0091: * @return the pre-Eclipse 3.0 sash ratio
0092: */
0093: public float getRatio() {
0094: int total = left + right;
0095: if (total > 0) {
0096: return (float) left / (float) total;
0097: }
0098:
0099: return 0.5f;
0100: }
0101: }
0102:
0103: private class SashContainerDropTarget extends AbstractDropTarget {
0104: private int side;
0105:
0106: private int cursor;
0107:
0108: private LayoutPart targetPart;
0109:
0110: private LayoutPart sourcePart;
0111:
0112: public SashContainerDropTarget(LayoutPart sourcePart, int side,
0113: int cursor, LayoutPart targetPart) {
0114: this .setTarget(sourcePart, side, cursor, targetPart);
0115: }
0116:
0117: public void setTarget(LayoutPart sourcePart, int side,
0118: int cursor, LayoutPart targetPart) {
0119: this .side = side;
0120: this .targetPart = targetPart;
0121: this .sourcePart = sourcePart;
0122: this .cursor = cursor;
0123: }
0124:
0125: public void drop() {
0126: if (side != SWT.NONE) {
0127: LayoutPart visiblePart = sourcePart;
0128:
0129: if (sourcePart instanceof PartStack) {
0130: visiblePart = getVisiblePart((PartStack) sourcePart);
0131: }
0132:
0133: dropObject(getVisibleParts(sourcePart), visiblePart,
0134: targetPart, side);
0135: }
0136: }
0137:
0138: public Cursor getCursor() {
0139: return DragCursors.getCursor(DragCursors
0140: .positionToDragCursor(cursor));
0141: }
0142:
0143: public Rectangle getSnapRectangle() {
0144: Rectangle targetBounds;
0145:
0146: if (targetPart != null) {
0147: targetBounds = DragUtil.getDisplayBounds(targetPart
0148: .getControl());
0149: } else {
0150: targetBounds = DragUtil.getDisplayBounds(getParent());
0151: }
0152:
0153: if (side == SWT.CENTER || side == SWT.NONE) {
0154: return targetBounds;
0155: }
0156:
0157: int distance = Geometry.getDimension(targetBounds,
0158: !Geometry.isHorizontal(side));
0159:
0160: return Geometry.getExtrudedEdge(targetBounds,
0161: (int) (distance * getDockingRatio(sourcePart,
0162: targetPart)), side);
0163: }
0164: }
0165:
0166: public PartSashContainer(String id, final WorkbenchPage page,
0167: Composite parentWidget) {
0168: super (id);
0169: this .page = page;
0170: this .parentWidget = parentWidget;
0171: resizeListener = new ControlAdapter() {
0172: public void controlResized(ControlEvent e) {
0173: resizeSashes();
0174: }
0175: };
0176: }
0177:
0178: /* (non-Javadoc)
0179: * @see org.eclipse.ui.internal.ILayoutContainer#obscuredByZoom(org.eclipse.ui.internal.LayoutPart)
0180: */
0181: public boolean childObscuredByZoom(LayoutPart toTest) {
0182: LayoutPart zoomPart = getZoomedPart();
0183:
0184: if (zoomPart != null && toTest != zoomPart) {
0185: return true;
0186: }
0187: return isObscuredByZoom();
0188: }
0189:
0190: /**
0191: * Given an object associated with a drag (a PartPane or PartStack), this returns
0192: * the actual PartPanes being dragged.
0193: *
0194: * @param pane
0195: * @return
0196: */
0197: private PartPane[] getVisibleParts(LayoutPart pane) {
0198: if (pane instanceof PartPane) {
0199: return new PartPane[] { (PartPane) pane };
0200: } else if (pane instanceof PartStack) {
0201: PartStack stack = (PartStack) pane;
0202:
0203: LayoutPart[] children = stack.getChildren();
0204: ArrayList result = new ArrayList(children.length);
0205: for (int idx = 0; idx < children.length; idx++) {
0206: LayoutPart next = children[idx];
0207: if (next instanceof PartPane) {
0208: result.add(next);
0209: }
0210: }
0211:
0212: return (PartPane[]) result.toArray(new PartPane[result
0213: .size()]);
0214: }
0215:
0216: return new PartPane[0];
0217: }
0218:
0219: /**
0220: * Find the sashs around the specified part.
0221: */
0222: public void findSashes(LayoutPart pane, PartPane.Sashes sashes) {
0223: if (root == null) {
0224: return;
0225: }
0226: LayoutTree part = root.find(pane);
0227: if (part == null) {
0228: return;
0229: }
0230: part.findSashes(sashes);
0231: }
0232:
0233: /**
0234: * Add a part.
0235: */
0236: public void add(LayoutPart child) {
0237: if (child == null) {
0238: return;
0239: }
0240:
0241: addEnhanced(child, SWT.RIGHT, 0.5f, findBottomRight());
0242: }
0243:
0244: /**
0245: * Add a new part relative to another. This should be used in place of <code>add</code>.
0246: * It differs as follows:
0247: * <ul>
0248: * <li>relationships are specified using SWT direction constants</li>
0249: * <li>the ratio applies to the newly added child -- not the upper-left child</li>
0250: * </ul>
0251: *
0252: * @param child new part to add to the layout
0253: * @param swtDirectionConstant one of SWT.TOP, SWT.BOTTOM, SWT.LEFT, or SWT.RIGHT
0254: * @param ratioForNewPart a value between 0.0 and 1.0 specifying how much space will be allocated for the newly added part
0255: * @param relative existing part indicating where the new child should be attached
0256: * @since 3.0
0257: */
0258: void addEnhanced(LayoutPart child, int swtDirectionConstant,
0259: float ratioForNewPart, LayoutPart relative) {
0260: int relativePosition = PageLayout
0261: .swtConstantToLayoutPosition(swtDirectionConstant);
0262:
0263: float ratioForUpperLeftPart;
0264:
0265: if (relativePosition == IPageLayout.RIGHT
0266: || relativePosition == IPageLayout.BOTTOM) {
0267: ratioForUpperLeftPart = 1.0f - ratioForNewPart;
0268: } else {
0269: ratioForUpperLeftPart = ratioForNewPart;
0270: }
0271:
0272: add(child, relativePosition, ratioForUpperLeftPart, relative);
0273: }
0274:
0275: /**
0276: * Add a part relative to another. For compatibility only. New code should use
0277: * addEnhanced, above.
0278: *
0279: * @param child the new part to add
0280: * @param relationship one of PageLayout.TOP, PageLayout.BOTTOM, PageLayout.LEFT, or PageLayout.RIGHT
0281: * @param ratio a value between 0.0 and 1.0, indicating how much space will be allocated to the UPPER-LEFT pane
0282: * @param relative part where the new part will be attached
0283: */
0284: public void add(LayoutPart child, int relationship, float ratio,
0285: LayoutPart relative) {
0286: boolean isHorizontal = (relationship == IPageLayout.LEFT || relationship == IPageLayout.RIGHT);
0287:
0288: LayoutTree node = null;
0289: if (root != null && relative != null) {
0290: node = root.find(relative);
0291: }
0292:
0293: Rectangle bounds;
0294: if (getParent() == null) {
0295: Control control = getPage().getClientComposite();
0296: if (control != null && !control.isDisposed()) {
0297: bounds = control.getBounds();
0298: } else {
0299: bounds = new Rectangle(0, 0, 800, 600);
0300: }
0301: bounds.x = 0;
0302: bounds.y = 0;
0303: } else {
0304: bounds = getBounds();
0305: }
0306:
0307: int totalSize = measureTree(bounds, node, isHorizontal);
0308:
0309: int left = (int) (totalSize * ratio);
0310: int right = totalSize - left;
0311:
0312: add(child, relationship, left, right, relative);
0313: }
0314:
0315: static int measureTree(Rectangle outerBounds, LayoutTree toMeasure,
0316: boolean horizontal) {
0317: if (toMeasure == null) {
0318: return Geometry.getDimension(outerBounds, horizontal);
0319: }
0320:
0321: LayoutTreeNode parent = toMeasure.getParent();
0322: if (parent == null) {
0323: return Geometry.getDimension(outerBounds, horizontal);
0324: }
0325:
0326: if (parent.getSash().isHorizontal() == horizontal) {
0327: return measureTree(outerBounds, parent, horizontal);
0328: }
0329:
0330: boolean isLeft = parent.isLeftChild(toMeasure);
0331:
0332: LayoutTree otherChild = parent.getChild(!isLeft);
0333: if (otherChild.isVisible()) {
0334: int left = parent.getSash().getLeft();
0335: int right = parent.getSash().getRight();
0336: int childSize = isLeft ? left : right;
0337:
0338: int bias = parent.getCompressionBias();
0339:
0340: // Normalize bias: 1 = we're fixed, -1 = other child is fixed
0341: if (isLeft) {
0342: bias = -bias;
0343: }
0344:
0345: if (bias == 1) {
0346: // If we're fixed, return the fixed size
0347: return childSize;
0348: } else if (bias == -1) {
0349:
0350: // If the other child is fixed, return the size of the parent minus the fixed size of the
0351: // other child
0352: return measureTree(outerBounds, parent, horizontal)
0353: - (left + right - childSize);
0354: }
0355:
0356: // Else return the size of the parent, scaled appropriately
0357: return measureTree(outerBounds, parent, horizontal)
0358: * childSize / (left + right);
0359: }
0360:
0361: return measureTree(outerBounds, parent, horizontal);
0362: }
0363:
0364: protected void addChild(RelationshipInfo info) {
0365: LayoutPart child = info.part;
0366:
0367: children.add(child);
0368:
0369: if (root == null) {
0370: root = new LayoutTree(child);
0371: } else {
0372: //Add the part to the tree.
0373: int vertical = (info.relationship == IPageLayout.LEFT || info.relationship == IPageLayout.RIGHT) ? SWT.VERTICAL
0374: : SWT.HORIZONTAL;
0375: boolean left = info.relationship == IPageLayout.LEFT
0376: || info.relationship == IPageLayout.TOP;
0377: LayoutPartSash sash = new LayoutPartSash(this , vertical);
0378: sash.setSizes(info.left, info.right);
0379: if ((parent != null) && !(child instanceof PartPlaceholder)) {
0380: sash.createControl(parent);
0381: }
0382: root = root.insert(child, left, sash, info.relative);
0383: }
0384:
0385: childAdded(child);
0386:
0387: if (active) {
0388: child.createControl(parent);
0389: child.setVisible(true);
0390: child.setContainer(this );
0391: resizeChild(child);
0392: }
0393:
0394: }
0395:
0396: /**
0397: * Adds the child using ratio and position attributes
0398: * from the specified placeholder without replacing
0399: * the placeholder
0400: *
0401: * FIXME: I believe there is a bug in computeRelation()
0402: * when a part is positioned relative to the editorarea.
0403: * We end up with a null relative and 0.0 for a ratio.
0404: */
0405: void addChildForPlaceholder(LayoutPart child, LayoutPart placeholder) {
0406: RelationshipInfo newRelationshipInfo = new RelationshipInfo();
0407: newRelationshipInfo.part = child;
0408: if (root != null) {
0409: newRelationshipInfo.relationship = IPageLayout.RIGHT;
0410: newRelationshipInfo.relative = root.findBottomRight();
0411: newRelationshipInfo.left = 200;
0412: newRelationshipInfo.right = 200;
0413: }
0414:
0415: // find the relationship info for the placeholder
0416: RelationshipInfo[] relationships = computeRelation();
0417: for (int i = 0; i < relationships.length; i++) {
0418: RelationshipInfo info = relationships[i];
0419: if (info.part == placeholder) {
0420: newRelationshipInfo.left = info.left;
0421: newRelationshipInfo.right = info.right;
0422: newRelationshipInfo.relationship = info.relationship;
0423: newRelationshipInfo.relative = info.relative;
0424: }
0425: }
0426:
0427: addChild(newRelationshipInfo);
0428: flushLayout();
0429: }
0430:
0431: /**
0432: * See ILayoutContainer#allowBorder
0433: */
0434: public boolean allowsBorder() {
0435: return true;
0436: }
0437:
0438: /**
0439: * Notification that a child layout part has been
0440: * added to the container. Subclasses may override
0441: * this method to perform any container specific
0442: * work.
0443: */
0444: protected void childAdded(LayoutPart child) {
0445: if (isDeferred()) {
0446: child.deferUpdates(true);
0447: }
0448: }
0449:
0450: /**
0451: * Notification that a child layout part has been
0452: * removed from the container. Subclasses may override
0453: * this method to perform any container specific
0454: * work.
0455: */
0456: protected void childRemoved(LayoutPart child) {
0457: if (isDeferred()) {
0458: child.deferUpdates(false);
0459: }
0460: }
0461:
0462: /**
0463: * Returns an array with all the relation ship between the
0464: * parts.
0465: */
0466: public RelationshipInfo[] computeRelation() {
0467: LayoutTree treeRoot = root;
0468: ArrayList list = new ArrayList();
0469: if (treeRoot == null) {
0470: return new RelationshipInfo[0];
0471: }
0472: RelationshipInfo r = new RelationshipInfo();
0473: r.part = treeRoot.computeRelation(list);
0474: list.add(0, r);
0475: RelationshipInfo[] result = new RelationshipInfo[list.size()];
0476: list.toArray(result);
0477: return result;
0478: }
0479:
0480: public void setActive(boolean isActive) {
0481: if (isActive == active) {
0482: return;
0483: }
0484:
0485: active = isActive;
0486:
0487: ArrayList children = (ArrayList) this .children.clone();
0488: for (int i = 0, length = children.size(); i < length; i++) {
0489: LayoutPart child = (LayoutPart) children.get(i);
0490:
0491: if (child instanceof PartStack) {
0492: PartStack stack = (PartStack) child;
0493: stack.setActive(isActive);
0494: }
0495: }
0496:
0497: if (isActive) {
0498:
0499: createControl(parentWidget);
0500:
0501: parent.addControlListener(resizeListener);
0502:
0503: DragUtil.addDragTarget(parent, this );
0504: DragUtil.addDragTarget(parent.getShell(), this );
0505:
0506: //ArrayList children = (ArrayList) this.children.clone();
0507: for (int i = 0, length = children.size(); i < length; i++) {
0508: LayoutPart child = (LayoutPart) children.get(i);
0509: child.setContainer(this );
0510: child.setVisible(zoomedPart == null
0511: || child == zoomedPart);
0512: if (!(child instanceof PartStack)) {
0513: if (root != null) {
0514: LayoutTree node = root.find(child);
0515: if (node != null) {
0516: node.flushCache();
0517: }
0518: }
0519: }
0520: }
0521:
0522: if (root != null) {
0523: //root.flushChildren();
0524: if (!isZoomed()) {
0525: root.createControl(parent);
0526: }
0527: }
0528:
0529: resizeSashes();
0530: } else {
0531: DragUtil.removeDragTarget(parent, this );
0532: DragUtil.removeDragTarget(parent.getShell(), this );
0533:
0534: // remove all Listeners
0535: if (resizeListener != null && parent != null) {
0536: parent.removeControlListener(resizeListener);
0537: }
0538:
0539: if (children != null) {
0540: for (int i = 0, length = children.size(); i < length; i++) {
0541: LayoutPart child = (LayoutPart) children.get(i);
0542: child.setContainer(null);
0543: if (child instanceof PartStack) {
0544: child.setVisible(false);
0545: }
0546: }
0547: }
0548:
0549: disposeSashes();
0550:
0551: //dispose();
0552: }
0553: }
0554:
0555: /**
0556: * @see LayoutPart#getControl
0557: */
0558: public void createControl(Composite parentWidget) {
0559: if (this .parent != null) {
0560: return;
0561: }
0562:
0563: parent = createParent(parentWidget);
0564:
0565: ArrayList children = (ArrayList) this .children.clone();
0566: for (int i = 0, length = children.size(); i < length; i++) {
0567: LayoutPart child = (LayoutPart) children.get(i);
0568: child.createControl(parent);
0569: }
0570:
0571: }
0572:
0573: /**
0574: * Subclasses override this method to specify
0575: * the composite to use to parent all children
0576: * layout parts it contains.
0577: */
0578: protected abstract Composite createParent(Composite parentWidget);
0579:
0580: /**
0581: * @see LayoutPart#dispose
0582: */
0583: public void dispose() {
0584: if (parent == null) {
0585: return;
0586: }
0587:
0588: if (children != null) {
0589: for (int i = 0, length = children.size(); i < length; i++) {
0590: LayoutPart child = (LayoutPart) children.get(i);
0591:
0592: // In PartSashContainer dispose really means deactivate, so we
0593: // only dispose PartTabFolders.
0594: if (child instanceof PartStack) {
0595: child.dispose();
0596: }
0597: }
0598: }
0599: disposeParent();
0600: this .parent = null;
0601: }
0602:
0603: /**
0604: * Subclasses override this method to dispose
0605: * of any swt resources created during createParent.
0606: */
0607: protected abstract void disposeParent();
0608:
0609: /**
0610: * Dispose all sashs used in this perspective.
0611: */
0612: public void disposeSashes() {
0613: if (root != null) {
0614: root.disposeSashes();
0615: }
0616: }
0617:
0618: /* (non-Javadoc)
0619: * @see org.eclipse.ui.internal.LayoutPart#setVisible(boolean)
0620: */
0621: public void setVisible(boolean makeVisible) {
0622:
0623: if (makeVisible == getVisible()) {
0624: return;
0625: }
0626:
0627: if (!SwtUtil.isDisposed(this .parent)) {
0628: this .parent.setEnabled(makeVisible);
0629: }
0630: super .setVisible(makeVisible);
0631:
0632: ArrayList children = (ArrayList) this .children.clone();
0633: for (int i = 0, length = children.size(); i < length; i++) {
0634: LayoutPart child = (LayoutPart) children.get(i);
0635: child.setVisible(makeVisible
0636: && (zoomedPart == null || child == zoomedPart));
0637: }
0638: }
0639:
0640: /**
0641: * Return the most bottom right part or null if none.
0642: */
0643: public LayoutPart findBottomRight() {
0644: if (root == null) {
0645: return null;
0646: }
0647: return root.findBottomRight();
0648: }
0649:
0650: /**
0651: * @see LayoutPart#getBounds
0652: */
0653: public Rectangle getBounds() {
0654: return this .parent.getBounds();
0655: }
0656:
0657: /**
0658: * @see ILayoutContainer#getChildren
0659: */
0660: public LayoutPart[] getChildren() {
0661: LayoutPart[] result = new LayoutPart[children.size()];
0662: children.toArray(result);
0663: return result;
0664: }
0665:
0666: /**
0667: * @see LayoutPart#getControl
0668: */
0669: public Control getControl() {
0670: return this .parent;
0671: }
0672:
0673: public LayoutTree getLayoutTree() {
0674: return root;
0675: }
0676:
0677: /**
0678: * For themes.
0679: *
0680: * @return the current WorkbenchPage.
0681: */
0682: public WorkbenchPage getPage() {
0683: return page;
0684: }
0685:
0686: /**
0687: * Returns the composite used to parent all the
0688: * layout parts contained within.
0689: */
0690: public Composite getParent() {
0691: return parent;
0692: }
0693:
0694: protected boolean isChild(LayoutPart part) {
0695: return children.indexOf(part) >= 0;
0696: }
0697:
0698: /**
0699: * Returns whether this container is zoomed.
0700: */
0701: public boolean isZoomed() {
0702: return (zoomedPart != null);
0703: }
0704:
0705: /* (non-Javadoc)
0706: * @see org.eclipse.ui.internal.LayoutPart#forceLayout(org.eclipse.ui.internal.LayoutPart)
0707: */
0708: public void resizeChild(LayoutPart childThatChanged) {
0709: if (root != null) {
0710: LayoutTree tree = root.find(childThatChanged);
0711:
0712: if (tree != null) {
0713: tree.flushCache();
0714: }
0715: }
0716:
0717: flushLayout();
0718:
0719: }
0720:
0721: /**
0722: * Remove a part.
0723: */
0724: public void remove(LayoutPart child) {
0725: if (child == getZoomedPart()) {
0726: childRequestZoomOut();
0727: }
0728:
0729: if (!isChild(child)) {
0730: return;
0731: }
0732:
0733: children.remove(child);
0734: if (root != null) {
0735: root = root.remove(child);
0736: }
0737: childRemoved(child);
0738:
0739: if (active) {
0740: child.setVisible(false);
0741: child.setContainer(null);
0742: flushLayout();
0743: }
0744: }
0745:
0746: /* (non-Javadoc)
0747: * @see org.eclipse.ui.internal.LayoutPart#forceLayout()
0748: */
0749: public void flushLayout() {
0750: layoutDirty = true;
0751: super .flushLayout();
0752:
0753: if (layoutDirty) {
0754: resizeSashes();
0755: }
0756: }
0757:
0758: /**
0759: * Replace one part with another.
0760: */
0761: public void replace(LayoutPart oldChild, LayoutPart newChild) {
0762:
0763: if (!isChild(oldChild)) {
0764: return;
0765: }
0766:
0767: if (oldChild == getZoomedPart()) {
0768: if (newChild instanceof PartPlaceholder) {
0769: childRequestZoomOut();
0770: } else {
0771: zoomedPart.setZoomed(false);
0772: zoomedPart = newChild;
0773: zoomedPart.setZoomed(true);
0774: }
0775: }
0776:
0777: children.remove(oldChild);
0778: children.add(newChild);
0779:
0780: childAdded(newChild);
0781:
0782: if (root != null) {
0783: LayoutTree leaf = null;
0784:
0785: leaf = root.find(oldChild);
0786: if (leaf != null) {
0787: leaf.setPart(newChild);
0788: }
0789: }
0790:
0791: childRemoved(oldChild);
0792: if (active) {
0793: oldChild.setVisible(false);
0794: oldChild.setContainer(null);
0795: newChild.createControl(parent);
0796: newChild.setContainer(this );
0797: newChild.setVisible(zoomedPart == null
0798: || zoomedPart == newChild);
0799: resizeChild(newChild);
0800: }
0801: }
0802:
0803: private void resizeSashes() {
0804: layoutDirty = false;
0805: if (!active) {
0806: return;
0807: }
0808:
0809: if (isZoomed()) {
0810: getZoomedPart().setBounds(parent.getClientArea());
0811: } else {
0812: if (root != null) {
0813: root.setBounds(parent.getClientArea());
0814: }
0815: }
0816: }
0817:
0818: /**
0819: * Returns the maximum size that can be utilized by this part if the given width and
0820: * height are available. Parts can overload this if they have a quantized set of preferred
0821: * sizes.
0822: *
0823: * @param width available horizontal space (pixels)
0824: * @return returns a new point where point.x is <= availableWidth and point.y is <= availableHeight
0825: */
0826: public int computePreferredSize(boolean width,
0827: int availableParallel, int availablePerpendicular,
0828: int preferredParallel) {
0829: if (isZoomed()) {
0830: return getZoomedPart().computePreferredSize(width,
0831: availableParallel, availablePerpendicular,
0832: preferredParallel);
0833: }
0834:
0835: if (root != null) {
0836: return root.computePreferredSize(width, availableParallel,
0837: availablePerpendicular, preferredParallel);
0838: }
0839:
0840: return preferredParallel;
0841: }
0842:
0843: public int getSizeFlags(boolean width) {
0844: if (isZoomed()) {
0845: return getZoomedPart().getSizeFlags(width);
0846: }
0847:
0848: if (root != null) {
0849: return root.getSizeFlags(width);
0850: }
0851:
0852: return 0;
0853: }
0854:
0855: /**
0856: * @see LayoutPart#setBounds
0857: */
0858: public void setBounds(Rectangle r) {
0859: this .parent.setBounds(r);
0860: }
0861:
0862: /**
0863: * Zoom in on a particular layout part.
0864: *
0865: * The implementation of zoom is quite simple. When zoom occurs we create
0866: * a zoom root which only contains the zoom part. We store the old
0867: * root in unzoomRoot and then active the zoom root. When unzoom occurs
0868: * we restore the unzoomRoot and dispose the zoom root.
0869: *
0870: * Note: Method assumes we are active.
0871: */
0872: private void zoomIn(LayoutPart part) {
0873: // Sanity check.
0874: if (isZoomed()) {
0875: return;
0876: }
0877:
0878: // Hide the sashes
0879: root.disposeSashes();
0880:
0881: // Make all parts invisible except for the zoomed part
0882: LayoutPart[] children = getChildren();
0883: for (int i = 0; i < children.length; i++) {
0884: LayoutPart child = children[i];
0885: child.setVisible(child == part);
0886: }
0887:
0888: zoomedPart = part;
0889:
0890: // Notify the part that it has been zoomed
0891: part.setZoomed(true);
0892:
0893: // Remember that we need to trigger a layout
0894: layoutDirty = true;
0895: }
0896:
0897: /**
0898: * Returns the currently zoomed part or null if none
0899: *
0900: * @return the currently zoomed part or null if none
0901: * @since 3.1
0902: */
0903: public LayoutPart getZoomedPart() {
0904: return zoomedPart;
0905: }
0906:
0907: public void childRequestZoomIn(LayoutPart toZoom) {
0908: if (!SwtUtil.isDisposed(this .parent)) {
0909: this .parent.setRedraw(false);
0910: }
0911: try {
0912: zoomIn(toZoom);
0913:
0914: requestZoomIn();
0915:
0916: if (layoutDirty) {
0917: resizeSashes();
0918: }
0919: } finally {
0920: if (!SwtUtil.isDisposed(this .parent)) {
0921: this .parent.setRedraw(true);
0922: }
0923: }
0924: }
0925:
0926: public void childRequestZoomOut() {
0927: if (!SwtUtil.isDisposed(this .parent)) {
0928: this .parent.setRedraw(false);
0929: }
0930: try {
0931: zoomOut();
0932:
0933: requestZoomOut();
0934:
0935: if (layoutDirty) {
0936: resizeSashes();
0937: }
0938: } finally {
0939: if (!SwtUtil.isDisposed(this .parent)) {
0940: this .parent.setRedraw(true);
0941: }
0942: }
0943: }
0944:
0945: /* (non-Javadoc)
0946: * @see org.eclipse.ui.internal.LayoutPart#setZoomed(boolean)
0947: */
0948: public void setZoomed(boolean isZoomed) {
0949: if (!isZoomed) {
0950: zoomOut();
0951: } else {
0952: if (!isZoomed()) {
0953: LayoutPart toZoom = pickPartToZoom();
0954:
0955: if (toZoom != null) {
0956: zoomIn(toZoom);
0957: }
0958: }
0959: }
0960: super .setZoomed(isZoomed);
0961: }
0962:
0963: public LayoutPart pickPartToZoom() {
0964: return findBottomRight();
0965: }
0966:
0967: /**
0968: * Zoom out.
0969: *
0970: * See zoomIn for implementation details.
0971: *
0972: * Note: Method assumes we are active.
0973: */
0974: private void zoomOut() {
0975: // Sanity check.
0976: if (!isZoomed()) {
0977: return;
0978: }
0979:
0980: LayoutPart zoomedPart = this .zoomedPart;
0981: this .zoomedPart = null;
0982: // Inform the part that it is no longer zoomed
0983: zoomedPart.setZoomed(false);
0984:
0985: // Make all children visible
0986: LayoutPart[] children = getChildren();
0987: for (int i = 0; i < children.length; i++) {
0988: LayoutPart child = children[i];
0989:
0990: child.setVisible(true);
0991: }
0992:
0993: // Recreate the sashes
0994: root.createControl(getParent());
0995:
0996: // Ensure that the part being un-zoomed will have its size refreshed.
0997: LayoutTree node = root.find(zoomedPart);
0998: node.flushCache();
0999:
1000: layoutDirty = true;
1001: }
1002:
1003: /* (non-Javadoc)
1004: * @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)
1005: */
1006: public IDropTarget drag(Control currentControl,
1007: Object draggedObject, Point position,
1008: Rectangle dragRectangle) {
1009: if (!(draggedObject instanceof LayoutPart)) {
1010: return null;
1011: }
1012:
1013: final LayoutPart sourcePart = (LayoutPart) draggedObject;
1014:
1015: if (!isStackType(sourcePart) && !isPaneType(sourcePart)) {
1016: return null;
1017: }
1018:
1019: boolean differentWindows = sourcePart.getWorkbenchWindow() != getWorkbenchWindow();
1020: boolean editorDropOK = ((sourcePart instanceof EditorPane) && sourcePart
1021: .getWorkbenchWindow().getWorkbench() == getWorkbenchWindow()
1022: .getWorkbench());
1023: if (differentWindows && !editorDropOK) {
1024: return null;
1025: }
1026:
1027: Rectangle containerBounds = DragUtil.getDisplayBounds(parent);
1028: LayoutPart targetPart = null;
1029: ILayoutContainer sourceContainer = isStackType(sourcePart) ? (ILayoutContainer) sourcePart
1030: : sourcePart.getContainer();
1031:
1032: // If this container has no visible children
1033: if (getVisibleChildrenCount(this ) == 0) {
1034: return createDropTarget(sourcePart, SWT.CENTER, SWT.CENTER,
1035: null);
1036: }
1037:
1038: if (containerBounds.contains(position)) {
1039:
1040: if (root != null) {
1041: targetPart = root.findPart(parent.toControl(position));
1042: }
1043:
1044: if (targetPart != null) {
1045: final Control targetControl = targetPart.getControl();
1046:
1047: final Rectangle targetBounds = DragUtil
1048: .getDisplayBounds(targetControl);
1049:
1050: int side = Geometry.getClosestSide(targetBounds,
1051: position);
1052: int distance = Geometry.getDistanceFromEdge(
1053: targetBounds, position, side);
1054:
1055: // is the source coming from a standalone part
1056: boolean standalone = (isStackType(sourcePart) && ((PartStack) sourcePart)
1057: .isStandalone())
1058: || (isPaneType(sourcePart)
1059: && ((PartPane) sourcePart).getStack() != null && ((PartPane) sourcePart)
1060: .getStack().isStandalone());
1061:
1062: // Only allow dropping onto an existing stack from different windows
1063: if (differentWindows
1064: && targetPart instanceof EditorStack) {
1065: IDropTarget target = targetPart.getDropTarget(
1066: draggedObject, position);
1067: return target;
1068: }
1069:
1070: // Reserve the 5 pixels around the edge of the part for the drop-on-edge cursor
1071: if (distance >= 5 && !standalone) {
1072: // Otherwise, ask the part if it has any special meaning for this drop location
1073: IDropTarget target = targetPart.getDropTarget(
1074: draggedObject, position);
1075: if (target != null) {
1076: return target;
1077: }
1078: }
1079:
1080: if (distance > 30 && isStackType(targetPart)
1081: && !standalone) {
1082: if (targetPart instanceof ILayoutContainer) {
1083: ILayoutContainer targetContainer = (ILayoutContainer) targetPart;
1084: if (targetContainer.allowsAdd(sourcePart)) {
1085: side = SWT.CENTER;
1086: }
1087: }
1088: }
1089:
1090: // If the part doesn't want to override this drop location then drop on the edge
1091:
1092: // A "pointless drop" would be one that will put the dragged object back where it started.
1093: // Note that it should be perfectly valid to drag an object back to where it came from -- however,
1094: // the drop should be ignored.
1095:
1096: boolean pointlessDrop = isZoomed();
1097:
1098: if (sourcePart == targetPart) {
1099: pointlessDrop = true;
1100: }
1101:
1102: if ((sourceContainer != null)
1103: && (sourceContainer == targetPart)
1104: && getVisibleChildrenCount(sourceContainer) <= 1) {
1105: pointlessDrop = true;
1106: }
1107:
1108: if (side == SWT.CENTER
1109: && sourcePart.getContainer() == targetPart) {
1110: pointlessDrop = true;
1111: }
1112:
1113: int cursor = side;
1114:
1115: if (pointlessDrop) {
1116: side = SWT.NONE;
1117: cursor = SWT.CENTER;
1118: }
1119:
1120: return createDropTarget(sourcePart, side, cursor,
1121: targetPart);
1122: }
1123: } else {
1124: // We only allow dropping into a stack, not creating one
1125: if (differentWindows)
1126: return null;
1127:
1128: int side = Geometry.getClosestSide(containerBounds,
1129: position);
1130:
1131: boolean pointlessDrop = isZoomed();
1132:
1133: if ((isStackType(sourcePart) && sourcePart.getContainer() == this )
1134: || (sourcePart.getContainer() != null
1135: && isPaneType(sourcePart) && getVisibleChildrenCount(sourcePart
1136: .getContainer()) <= 1)
1137: && ((LayoutPart) sourcePart.getContainer())
1138: .getContainer() == this ) {
1139: if (root == null || getVisibleChildrenCount(this ) <= 1) {
1140: pointlessDrop = true;
1141: }
1142: }
1143:
1144: int cursor = Geometry.getOppositeSide(side);
1145:
1146: if (pointlessDrop) {
1147: side = SWT.NONE;
1148: }
1149:
1150: return createDropTarget(sourcePart, side, cursor, null);
1151: }
1152:
1153: return null;
1154: }
1155:
1156: /**
1157: * @param sourcePart
1158: * @param targetPart
1159: * @param side
1160: * @param cursor
1161: * @return
1162: * @since 3.1
1163: */
1164: private SashContainerDropTarget createDropTarget(
1165: final LayoutPart sourcePart, int side, int cursor,
1166: LayoutPart targetPart) {
1167: if (dropTarget == null) {
1168: dropTarget = new SashContainerDropTarget(sourcePart, side,
1169: cursor, targetPart);
1170: } else {
1171: dropTarget.setTarget(sourcePart, side, cursor, targetPart);
1172: }
1173: return dropTarget;
1174: }
1175:
1176: /**
1177: * Returns true iff this PartSashContainer allows its parts to be stacked onto the given
1178: * container.
1179: *
1180: * @param container
1181: * @return
1182: */
1183: public abstract boolean isStackType(LayoutPart toTest);
1184:
1185: public abstract boolean isPaneType(LayoutPart toTest);
1186:
1187: /* (non-Javadoc)
1188: * @see org.eclipse.ui.internal.PartSashContainer#dropObject(org.eclipse.ui.internal.LayoutPart, org.eclipse.ui.internal.LayoutPart, int)
1189: */
1190: protected void dropObject(PartPane[] toDrop,
1191: LayoutPart visiblePart, LayoutPart targetPart, int side) {
1192: getControl().setRedraw(false);
1193:
1194: // Targetpart is null if there isn't a part under the cursor (all the parts are
1195: // hidden or the container is empty). In this case, the actual side doesn't really
1196: // since we'll be the only visible container and will fill the entire space. However,
1197: // we can't leave it as SWT.CENTER since we can't stack if we don't have something
1198: // to stack on. In this case, we pick SWT.BOTTOM -- this will insert the new pane
1199: // below any currently-hidden parts.
1200: if (targetPart == null && side == SWT.CENTER) {
1201: side = SWT.BOTTOM;
1202: }
1203:
1204: if (side == SWT.CENTER) {
1205: if (isStackType(targetPart)) {
1206: PartStack stack = (PartStack) targetPart;
1207: for (int idx = 0; idx < toDrop.length; idx++) {
1208: PartPane next = toDrop[idx];
1209: stack(next, stack);
1210: }
1211: }
1212: } else {
1213: PartStack newPart = createStack();
1214:
1215: // if the toDrop array has 1 item propogate the stack
1216: // appearance
1217: if (toDrop.length == 1 && toDrop[0].getStack() != null) {
1218: toDrop[0].getStack().copyAppearanceProperties(newPart);
1219: }
1220:
1221: for (int idx = 0; idx < toDrop.length; idx++) {
1222: PartPane next = toDrop[idx];
1223: stack(next, newPart);
1224: }
1225:
1226: addEnhanced(newPart, side, getDockingRatio(newPart,
1227: targetPart), targetPart);
1228: }
1229:
1230: if (visiblePart != null) {
1231: setVisiblePart(visiblePart.getContainer(), visiblePart);
1232: }
1233:
1234: getControl().setRedraw(true);
1235:
1236: if (visiblePart != null) {
1237: visiblePart.setFocus();
1238: }
1239: }
1240:
1241: protected abstract PartStack createStack();
1242:
1243: public void stack(LayoutPart newPart, ILayoutContainer container) {
1244: getControl().setRedraw(false);
1245:
1246: // Only deref the part if it is being referenced in -this- perspective
1247: Perspective persp = page.getActivePerspective();
1248: PerspectiveHelper pres = (persp != null) ? persp
1249: .getPresentation() : null;
1250: if (pres != null && newPart instanceof ViewPane) {
1251: ViewPane vp = (ViewPane) newPart;
1252: IViewReference vRef = vp.getViewReference();
1253: LayoutPart fpp = pres.findPart(vRef.getId(), vRef
1254: .getSecondaryId());
1255:
1256: if (fpp != null) {
1257: // Remove the part from old container.
1258: derefPart(newPart);
1259: }
1260: } else {
1261: // Remove the part from old container.
1262: derefPart(newPart);
1263: }
1264:
1265: // Reparent part and add it to the workbook
1266: newPart.reparent(getParent());
1267: container.add(newPart);
1268: getControl().setRedraw(true);
1269:
1270: }
1271:
1272: /**
1273: * @param container
1274: * @param visiblePart
1275: */
1276: protected abstract void setVisiblePart(ILayoutContainer container,
1277: LayoutPart visiblePart);
1278:
1279: /**
1280: * @param container
1281: * @return
1282: */
1283: protected abstract LayoutPart getVisiblePart(
1284: ILayoutContainer container);
1285:
1286: /**
1287: * @param sourcePart
1288: */
1289: protected void derefPart(LayoutPart sourcePart) {
1290: ILayoutContainer container = sourcePart.getContainer();
1291: if (container != null) {
1292: container.remove(sourcePart);
1293: }
1294:
1295: if (container instanceof LayoutPart) {
1296: if (isStackType((LayoutPart) container)) {
1297: PartStack stack = (PartStack) container;
1298: if (stack.getChildren().length == 0) {
1299: remove(stack);
1300: stack.dispose();
1301: }
1302: }
1303: }
1304: }
1305:
1306: protected int getVisibleChildrenCount(ILayoutContainer container) {
1307: // Treat null as an empty container
1308: if (container == null) {
1309: return 0;
1310: }
1311:
1312: LayoutPart[] children = container.getChildren();
1313:
1314: int count = 0;
1315: for (int idx = 0; idx < children.length; idx++) {
1316: if (!(children[idx] instanceof PartPlaceholder)) {
1317: count++;
1318: }
1319: }
1320:
1321: return count;
1322: }
1323:
1324: protected float getDockingRatio(LayoutPart dragged,
1325: LayoutPart target) {
1326: return 0.5f;
1327: }
1328:
1329: /**
1330: * Writes a description of the layout to the given string buffer.
1331: * This is used for drag-drop test suites to determine if two layouts are the
1332: * same. Like a hash code, the description should compare as equal iff the
1333: * layouts are the same. However, it should be user-readable in order to
1334: * help debug failed tests. Although these are english readable strings,
1335: * they should not be translated or equality tests will fail.
1336: *
1337: * @param buf
1338: */
1339: public void describeLayout(StringBuffer buf) {
1340: if (root == null) {
1341: return;
1342: }
1343:
1344: if (isZoomed()) {
1345: buf.append("zoomed "); //$NON-NLS-1$
1346: root.describeLayout(buf);
1347: } else {
1348: buf.append("layout "); //$NON-NLS-1$
1349: root.describeLayout(buf);
1350: }
1351: }
1352:
1353: /**
1354: * Adds a new child to the container relative to some part
1355: *
1356: * @param child
1357: * @param relationship
1358: * @param left preferred pixel size of the left/top child
1359: * @param right preferred pixel size of the right/bottom child
1360: * @param relative relative part
1361: */
1362: void add(LayoutPart child, int relationship, int left, int right,
1363: LayoutPart relative) {
1364:
1365: if (child == null) {
1366: return;
1367: }
1368: if (relative != null && !isChild(relative)) {
1369: return;
1370: }
1371: if (relationship < IPageLayout.LEFT
1372: || relationship > IPageLayout.BOTTOM) {
1373: relationship = IPageLayout.LEFT;
1374: }
1375:
1376: // store info about relative positions
1377: RelationshipInfo info = new RelationshipInfo();
1378: info.part = child;
1379: info.relationship = relationship;
1380: info.left = left;
1381: info.right = right;
1382: info.relative = relative;
1383: addChild(info);
1384: }
1385:
1386: /* (non-Javadoc)
1387: * @see org.eclipse.ui.internal.ILayoutContainer#isZoomed(org.eclipse.ui.internal.LayoutPart)
1388: */
1389: public boolean childIsZoomed(LayoutPart toTest) {
1390: return toTest == getZoomedPart();
1391: }
1392:
1393: /* (non-Javadoc)
1394: * @see org.eclipse.ui.internal.ILayoutContainer#allowsAutoFocus()
1395: */
1396: public boolean allowsAutoFocus() {
1397: return true;
1398: }
1399:
1400: /* (non-Javadoc)
1401: * @see org.eclipse.ui.internal.LayoutPart#startDeferringEvents()
1402: */
1403: protected void startDeferringEvents() {
1404: super .startDeferringEvents();
1405:
1406: LayoutPart[] deferredChildren = (LayoutPart[]) children
1407: .toArray(new LayoutPart[children.size()]);
1408: for (int i = 0; i < deferredChildren.length; i++) {
1409: LayoutPart child = deferredChildren[i];
1410:
1411: child.deferUpdates(true);
1412: }
1413: }
1414:
1415: /* (non-Javadoc)
1416: * @see org.eclipse.ui.internal.LayoutPart#handleDeferredEvents()
1417: */
1418: protected void handleDeferredEvents() {
1419: super .handleDeferredEvents();
1420:
1421: LayoutPart[] deferredChildren = (LayoutPart[]) children
1422: .toArray(new LayoutPart[children.size()]);
1423: for (int i = 0; i < deferredChildren.length; i++) {
1424: LayoutPart child = deferredChildren[i];
1425:
1426: child.deferUpdates(false);
1427: }
1428: }
1429:
1430: /* (non-Javadoc)
1431: * @see org.eclipse.ui.internal.LayoutPart#testInvariants()
1432: */
1433: public void testInvariants() {
1434: super .testInvariants();
1435:
1436: // If we have a parent container, ensure that we are displaying the zoomed appearance iff
1437: // our parent is zoomed in on us
1438: if (getContainer() != null) {
1439: Assert.isTrue((getZoomedPart() != null) == (getContainer()
1440: .childIsZoomed(this )));
1441: }
1442:
1443: LayoutPart[] childArray = getChildren();
1444:
1445: for (int idx = 0; idx < childArray.length; idx++) {
1446: childArray[idx].testInvariants();
1447: }
1448:
1449: // If we're zoomed, ensure that we're actually zoomed into one of our children
1450: if (isZoomed()) {
1451: Assert.isTrue(children.contains(zoomedPart));
1452: }
1453: }
1454: }
|