KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > LayoutTree


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Randy Hudson <hudsonr@us.ibm.com>
11  * - Fix for bug 19524 - Resizing WorkbenchWindow resizes Views
12  * Cagatay Kavukcuoglu <cagatayk@acm.org>
13  * - Fix for bug 10025 - Resizing views should not use height ratios
14  *******************************************************************************/

15 package org.eclipse.ui.internal;
16
17 import java.util.ArrayList JavaDoc;
18
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.jface.util.Geometry;
21 import org.eclipse.swt.SWT;
22 import org.eclipse.swt.graphics.Point;
23 import org.eclipse.swt.graphics.Rectangle;
24 import org.eclipse.swt.widgets.Composite;
25 import org.eclipse.ui.ISizeProvider;
26
27 /**
28  * Implementation of a tree where the node is allways a sash
29  * and it allways has two chidren. If a children is removed
30  * the sash, ie the node, is removed as well and its other children
31  * placed on its parent.
32  */

33 public class LayoutTree implements ISizeProvider {
34     /* The parent of this tree or null if it is the root */
35     LayoutTreeNode parent;
36
37     /* Any LayoutPart if this is a leaf or a LayoutSashPart if it is a node */
38     LayoutPart part;
39     
40     // Cached information
41
private int cachedMinimumWidthHint = SWT.DEFAULT;
42     private int cachedMinimumWidth = SWT.DEFAULT;
43     private int cachedMinimumHeightHint = SWT.DEFAULT;
44     private int cachedMinimumHeight = SWT.DEFAULT;
45     private int cachedMaximumWidthHint = SWT.DEFAULT;
46     private int cachedMaximumWidth = SWT.DEFAULT;
47     private int cachedMaximumHeightHint = SWT.DEFAULT;
48     private int cachedMaximumHeight = SWT.DEFAULT;
49     
50     // Cached size flags
51
private boolean sizeFlagsDirty = true;
52     private int widthSizeFlags = 0;
53     private int heightSizeFlags = 0;
54     
55     // Cache statistics. For use in benchmarks and test suites only!
56
public static int minCacheHits;
57     public static int minCacheMisses;
58     public static int maxCacheHits;
59     public static int maxCacheMisses;
60     
61     private boolean forceLayout = true;
62     private Rectangle currentBounds = new Rectangle(0,0,0,0);
63     
64     /**
65      * Initialize this tree with its part.
66      */

67     public LayoutTree(LayoutPart part) {
68         this.part = part;
69     }
70
71     /**
72      * Add the relation ship between the children in the list
73      * and returns the left children.
74      */

75     public LayoutPart computeRelation(ArrayList JavaDoc relations) {
76         return part;
77     }
78
79     /**
80      * Locates the part that intersects the given point
81      *
82      * @param toFind
83      * @return
84      */

85     public LayoutPart findPart(Point toFind) {
86         return part;
87     }
88     
89     /**
90      * Dispose all Sashs in this tree
91      */

92     public void disposeSashes() {
93     }
94     
95     /**
96      * Find a LayoutPart in the tree and return its sub-tree. Returns
97      * null if the child is not found.
98      */

99     public LayoutTree find(LayoutPart child) {
100         if (part != child) {
101             return null;
102         }
103         return this;
104     }
105
106     /**
107      * Find the Left,Right,Top and Botton
108      * sashes around this tree and set them
109      * in <code>sashes</code>
110      */

111     public void findSashes(PartPane.Sashes sashes) {
112         if (getParent() == null) {
113             return;
114         }
115         getParent().findSashes(this, sashes);
116     }
117
118     /**
119      * Find the part that is in the bottom rigth possition.
120      */

121     public LayoutPart findBottomRight() {
122         return part;
123     }
124     
125     /**
126      * Find a sash in the tree and return its sub-tree. Returns
127      * null if the sash is not found.
128      */

129     public LayoutTreeNode findSash(LayoutPartSash sash) {
130         return null;
131     }
132
133     /**
134      * Return the bounds of this tree which is the rectangle that
135      * contains all Controls in this tree.
136      */

137     public final Rectangle getBounds() {
138         return Geometry.copy(currentBounds);
139     }
140     
141     /**
142      * Subtracts two integers. If a is INFINITE, this is treated as
143      * positive infinity.
144      *
145      * @param a a positive integer or INFINITE indicating positive infinity
146      * @param b a positive integer (may not be INFINITE)
147      * @return a - b, or INFINITE if a == INFINITE
148      * @since 3.1
149      */

150     public static int subtract(int a, int b) {
151         Assert.isTrue(b >= 0 && b < INFINITE);
152         
153         return add(a, -b);
154     }
155     
156     /**
157      * Adds two positive integers. Treates INFINITE as positive infinity.
158      *
159      * @param a a positive integer
160      * @param b a positive integer
161      * @return a + b, or INFINITE if a or b are positive infinity
162      * @since 3.1
163      */

164     public static int add(int a, int b) {
165         if (a == INFINITE || b == INFINITE) {
166             return INFINITE;
167         }
168         
169         return a + b;
170     }
171     
172     /**
173      * Asserts that toCheck is a positive integer less than INFINITE / 2 or equal
174      * to INFINITE. Many of the methods of this class use positive integers as sizes,
175      * with INFINITE indicating positive infinity. This picks up accidental addition or
176      * subtraction from infinity.
177      *
178      * @param toCheck integer to validate
179      * @since 3.1
180      */

181     public static void assertValidSize(int toCheck) {
182         Assert.isTrue(toCheck >= 0 && (toCheck == INFINITE || toCheck < INFINITE / 2));
183     }
184     
185     /**
186      * Computes the preferred size for this object. The interpretation of the result depends on the flags returned
187      * by getSizeFlags(). If the caller is looking for a maximum or minimum size, this delegates to computeMinimumSize
188      * or computeMaximumSize in order to benefit from caching optimizations. Otherwise, it delegates to
189      * doComputePreferredSize. Subclasses should overload one of doComputeMinimumSize, doComputeMaximumSize, or
190      * doComputePreferredSize to specialize the return value.
191      *
192      * @see LayoutPart#computePreferredSize(boolean, int, int, int)
193      */

194     public final int computePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredParallel) {
195         assertValidSize(availableParallel);
196         assertValidSize(availablePerpendicular);
197         assertValidSize(preferredParallel);
198         
199         if (!isVisible()) {
200             return 0;
201         }
202
203         if (availableParallel == 0) {
204             return 0;
205         }
206
207         if (preferredParallel == 0) {
208             return Math.min(availableParallel, computeMinimumSize(width, availablePerpendicular));
209         } else if (preferredParallel == INFINITE && availableParallel == INFINITE) {
210             return computeMaximumSize(width, availablePerpendicular);
211         }
212         
213         // Optimization: if this subtree doesn't have any size preferences beyond its minimum and maximum
214
// size, simply return the preferred size
215
if (!hasSizeFlag(width, SWT.FILL)) {
216             return preferredParallel;
217         }
218         
219         int result = doComputePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel);
220
221         return result;
222     }
223     
224     /**
225      * Returns the size flags for this tree.
226      *
227      * @see org.eclipse.ui.presentations.StackPresentation#getSizeFlags(boolean)
228      *
229      * @param b indicates whether the caller wants the flags for computing widths (=true) or heights (=false)
230      * @return a bitwise combiniation of flags with the same meaning as StackPresentation.getSizeFlags(boolean)
231      */

232     protected int doGetSizeFlags(boolean width) {
233         return part.getSizeFlags(width);
234     }
235
236     /**
237      * Subclasses should overload this method instead of computePreferredSize(boolean, int, int, int)
238      *
239      * @see org.eclipse.ui.presentations.StackPresentation#computePreferredSize(boolean, int, int, int)
240      *
241      * @since 3.1
242      */

243     protected int doComputePreferredSize(boolean width, int availableParallel, int availablePerpendicular, int preferredParallel) {
244         int result = Math.min(availableParallel,
245                 part.computePreferredSize(width, availableParallel, availablePerpendicular, preferredParallel));
246
247         assertValidSize(result);
248         return result;
249     }
250     
251     /**
252      * Returns the minimum size for this subtree. Equivalent to calling
253      * computePreferredSize(width, INFINITE, availablePerpendicular, 0).
254      * Returns a cached value if possible or defers to doComputeMinimumSize otherwise.
255      * Subclasses should overload doComputeMinimumSize if they want to specialize the
256      * return value.
257      *
258      * @param width true iff computing the minimum width, false iff computing the minimum height
259      * @param availablePerpendicular available space (pixels) perpendicular to the dimension
260      * being computed. This is a height when computing a width, or a width when computing a height.
261      *
262      * @see LayoutPart#computePreferredSize(boolean, int, int, int)
263      */

264     public final int computeMinimumSize(boolean width, int availablePerpendicular) {
265         assertValidSize(availablePerpendicular);
266         
267         // Optimization: if this subtree has no minimum size, then always return 0 as its
268
// minimum size.
269
if (!hasSizeFlag(width, SWT.MIN)) {
270             return 0;
271         }
272         
273         // If this subtree doesn't contain any wrapping controls (ie: they don't care
274
// about their perpendicular size) then force the perpendicular
275
// size to be INFINITE. This ensures that we will get a cache hit
276
// every time for non-wrapping controls.
277
if (!hasSizeFlag(width, SWT.WRAP)) {
278             availablePerpendicular = INFINITE;
279         }
280         
281         if (width) {
282             // Check if we have a cached width measurement (we can only return a cached
283
// value if we computed it for the same height)
284
if (cachedMinimumWidthHint == availablePerpendicular) {
285                 minCacheHits++;
286                 return cachedMinimumWidth;
287             }
288             
289             // Recompute the minimum width and store it in the cache
290

291             minCacheMisses++;
292              
293             int result = doComputeMinimumSize(width, availablePerpendicular);
294             cachedMinimumWidth = result;
295             cachedMinimumWidthHint = availablePerpendicular;
296             return result;
297             
298         } else {
299             // Check if we have a cached height measurement (we can only return a cached
300
// value if we computed it for the same width)
301
if (cachedMinimumHeightHint == availablePerpendicular) {
302                 minCacheHits++;
303                 return cachedMinimumHeight;
304             }
305             
306             // Recompute the minimum width and store it in the cache
307
minCacheMisses++;
308             
309             int result = doComputeMinimumSize(width, availablePerpendicular);
310             cachedMinimumHeight = result;
311             cachedMinimumHeightHint = availablePerpendicular;
312             return result;
313         }
314     }
315     
316     /**
317      * For use in benchmarks and test suites only. Displays cache utilization statistics for all
318      * LayoutTree instances.
319      *
320      * @since 3.1
321      */

322     public static void printCacheStatistics() {
323         System.out.println("minimize cache " + minCacheHits + " / " + (minCacheHits + minCacheMisses) + " hits " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
324
minCacheHits * 100 / (minCacheHits + minCacheMisses) + "%"); //$NON-NLS-1$
325
System.out.println("maximize cache " + maxCacheHits + " / " + (maxCacheHits + maxCacheMisses) + " hits" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
326
maxCacheHits * 100 / (maxCacheHits + maxCacheMisses) + "%"); //$NON-NLS-1$
327
}
328     
329     public int doComputeMinimumSize(boolean width, int availablePerpendicular) {
330         int result = doComputePreferredSize(width, INFINITE, availablePerpendicular, 0);
331         assertValidSize(result);
332         return result;
333     }
334
335     public final int computeMaximumSize(boolean width, int availablePerpendicular) {
336         assertValidSize(availablePerpendicular);
337         
338         // Optimization: if this subtree has no maximum size, then always return INFINITE as its
339
// maximum size.
340
if (!hasSizeFlag(width, SWT.MAX)) {
341             return INFINITE;
342         }
343         
344         // If this subtree doesn't contain any wrapping controls (ie: they don't care
345
// about their perpendicular size) then force the perpendicular
346
// size to be INFINITE. This ensures that we will get a cache hit
347
// every time.
348
if (!hasSizeFlag(width, SWT.WRAP)) {
349             availablePerpendicular = INFINITE;
350         }
351         
352         if (width) {
353             // Check if we have a cached width measurement (we can only return a cached
354
// value if we computed it for the same height)
355
if (cachedMaximumWidthHint == availablePerpendicular) {
356                 maxCacheHits++;
357                 return cachedMaximumWidth;
358             }
359             
360             maxCacheMisses++;
361             
362             // Recompute the maximum width and store it in the cache
363
int result = doComputeMaximumSize(width, availablePerpendicular);
364             cachedMaximumWidth = result;
365             cachedMaximumWidthHint = availablePerpendicular;
366             return result;
367             
368         } else {
369             // Check if we have a cached height measurement
370
if (cachedMaximumHeightHint == availablePerpendicular) {
371                 maxCacheHits++;
372                 return cachedMaximumHeight;
373             }
374             
375             maxCacheMisses++;
376             
377             // Recompute the maximum height and store it in the cache
378
int result = doComputeMaximumSize(width, availablePerpendicular);
379             cachedMaximumHeight = result;
380             cachedMaximumHeightHint = availablePerpendicular;
381             return result;
382         }
383     }
384     
385     protected int doComputeMaximumSize(boolean width, int availablePerpendicular) {
386         return doComputePreferredSize(width, INFINITE, availablePerpendicular, INFINITE);
387     }
388     
389     /**
390      * Called to flush any cached information in this tree and its parents.
391      */

392     public void flushNode() {
393         
394         // Clear cached sizes
395
cachedMinimumWidthHint = SWT.DEFAULT;
396         cachedMinimumWidth = SWT.DEFAULT;
397         cachedMinimumHeightHint = SWT.DEFAULT;
398         cachedMinimumHeight = SWT.DEFAULT;
399         cachedMaximumWidthHint = SWT.DEFAULT;
400         cachedMaximumWidth = SWT.DEFAULT;
401         cachedMaximumHeightHint = SWT.DEFAULT;
402         cachedMaximumHeight = SWT.DEFAULT;
403         
404         // Flags may have changed. Ensure that they are recomputed the next time around
405
sizeFlagsDirty = true;
406         
407         // The next setBounds call should trigger a layout even if set to the same bounds since
408
// one of the children has changed.
409
forceLayout = true;
410     }
411     
412     /**
413      * Flushes all cached information about this node and all of its children.
414      * This should be called if something may have caused all children to become
415      * out of synch with their cached information (for example, if a lot of changes
416      * may have happened without calling flushCache after each change)
417      *
418      * @since 3.1
419      */

420     public void flushChildren() {
421         flushNode();
422     }
423     
424     /**
425      * Flushes all cached information about this node and all of its ancestors.
426      * This should be called when a single child changes.
427      *
428      * @since 3.1
429      */

430     public final void flushCache() {
431         flushNode();
432         
433         if (parent != null) {
434             parent.flushCache();
435         }
436     }
437     
438     public final int getSizeFlags(boolean width) {
439         if (sizeFlagsDirty) {
440             widthSizeFlags = doGetSizeFlags(true);
441             heightSizeFlags = doGetSizeFlags(false);
442             sizeFlagsDirty = false;
443         }
444         
445         return width ? widthSizeFlags : heightSizeFlags;
446     }
447         
448     /**
449      * Returns the parent of this tree or null if it is the root.
450      */

451     public LayoutTreeNode getParent() {
452         return parent;
453     }
454
455     /**
456      * Inserts a new child on the tree. The child will be placed beside
457      * the <code>relative</code> child. Returns the new root of the tree.
458      */

459     public LayoutTree insert(LayoutPart child, boolean left,
460             LayoutPartSash sash, LayoutPart relative) {
461         LayoutTree relativeChild = find(relative);
462         LayoutTreeNode node = new LayoutTreeNode(sash);
463         if (relativeChild == null) {
464             //Did not find the relative part. Insert beside the root.
465
node.setChild(left, child);
466             node.setChild(!left, this);
467             return node;
468         } else {
469             LayoutTreeNode oldParent = relativeChild.getParent();
470             node.setChild(left, child);
471             node.setChild(!left, relativeChild);
472             if (oldParent == null) {
473                 //It was the root. Return a new root.
474
return node;
475             }
476             oldParent.replaceChild(relativeChild, node);
477             return this;
478         }
479     }
480
481     /**
482      * Returns true if this tree can be compressed and expanded.
483      * @return true if springy
484      */

485     public boolean isCompressible() {
486         //Added for bug 19524
487
return part.isCompressible();
488     }
489
490     /**
491      * Returns true if this tree has visible parts otherwise returns false.
492      */

493     public boolean isVisible() {
494         return !(part instanceof PartPlaceholder);
495     }
496
497     /**
498      * Recompute the ratios in this tree.
499      */

500     public void recomputeRatio() {
501     }
502
503     /**
504      * Find a child in the tree and remove it and its parent.
505      * The other child of its parent is placed on the parent's parent.
506      * Returns the new root of the tree.
507      */

508     public LayoutTree remove(LayoutPart child) {
509         LayoutTree tree = find(child);
510         if (tree == null) {
511             return this;
512         }
513         LayoutTreeNode oldParent = tree.getParent();
514         if (oldParent == null) {
515             //It was the root and the only child of this tree
516
return null;
517         }
518         if (oldParent.getParent() == null) {
519             return oldParent.remove(tree);
520         }
521
522         oldParent.remove(tree);
523         return this;
524     }
525
526     /**
527      * Sets the bounds of this node. If the bounds have changed or any children have
528      * changed then the children will be recursively layed out. This implementation
529      * filters out redundant calls and delegates to doSetBounds to layout the children.
530      * Subclasses should overload doSetBounds to lay out their children.
531      *
532      * @param bounds new bounds of the tree
533      */

534     public final void setBounds(Rectangle bounds) {
535         if (!bounds.equals(currentBounds) || forceLayout) {
536             currentBounds = Geometry.copy(bounds);
537             
538             doSetBounds(currentBounds);
539             forceLayout = false;
540         }
541     }
542     
543     /**
544      * Resize the parts on this tree to fit in <code>bounds</code>.
545      */

546     protected void doSetBounds(Rectangle bounds) {
547         part.setBounds(bounds);
548     }
549
550     /**
551      * Set the parent of this tree.
552      */

553     void setParent(LayoutTreeNode parent) {
554         this.parent = parent;
555     }
556
557     /**
558      * Set the part of this leaf
559      */

560     void setPart(LayoutPart part) {
561         this.part = part;
562         flushCache();
563     }
564
565     /**
566      * Returns a string representation of this object.
567      */

568     public String JavaDoc toString() {
569         return "(" + part.toString() + ")";//$NON-NLS-2$//$NON-NLS-1$
570
}
571
572     /**
573      * Creates SWT controls owned by the LayoutTree (ie: the sashes). Does not affect the
574      * LayoutParts that are being arranged by the LayoutTree.
575      *
576      * @param parent
577      * @since 3.1
578      */

579     public void createControl(Composite parent) {
580     }
581         
582     /**
583      * Writes a description of the layout to the given string buffer.
584      * This is used for drag-drop test suites to determine if two layouts are the
585      * same. Like a hash code, the description should compare as equal iff the
586      * layouts are the same. However, it should be user-readable in order to
587      * help debug failed tests. Although these are english readable strings,
588      * they should not be translated or equality tests will fail.
589      * <p>
590      * This is only intended for use by test suites.
591      * </p>
592      *
593      * @param buf
594      */

595     public void describeLayout(StringBuffer JavaDoc buf) {
596         part.describeLayout(buf);
597     }
598
599     /**
600      * This is a shorthand method that checks if the tree contains the
601      * given size flag. For example, hasSizeFlag(false, SWT.MIN) returns
602      * true iff the receiver enforces a minimum height, or
603      * hasSizeFlag(true, SWT.WRAP) returns true iff the receiver needs to
604      * know its height when computing its preferred width.
605      *
606      * @param vertical
607      * @return
608      * @since 3.1
609      */

610     public final boolean hasSizeFlag(boolean width, int flag) {
611         return (getSizeFlags(width) & flag) != 0;
612     }
613
614 }
615
Popular Tags