KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > forms > widgets > SizeCache


1 /*******************************************************************************
2  * Copyright (c) 2004, 2007 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  *******************************************************************************/

11 package org.eclipse.ui.forms.widgets;
12
13 import java.util.List JavaDoc;
14
15 import org.eclipse.jface.util.Geometry;
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.graphics.Point;
18 import org.eclipse.swt.graphics.Rectangle;
19 import org.eclipse.swt.widgets.Button;
20 import org.eclipse.swt.widgets.Combo;
21 import org.eclipse.swt.widgets.Composite;
22 import org.eclipse.swt.widgets.Control;
23 import org.eclipse.swt.widgets.Label;
24 import org.eclipse.swt.widgets.Layout;
25 import org.eclipse.swt.widgets.ProgressBar;
26 import org.eclipse.swt.widgets.Sash;
27 import org.eclipse.swt.widgets.Scale;
28 import org.eclipse.swt.widgets.Scrollable;
29 import org.eclipse.swt.widgets.Slider;
30 import org.eclipse.swt.widgets.Text;
31 import org.eclipse.swt.widgets.ToolBar;
32 import org.eclipse.swt.widgets.Tree;
33 import org.eclipse.ui.internal.forms.widgets.FormUtil;
34
35 /**
36  * Caches the preferred size of an SWT control
37  *
38  * @since 3.0
39  */

40 public class SizeCache {
41     private Control control;
42
43     private Point preferredSize;
44
45     private int cachedWidthQuery;
46     private int cachedWidthResult;
47
48     private int cachedHeightQuery;
49     private int cachedHeightResult;
50     
51     private int minimumWidth;
52     private int heightAtMinimumWidth = -1;
53     private int maximumWidth;
54     
55     /**
56      * True iff we should recursively flush all children on the next layout
57      */

58     private boolean flushChildren;
59
60     /**
61      * True iff changing the height hint does not affect the preferred width and changing
62      * the width hint does not change the preferred height
63      */

64     private boolean independentDimensions = false;
65
66     /**
67      * True iff the preferred height for any hint larger than the preferred width will not
68      * change the preferred height.
69      */

70     private boolean preferredWidthOrLargerIsMinimumHeight = false;
71
72     // HACK: these values estimate how much to subtract from the width and height
73
// hints that get passed into computeSize, in order to produce a result
74
// that is exactly the desired size. To be removed once bug 46112 is fixed (note:
75
// bug 46112 is currently flagged as a duplicate, but there is still no workaround).
76
private int widthAdjustment = 0;
77
78     private int heightAdjustment = 0;
79
80     private int minimumHeight;
81
82     private int widthAtMinimumHeight = -1;
83     
84     // If the layout is dirty, this is the size of the control at the time its
85
// layout was dirtied. null if the layout is not dirty.
86
private Point dirtySize = null;
87
88
89     // END OF HACK
90

91     public SizeCache() {
92         this(null);
93     }
94
95     /**
96      * Creates a cache for size computations on the given control
97      *
98      * @param control the control for which sizes will be calculated,
99      * or null to always return (0,0)
100      */

101     public SizeCache(Control control) {
102         setControl(control);
103     }
104
105     /**
106      * Sets the control whose size is being cached. Does nothing (will not
107      * even flush the cache) if this is the same control as last time.
108      *
109      * @param newControl the control whose size is being cached, or null to always return (0,0)
110      */

111     public void setControl(Control newControl) {
112         if (newControl != control) {
113             control = newControl;
114             if (control == null) {
115                 independentDimensions = true;
116                 preferredWidthOrLargerIsMinimumHeight = false;
117                 widthAdjustment = 0;
118                 heightAdjustment = 0;
119             } else {
120                 independentDimensions = independentLengthAndWidth(control);
121                 preferredWidthOrLargerIsMinimumHeight = isPreferredWidthMaximum(control);
122                 computeHintOffset(control);
123                 flush();
124             }
125         }
126     }
127
128     /**
129      * Returns the control whose size is being cached
130      *
131      * @return the control whose size is being cached, or null if this cache always returns (0,0)
132      */

133     public Control getControl() {
134         return control;
135     }
136
137     /**
138      * Flush the cache (should be called if the control's contents may have changed since the
139      * last query)
140      */

141     public void flush() {
142         flush(true);
143     }
144
145     public void flush(boolean recursive) {
146         preferredSize = null;
147         cachedWidthQuery = -1;
148         cachedWidthResult = -1;
149         cachedHeightQuery = -1;
150         cachedHeightResult = -1;
151         minimumWidth = -1;
152         maximumWidth = -1;
153         minimumHeight = -1;
154         heightAtMinimumWidth = -1;
155         widthAtMinimumHeight = -1;
156         
157         if (recursive || dirtySize != null) {
158             if (control == null || control.isDisposed()) {
159                 dirtySize = new Point(0,0);
160                 control = null;
161             } else {
162                 dirtySize = control.getSize();
163             }
164         }
165         
166         this.flushChildren = this.flushChildren || recursive;
167     }
168
169     private Point getPreferredSize() {
170         if (preferredSize == null) {
171             preferredSize = controlComputeSize(SWT.DEFAULT, SWT.DEFAULT);
172         }
173
174         return preferredSize;
175     }
176
177     /**
178      * Computes the preferred size of the control.
179      *
180      * @param widthHint the known width of the control (pixels) or SWT.DEFAULT if unknown
181      * @param heightHint the known height of the control (pixels) or SWT.DEFAULT if unknown
182      * @return the preferred size of the control
183      */

184     public Point computeSize(int widthHint, int heightHint) {
185         if (control == null || control.isDisposed()) {
186             return new Point(0, 0);
187         }
188         
189         // If we're asking for a result smaller than the minimum width
190
int minWidth = computeMinimumWidth();
191         
192         if (widthHint != SWT.DEFAULT && widthHint + widthAdjustment < minWidth) {
193             if (heightHint == SWT.DEFAULT) {
194                 return new Point(minWidth, computeHeightAtMinimumWidth());
195             }
196             
197             widthHint = minWidth - widthAdjustment;
198         }
199         
200         // If we're asking for a result smaller than the minimum height
201
int minHeight = computeMinimumHeight();
202         
203         if (heightHint != SWT.DEFAULT && heightHint + heightAdjustment < minHeight) {
204             if (widthHint == SWT.DEFAULT) {
205                 return new Point(computeWidthAtMinimumHeight(), minHeight);
206             }
207             
208             heightHint = minHeight - heightAdjustment;
209         }
210         
211         // If both dimensions were supplied in the input, compute the trivial result
212
if (widthHint != SWT.DEFAULT && heightHint != SWT.DEFAULT) {
213             return new Point(widthHint + widthAdjustment, heightHint + heightAdjustment);
214         }
215
216         // No hints given -- find the preferred size
217
if (widthHint == SWT.DEFAULT && heightHint == SWT.DEFAULT) {
218             return Geometry.copy(getPreferredSize());
219         }
220
221         // If the length and width are independent, compute the preferred size
222
// and adjust whatever dimension was supplied in the input
223
if (independentDimensions) {
224             Point result = Geometry.copy(getPreferredSize());
225
226             if (widthHint != SWT.DEFAULT) {
227                 result.x = widthHint + widthAdjustment;
228             }
229
230             if (heightHint != SWT.DEFAULT) {
231                 result.y = heightHint + heightAdjustment;
232             }
233
234             return result;
235         }
236
237         // Computing a height
238
if (heightHint == SWT.DEFAULT) {
239             // If we know the control's preferred size
240
if (preferredSize != null) {
241                 // If the given width is the preferred width, then return the preferred size
242
if (widthHint + widthAdjustment == preferredSize.x) {
243                     return Geometry.copy(preferredSize);
244                 }
245             }
246
247             // If we have a cached height measurement
248
if (cachedHeightQuery != -1) {
249                 // If this was measured with the same width hint
250
if (cachedHeightQuery == widthHint) {
251                     return new Point(widthHint + widthAdjustment, cachedHeightResult);
252                 }
253             }
254
255             // If this is a control where any hint larger than the
256
// preferred width results in the minimum height, determine if
257
// we can compute the result based on the preferred height
258
if (preferredWidthOrLargerIsMinimumHeight) {
259                 // Computed the preferred size (if we don't already know it)
260
getPreferredSize();
261
262                 // If the width hint is larger than the preferred width, then
263
// we can compute the result from the preferred width
264
if (widthHint + widthAdjustment >= preferredSize.x) {
265                     return new Point(widthHint + widthAdjustment, preferredSize.y);
266                 }
267             }
268
269             // Else we can't find an existing size in the cache, so recompute
270
// it from scratch.
271
Point newHeight = controlComputeSize(widthHint - widthAdjustment, SWT.DEFAULT);
272
273             cachedHeightQuery = heightHint;
274             cachedHeightResult = newHeight.y;
275             
276             return newHeight;
277         }
278
279         // Computing a width
280
if (widthHint == SWT.DEFAULT) {
281             // If we know the control's preferred size
282
if (preferredSize != null) {
283                 // If the given height is the preferred height, then return the preferred size
284
if (heightHint + heightAdjustment == preferredSize.y) {
285                     return Geometry.copy(preferredSize);
286                 }
287             }
288
289             // If we have a cached width measurement with the same height hint
290
if (cachedWidthQuery == heightHint) {
291                 return new Point(cachedWidthResult, heightHint + heightAdjustment);
292             }
293
294             Point widthResult = controlComputeSize(SWT.DEFAULT, heightHint - heightAdjustment);
295
296             cachedWidthQuery = heightHint;
297             cachedWidthResult = widthResult.x;
298             
299             return widthResult;
300         }
301
302         return controlComputeSize(widthHint, heightHint);
303     }
304     
305     /**
306      * Compute the control's size, and ensure that non-default hints are returned verbatim
307      * (this tries to compensate for SWT's hints, which aren't really the outer width of the
308      * control).
309      *
310      * @param widthHint the horizontal hint
311      * @param heightHint the vertical hint
312      * @return the control's size
313      */

314     public Point computeAdjustedSize(int widthHint, int heightHint) {
315         int adjustedWidthHint = widthHint == SWT.DEFAULT ? SWT.DEFAULT : Math
316                 .max(0, widthHint - widthAdjustment);
317         int adjustedHeightHint = heightHint == SWT.DEFAULT ? SWT.DEFAULT : Math
318                 .max(0, heightHint - heightAdjustment);
319
320         Point result = computeSize(adjustedWidthHint, adjustedHeightHint);
321
322         // If the amounts we subtracted off the widthHint and heightHint didn't do the trick, then
323
// manually adjust the result to ensure that a non-default hint will return that result verbatim.
324

325         return result;
326     }
327
328     /**
329      * Returns true if the preferred length of the given control is
330      * independent of the width and visa-versa. If this returns true,
331      * then changing the widthHint argument to control.computeSize will
332      * never change the resulting height and changing the heightHint
333      * will never change the resulting width. Returns false if unknown.
334      * <p>
335      * This information can be used to improve caching. Incorrectly returning
336      * a value of false may decrease performance, but incorrectly returning
337      * a value of true will generate incorrect layouts... so always return
338      * false if unsure.
339      * </p>
340      *
341      * @param control
342      * @return
343      */

344     static boolean independentLengthAndWidth(Control control) {
345         if (control == null || control.isDisposed()) {
346             return true;
347         }
348
349         if (control instanceof Button || control instanceof ProgressBar
350                 || control instanceof Sash || control instanceof Scale
351                 || control instanceof Slider || control instanceof List JavaDoc
352                 || control instanceof Combo || control instanceof Tree) {
353             return true;
354         }
355
356         if (control instanceof Label || control instanceof Text) {
357             return (control.getStyle() & SWT.WRAP) == 0;
358         }
359
360         // Unless we're certain that the control has this property, we should
361
// return false.
362

363         return false;
364     }
365     
366     /**
367      * Try to figure out how much we need to subtract from the hints that we
368      * pass into the given control's computeSize(...) method. This tries to
369      * compensate for bug 46112. To be removed once SWT provides an "official"
370      * way to compute one dimension of a control's size given the other known
371      * dimension.
372      *
373      * @param control
374      */

375     private void computeHintOffset(Control control) {
376         if (control instanceof Scrollable) {
377             // For scrollables, subtract off the trim size
378
Scrollable scrollable = (Scrollable) control;
379             Rectangle trim = scrollable.computeTrim(0, 0, 0, 0);
380
381             widthAdjustment = trim.width;
382             heightAdjustment = trim.height;
383         } else {
384             // For non-composites, subtract off 2 * the border size
385
widthAdjustment = control.getBorderWidth() * 2;
386             heightAdjustment = widthAdjustment;
387         }
388     }
389
390     private Point controlComputeSize(int widthHint, int heightHint) {
391         Point result = control.computeSize(widthHint, heightHint, flushChildren);
392         flushChildren = false;
393         
394         return result;
395     }
396
397     /**
398      * Returns true only if the control will return a constant height for any
399      * width hint larger than the preferred width. Returns false if there is
400      * any situation in which the control does not have this property.
401      *
402      * <p>
403      * Note: this method is only important for wrapping controls, and it can
404      * safely return false for anything else. AFAIK, all SWT controls have this
405      * property, but to be safe they will only be added to the list once the
406      * property has been confirmed.
407      * </p>
408      *
409      * @param control
410      * @return
411      */

412     private static boolean isPreferredWidthMaximum(Control control) {
413         return (control instanceof ToolBar
414         //|| control instanceof CoolBar
415
|| control instanceof Label);
416     }
417     
418     public int computeMinimumWidth() {
419         if (minimumWidth == -1) {
420             if (control instanceof Composite) {
421                 Layout layout = ((Composite)control).getLayout();
422                 if (layout instanceof ILayoutExtension) {
423                     minimumWidth = ((ILayoutExtension)layout).computeMinimumWidth((Composite)control, flushChildren);
424                     flushChildren = false;
425                 }
426             }
427         }
428
429         if (minimumWidth == -1) {
430             Point minWidth = controlComputeSize(FormUtil.getWidthHint(5, control), SWT.DEFAULT);
431             minimumWidth = minWidth.x;
432             heightAtMinimumWidth = minWidth.y;
433         }
434         
435         return minimumWidth;
436     }
437     
438     public int computeMaximumWidth() {
439         if (maximumWidth == -1) {
440             if (control instanceof Composite) {
441                 Layout layout = ((Composite)control).getLayout();
442                 if (layout instanceof ILayoutExtension) {
443                     maximumWidth = ((ILayoutExtension)layout).computeMaximumWidth((Composite)control, flushChildren);
444                     flushChildren = false;
445                 }
446             }
447         }
448
449         if (maximumWidth == -1) {
450             maximumWidth = getPreferredSize().x;
451         }
452         
453         return maximumWidth;
454     }
455     
456     private int computeHeightAtMinimumWidth() {
457         int minimumWidth = computeMinimumWidth();
458         
459         if (heightAtMinimumWidth == -1) {
460             heightAtMinimumWidth = controlComputeSize(minimumWidth - widthAdjustment, SWT.DEFAULT).y;
461         }
462         
463         return heightAtMinimumWidth;
464     }
465     
466     private int computeWidthAtMinimumHeight() {
467         int minimumHeight = computeMinimumHeight();
468         
469         if (widthAtMinimumHeight == -1) {
470             widthAtMinimumHeight = controlComputeSize(SWT.DEFAULT, minimumHeight - heightAdjustment).x;
471         }
472         
473         return widthAtMinimumHeight;
474     }
475
476     private int computeMinimumHeight() {
477         if (minimumHeight == -1) {
478             Point sizeAtMinHeight = controlComputeSize(SWT.DEFAULT, 0);
479             
480             minimumHeight = sizeAtMinHeight.y;
481             widthAtMinimumHeight = sizeAtMinHeight.x;
482         }
483         
484         return minimumHeight;
485     }
486     
487     public Point computeMinimumSize() {
488         return new Point(computeMinimumWidth(), computeMinimumHeight());
489     }
490
491     public void setSize(Point newSize) {
492         if (control != null) {
493             control.setSize(newSize);
494         }
495         
496         layoutIfNecessary();
497     }
498     
499     public void setSize(int width, int height) {
500         if (control != null) {
501             control.setSize(width, height);
502         }
503         
504         layoutIfNecessary();
505     }
506     
507     public void setBounds(int x, int y, int width, int height) {
508         if (control != null) {
509             control.setBounds(x, y, width, height);
510         }
511         
512         layoutIfNecessary();
513     }
514     
515     public void setBounds(Rectangle bounds) {
516         if (control != null) {
517             control.setBounds(bounds);
518         }
519         
520         layoutIfNecessary();
521     }
522
523     public void layoutIfNecessary() {
524         if (dirtySize != null && control != null && control instanceof Composite) {
525             if (control.getSize().equals(dirtySize)) {
526                 ((Composite)control).layout(flushChildren);
527                 flushChildren = false;
528             }
529         }
530         dirtySize = null;
531     }
532 }
533
Popular Tags