org.eclipse.ui.internal.LayoutTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of workbench Show documentation
Show all versions of workbench Show documentation
This plug-in contains the bulk of the Workbench implementation, and depends on JFace, SWT, and Core Runtime. It cannot be used independently from org.eclipse.ui. Workbench client plug-ins should not depend directly on this plug-in.
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2006 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
* Randy Hudson
* - Fix for bug 19524 - Resizing WorkbenchWindow resizes Views
* Cagatay Kavukcuoglu
* - Fix for bug 10025 - Resizing views should not use height ratios
*******************************************************************************/
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.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISizeProvider;
/**
* Implementation of a tree where the node is allways a sash
* and it allways has two chidren. If a children is removed
* the sash, ie the node, is removed as well and its other children
* placed on its parent.
*/
public class LayoutTree implements ISizeProvider {
/* The parent of this tree or null if it is the root */
LayoutTreeNode parent;
/* Any LayoutPart if this is a leaf or a LayoutSashPart if it is a node */
LayoutPart part;
// Cached information
private int cachedMinimumWidthHint = SWT.DEFAULT;
private int cachedMinimumWidth = SWT.DEFAULT;
private int cachedMinimumHeightHint = SWT.DEFAULT;
private int cachedMinimumHeight = SWT.DEFAULT;
private int cachedMaximumWidthHint = SWT.DEFAULT;
private int cachedMaximumWidth = SWT.DEFAULT;
private int cachedMaximumHeightHint = SWT.DEFAULT;
private int cachedMaximumHeight = SWT.DEFAULT;
// Cached size flags
private boolean sizeFlagsDirty = true;
private int widthSizeFlags = 0;
private int heightSizeFlags = 0;
// Cache statistics. For use in benchmarks and test suites only!
public static int minCacheHits;
public static int minCacheMisses;
public static int maxCacheHits;
public static int maxCacheMisses;
private boolean forceLayout = true;
private Rectangle currentBounds = new Rectangle(0,0,0,0);
/**
* Initialize this tree with its part.
*/
public LayoutTree(LayoutPart part) {
this.part = part;
}
/**
* Add the relation ship between the children in the list
* and returns the left children.
*/
public LayoutPart computeRelation(ArrayList relations) {
return part;
}
/**
* Locates the part that intersects the given point
*
* @param toFind
* @return
*/
public LayoutPart findPart(Point toFind) {
return part;
}
/**
* Dispose all Sashs in this tree
*/
public void disposeSashes() {
}
/**
* Find a LayoutPart in the tree and return its sub-tree. Returns
* null if the child is not found.
*/
public LayoutTree find(LayoutPart child) {
if (part != child) {
return null;
}
return this;
}
/**
* Find the Left,Right,Top and Botton
* sashes around this tree and set them
* in sashes
*/
public void findSashes(PartPane.Sashes sashes) {
if (getParent() == null) {
return;
}
getParent().findSashes(this, sashes);
}
/**
* Find the part that is in the bottom rigth possition.
*/
public LayoutPart findBottomRight() {
return part;
}
/**
* Find a sash in the tree and return its sub-tree. Returns
* null if the sash is not found.
*/
public LayoutTreeNode findSash(LayoutPartSash sash) {
return null;
}
/**
* Return the bounds of this tree which is the rectangle that
* contains all Controls in this tree.
*/
public final Rectangle getBounds() {
return Geometry.copy(currentBounds);
}
/**
* Subtracts two integers. If a is INFINITE, this is treated as
* positive infinity.
*
* @param a a positive integer or INFINITE indicating positive infinity
* @param b a positive integer (may not be INFINITE)
* @return a - b, or INFINITE if a == INFINITE
* @since 3.1
*/
public static int subtract(int a, int b) {
Assert.isTrue(b >= 0 && b < INFINITE);
return add(a, -b);
}
/**
* Adds two positive integers. Treates INFINITE as positive infinity.
*
* @param a a positive integer
* @param b a positive integer
* @return a + b, or INFINITE if a or b are positive infinity
* @since 3.1
*/
public static int add(int a, int b) {
if (a == INFINITE || b == INFINITE) {
return INFINITE;
}
return a + b;
}
/**
* Asserts that toCheck is a positive integer less than INFINITE / 2 or equal
* to INFINITE. Many of the methods of this class use positive integers as sizes,
* with INFINITE indicating positive infinity. This picks up accidental addition or
* subtraction from infinity.
*
* @param toCheck integer to validate
* @since 3.1
*/
public static void assertValidSize(int toCheck) {
Assert.isTrue(toCheck >= 0 && (toCheck == INFINITE || toCheck < INFINITE / 2));
}
/**
* Computes the preferred size for this object. The interpretation of the result depends on the flags returned
* by getSizeFlags(). If the caller is looking for a maximum or minimum size, this delegates to computeMinimumSize
* or computeMaximumSize in order to benefit from caching optimizations. Otherwise, it delegates to
* doComputePreferredSize. Subclasses should overload one of doComputeMinimumSize, doComputeMaximumSize, or
* doComputePreferredSize to specialize the return value.
*
* @see LayoutPart#computePreferredSize(boolean, int, int, int)
*/
public final int computePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredParallel) {
assertValidSize(availableParallel);
assertValidSize(availablePerpendicular);
assertValidSize(preferredParallel);
if (!isVisible()) {
return 0;
}
if (availableParallel == 0) {
return 0;
}
if (preferredParallel == 0) {
return Math.min(availableParallel, computeMinimumSize(width, availablePerpendicular));
} else if (preferredParallel == INFINITE && availableParallel == INFINITE) {
return computeMaximumSize(width, availablePerpendicular);
}
// Optimization: if this subtree doesn't have any size preferences beyond its minimum and maximum
// size, simply return the preferred size
if (!hasSizeFlag(width, SWT.FILL)) {
return preferredParallel;
}
int result = doComputePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel);
return result;
}
/**
* Returns the size flags for this tree.
*
* @see org.eclipse.ui.presentations.StackPresentation#getSizeFlags(boolean)
*
* @param b indicates whether the caller wants the flags for computing widths (=true) or heights (=false)
* @return a bitwise combiniation of flags with the same meaning as StackPresentation.getSizeFlags(boolean)
*/
protected int doGetSizeFlags(boolean width) {
return part.getSizeFlags(width);
}
/**
* Subclasses should overload this method instead of computePreferredSize(boolean, int, int, int)
*
* @see org.eclipse.ui.presentations.StackPresentation#computePreferredSize(boolean, int, int, int)
*
* @since 3.1
*/
protected int doComputePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredParallel) {
int result = Math.min(availableParallel,
part.computePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel));
assertValidSize(result);
return result;
}
/**
* Returns the minimum size for this subtree. Equivalent to calling
* computePreferredSize(width, INFINITE, availablePerpendicular, 0).
* Returns a cached value if possible or defers to doComputeMinimumSize otherwise.
* Subclasses should overload doComputeMinimumSize if they want to specialize the
* return value.
*
* @param width true iff computing the minimum width, false iff computing the minimum height
* @param availablePerpendicular available space (pixels) perpendicular to the dimension
* being computed. This is a height when computing a width, or a width when computing a height.
*
* @see LayoutPart#computePreferredSize(boolean, int, int, int)
*/
public final int computeMinimumSize(boolean width, int availablePerpendicular) {
assertValidSize(availablePerpendicular);
// Optimization: if this subtree has no minimum size, then always return 0 as its
// minimum size.
if (!hasSizeFlag(width, SWT.MIN)) {
return 0;
}
// If this subtree doesn't contain any wrapping controls (ie: they don't care
// about their perpendicular size) then force the perpendicular
// size to be INFINITE. This ensures that we will get a cache hit
// every time for non-wrapping controls.
if (!hasSizeFlag(width, SWT.WRAP)) {
availablePerpendicular = INFINITE;
}
if (width) {
// Check if we have a cached width measurement (we can only return a cached
// value if we computed it for the same height)
if (cachedMinimumWidthHint == availablePerpendicular) {
minCacheHits++;
return cachedMinimumWidth;
}
// Recompute the minimum width and store it in the cache
minCacheMisses++;
int result = doComputeMinimumSize(width, availablePerpendicular);
cachedMinimumWidth = result;
cachedMinimumWidthHint = availablePerpendicular;
return result;
} else {
// Check if we have a cached height measurement (we can only return a cached
// value if we computed it for the same width)
if (cachedMinimumHeightHint == availablePerpendicular) {
minCacheHits++;
return cachedMinimumHeight;
}
// Recompute the minimum width and store it in the cache
minCacheMisses++;
int result = doComputeMinimumSize(width, availablePerpendicular);
cachedMinimumHeight = result;
cachedMinimumHeightHint = availablePerpendicular;
return result;
}
}
/**
* For use in benchmarks and test suites only. Displays cache utilization statistics for all
* LayoutTree instances.
*
* @since 3.1
*/
public static void printCacheStatistics() {
System.out.println("minimize cache " + minCacheHits + " / " + (minCacheHits + minCacheMisses) + " hits " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
minCacheHits * 100 / (minCacheHits + minCacheMisses) + "%"); //$NON-NLS-1$
System.out.println("maximize cache " + maxCacheHits + " / " + (maxCacheHits + maxCacheMisses) + " hits" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
maxCacheHits * 100 / (maxCacheHits + maxCacheMisses) + "%"); //$NON-NLS-1$
}
public int doComputeMinimumSize(boolean width, int availablePerpendicular) {
int result = doComputePreferredSize(width, INFINITE, availablePerpendicular, 0);
assertValidSize(result);
return result;
}
public final int computeMaximumSize(boolean width, int availablePerpendicular) {
assertValidSize(availablePerpendicular);
// Optimization: if this subtree has no maximum size, then always return INFINITE as its
// maximum size.
if (!hasSizeFlag(width, SWT.MAX)) {
return INFINITE;
}
// If this subtree doesn't contain any wrapping controls (ie: they don't care
// about their perpendicular size) then force the perpendicular
// size to be INFINITE. This ensures that we will get a cache hit
// every time.
if (!hasSizeFlag(width, SWT.WRAP)) {
availablePerpendicular = INFINITE;
}
if (width) {
// Check if we have a cached width measurement (we can only return a cached
// value if we computed it for the same height)
if (cachedMaximumWidthHint == availablePerpendicular) {
maxCacheHits++;
return cachedMaximumWidth;
}
maxCacheMisses++;
// Recompute the maximum width and store it in the cache
int result = doComputeMaximumSize(width, availablePerpendicular);
cachedMaximumWidth = result;
cachedMaximumWidthHint = availablePerpendicular;
return result;
} else {
// Check if we have a cached height measurement
if (cachedMaximumHeightHint == availablePerpendicular) {
maxCacheHits++;
return cachedMaximumHeight;
}
maxCacheMisses++;
// Recompute the maximum height and store it in the cache
int result = doComputeMaximumSize(width, availablePerpendicular);
cachedMaximumHeight = result;
cachedMaximumHeightHint = availablePerpendicular;
return result;
}
}
protected int doComputeMaximumSize(boolean width, int availablePerpendicular) {
return doComputePreferredSize(width, INFINITE, availablePerpendicular, INFINITE);
}
/**
* Called to flush any cached information in this tree and its parents.
*/
public void flushNode() {
// Clear cached sizes
cachedMinimumWidthHint = SWT.DEFAULT;
cachedMinimumWidth = SWT.DEFAULT;
cachedMinimumHeightHint = SWT.DEFAULT;
cachedMinimumHeight = SWT.DEFAULT;
cachedMaximumWidthHint = SWT.DEFAULT;
cachedMaximumWidth = SWT.DEFAULT;
cachedMaximumHeightHint = SWT.DEFAULT;
cachedMaximumHeight = SWT.DEFAULT;
// Flags may have changed. Ensure that they are recomputed the next time around
sizeFlagsDirty = true;
// The next setBounds call should trigger a layout even if set to the same bounds since
// one of the children has changed.
forceLayout = true;
}
/**
* Flushes all cached information about this node and all of its children.
* This should be called if something may have caused all children to become
* out of synch with their cached information (for example, if a lot of changes
* may have happened without calling flushCache after each change)
*
* @since 3.1
*/
public void flushChildren() {
flushNode();
}
/**
* Flushes all cached information about this node and all of its ancestors.
* This should be called when a single child changes.
*
* @since 3.1
*/
public final void flushCache() {
flushNode();
if (parent != null) {
parent.flushCache();
}
}
public final int getSizeFlags(boolean width) {
if (sizeFlagsDirty) {
widthSizeFlags = doGetSizeFlags(true);
heightSizeFlags = doGetSizeFlags(false);
sizeFlagsDirty = false;
}
return width ? widthSizeFlags : heightSizeFlags;
}
/**
* Returns the parent of this tree or null if it is the root.
*/
public LayoutTreeNode getParent() {
return parent;
}
/**
* Inserts a new child on the tree. The child will be placed beside
* the relative
child. Returns the new root of the tree.
*/
public LayoutTree insert(LayoutPart child, boolean left,
LayoutPartSash sash, LayoutPart relative) {
LayoutTree relativeChild = find(relative);
LayoutTreeNode node = new LayoutTreeNode(sash);
if (relativeChild == null) {
//Did not find the relative part. Insert beside the root.
node.setChild(left, child);
node.setChild(!left, this);
return node;
} else {
LayoutTreeNode oldParent = relativeChild.getParent();
node.setChild(left, child);
node.setChild(!left, relativeChild);
if (oldParent == null) {
//It was the root. Return a new root.
return node;
}
oldParent.replaceChild(relativeChild, node);
return this;
}
}
/**
* Returns true if this tree can be compressed and expanded.
* @return true if springy
*/
public boolean isCompressible() {
//Added for bug 19524
return part.isCompressible();
}
/**
* Returns true if this tree has visible parts otherwise returns false.
*/
public boolean isVisible() {
return !(part instanceof PartPlaceholder);
}
/**
* Recompute the ratios in this tree.
*/
public void recomputeRatio() {
}
/**
* Find a child in the tree and remove it and its parent.
* The other child of its parent is placed on the parent's parent.
* Returns the new root of the tree.
*/
public LayoutTree remove(LayoutPart child) {
LayoutTree tree = find(child);
if (tree == null) {
return this;
}
LayoutTreeNode oldParent = tree.getParent();
if (oldParent == null) {
//It was the root and the only child of this tree
return null;
}
if (oldParent.getParent() == null) {
return oldParent.remove(tree);
}
oldParent.remove(tree);
return this;
}
/**
* Sets the bounds of this node. If the bounds have changed or any children have
* changed then the children will be recursively layed out. This implementation
* filters out redundant calls and delegates to doSetBounds to layout the children.
* Subclasses should overload doSetBounds to lay out their children.
*
* @param bounds new bounds of the tree
*/
public final void setBounds(Rectangle bounds) {
if (!bounds.equals(currentBounds) || forceLayout) {
currentBounds = Geometry.copy(bounds);
doSetBounds(currentBounds);
forceLayout = false;
}
}
/**
* Resize the parts on this tree to fit in bounds
.
*/
protected void doSetBounds(Rectangle bounds) {
part.setBounds(bounds);
}
/**
* Set the parent of this tree.
*/
void setParent(LayoutTreeNode parent) {
this.parent = parent;
}
/**
* Set the part of this leaf
*/
void setPart(LayoutPart part) {
this.part = part;
flushCache();
}
/**
* Returns a string representation of this object.
*/
public String toString() {
return "(" + part.toString() + ")";//$NON-NLS-2$//$NON-NLS-1$
}
/**
* Creates SWT controls owned by the LayoutTree (ie: the sashes). Does not affect the
* LayoutParts that are being arranged by the LayoutTree.
*
* @param parent
* @since 3.1
*/
public void createControl(Composite parent) {
}
/**
* 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.
*
* This is only intended for use by test suites.
*
*
* @param buf
*/
public void describeLayout(StringBuffer buf) {
part.describeLayout(buf);
}
/**
* This is a shorthand method that checks if the tree contains the
* given size flag. For example, hasSizeFlag(false, SWT.MIN) returns
* true iff the receiver enforces a minimum height, or
* hasSizeFlag(true, SWT.WRAP) returns true iff the receiver needs to
* know its height when computing its preferred width.
*
* @param vertical
* @return
* @since 3.1
*/
public final boolean hasSizeFlag(boolean width, int flag) {
return (getSizeFlags(width) & flag) != 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy