KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > layout > SizeCache


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

11 package org.eclipse.ui.internal.layout;
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.ProgressBar;
25 import org.eclipse.swt.widgets.Sash;
26 import org.eclipse.swt.widgets.Scale;
27 import org.eclipse.swt.widgets.Slider;
28 import org.eclipse.swt.widgets.Text;
29 import org.eclipse.swt.widgets.ToolBar;
30 import org.eclipse.swt.widgets.Tree;
31
32 /**
33  * Caches the preferred size of an SWT control
34  *
35  * @since 3.0
36  */

37 public class SizeCache {
38     private Control control;
39
40     private Point preferredSize;
41
42     private Point cachedWidth;
43
44     private Point cachedHeight;
45
46     /**
47      * True iff we should recursively flush all children on the next layout
48      */

49     private boolean flushChildren;
50
51     /**
52      * True iff changing the height hint does not affect the preferred width and changing
53      * the width hint does not change the preferred height
54      */

55     private boolean independentDimensions = false;
56
57     /**
58      * True iff the preferred height for any hint larger than the preferred width will not
59      * change the preferred height.
60      */

61     private boolean preferredWidthOrLargerIsMinimumHeight = false;
62
63     // HACK: these values estimate how much to subtract from the width and height
64
// hints that get passed into computeSize, in order to produce a result
65
// that is exactly the desired size. To be removed once bug 46112 is fixed (note:
66
// bug 46112 is currently flagged as a duplicate, but there is still no workaround).
67
private int widthAdjustment = 0;
68
69     private int heightAdjustment = 0;
70
71     // END OF HACK
72

73     public SizeCache() {
74         this(null);
75     }
76
77     /**
78      * Creates a cache for size computations on the given control
79      *
80      * @param control the control for which sizes will be calculated,
81      * or null to always return (0,0)
82      */

83     public SizeCache(Control control) {
84         setControl(control);
85     }
86
87     /**
88      * Sets the control whose size is being cached. Does nothing (will not
89      * even flush the cache) if this is the same control as last time.
90      *
91      * @param newControl the control whose size is being cached, or null to always return (0,0)
92      */

93     public void setControl(Control newControl) {
94         if (newControl != control) {
95             control = newControl;
96             if (control == null) {
97                 independentDimensions = true;
98                 preferredWidthOrLargerIsMinimumHeight = false;
99                 widthAdjustment = 0;
100                 heightAdjustment = 0;
101             } else {
102                 independentDimensions = independentLengthAndWidth(control);
103                 preferredWidthOrLargerIsMinimumHeight = isPreferredWidthMaximum(control);
104                 computeHintOffset(control);
105                 flush();
106             }
107         }
108     }
109
110     /**
111      * Returns the control whose size is being cached
112      *
113      * @return the control whose size is being cached, or null if this cache always returns (0,0)
114      */

115     public Control getControl() {
116         return control;
117     }
118
119     /**
120      * Flush the cache (should be called if the control's contents may have changed since the
121      * last query)
122      */

123     public void flush() {
124         flush(true);
125     }
126
127     public void flush(boolean recursive) {
128         preferredSize = null;
129         cachedWidth = null;
130         cachedHeight = null;
131         this.flushChildren = recursive;
132     }
133
134     private Point getPreferredSize() {
135         if (preferredSize == null) {
136             preferredSize = computeSize(control, SWT.DEFAULT, SWT.DEFAULT);
137         }
138
139         return preferredSize;
140     }
141
142     /**
143      * Computes the preferred size of the control.
144      *
145      * @param widthHint the known width of the control (pixels) or SWT.DEFAULT if unknown
146      * @param heightHint the known height of the control (pixels) or SWT.DEFAULT if unknown
147      * @return the preferred size of the control
148      */

149     public Point computeSize(int widthHint, int heightHint) {
150         if (control == null) {
151             return new Point(0, 0);
152         }
153
154         // If both dimensions were supplied in the input, return them verbatim
155
if (widthHint != SWT.DEFAULT && heightHint != SWT.DEFAULT) {
156             return new Point(widthHint, heightHint);
157         }
158
159         // No hints given -- find the preferred size
160
if (widthHint == SWT.DEFAULT && heightHint == SWT.DEFAULT) {
161             return Geometry.copy(getPreferredSize());
162         }
163
164         // If the length and width are independent, compute the preferred size
165
// and adjust whatever dimension was supplied in the input
166
if (independentDimensions) {
167             Point result = Geometry.copy(getPreferredSize());
168
169             if (widthHint != SWT.DEFAULT) {
170                 result.x = widthHint;
171             }
172
173             if (heightHint != SWT.DEFAULT) {
174                 result.y = heightHint;
175             }
176
177             return result;
178         }
179
180         // Computing a height
181
if (heightHint == SWT.DEFAULT) {
182             // If we know the control's preferred size
183
if (preferredSize != null) {
184                 // If the given width is the preferred width, then return the preferred size
185
if (widthHint == preferredSize.x) {
186                     return Geometry.copy(preferredSize);
187                 }
188             }
189
190             // If we have a cached height measurement
191
if (cachedHeight != null) {
192                 // If this was measured with the same width hint
193
if (cachedHeight.x == widthHint) {
194                     return Geometry.copy(cachedHeight);
195                 }
196             }
197
198             // If this is a control where any hint larger than the
199
// preferred width results in the minimum height, determine if
200
// we can compute the result based on the preferred height
201
if (preferredWidthOrLargerIsMinimumHeight) {
202                 // Computed the preferred size (if we don't already know it)
203
getPreferredSize();
204
205                 // If the width hint is larger than the preferred width, then
206
// we can compute the result from the preferred width
207
if (widthHint >= preferredSize.x) {
208                     Point result = Geometry.copy(preferredSize);
209                     result.x = widthHint;
210                     return result;
211                 }
212             }
213
214             // Else we can't find an existing size in the cache, so recompute
215
// it from scratch.
216
cachedHeight = computeSize(control, widthHint, heightHint);
217
218             return Geometry.copy(cachedHeight);
219         }
220
221         // Computing a width
222
if (widthHint == SWT.DEFAULT) {
223             // If we know the control's preferred size
224
if (preferredSize != null) {
225                 // If the given height is the preferred height, then return the preferred size
226
if (heightHint == preferredSize.y) {
227                     return Geometry.copy(preferredSize);
228                 }
229             }
230
231             // If we have a cached width measurement
232
if (cachedWidth != null) {
233                 // If this was measured with the same height hint
234
if (cachedWidth.y == heightHint) {
235                     return Geometry.copy(cachedWidth);
236                 }
237             }
238
239             cachedWidth = computeSize(control, widthHint, heightHint);
240
241             return Geometry.copy(cachedWidth);
242         }
243
244         return computeSize(control, widthHint, heightHint);
245     }
246
247     /**
248      * Compute the control's size, and ensure that non-default hints are returned verbatim
249      * (this tries to compensate for SWT's hints, which aren't really the outer width of the
250      * control).
251      *
252      * @param control
253      * @param widthHint
254      * @param heightHint
255      * @return
256      */

257     private Point computeSize(Control control, int widthHint, int heightHint) {
258         int adjustedWidthHint = widthHint == SWT.DEFAULT ? SWT.DEFAULT : Math
259                 .max(0, widthHint - widthAdjustment);
260         int adjustedHeightHint = heightHint == SWT.DEFAULT ? SWT.DEFAULT : Math
261                 .max(0, heightHint - heightAdjustment);
262
263         Point result = control.computeSize(adjustedWidthHint,
264                 adjustedHeightHint, flushChildren);
265         flushChildren = false;
266
267         // If the amounts we subtracted off the widthHint and heightHint didn't do the trick, then
268
// manually adjust the result to ensure that a non-default hint will return that result verbatim.
269

270         if (widthHint != SWT.DEFAULT) {
271             result.x = widthHint;
272         }
273
274         if (heightHint != SWT.DEFAULT) {
275             result.y = heightHint;
276         }
277
278         return result;
279     }
280
281     /**
282      * Returns true if the preferred length of the given control is
283      * independent of the width and visa-versa. If this returns true,
284      * then changing the widthHint argument to control.computeSize will
285      * never change the resulting height and changing the heightHint
286      * will never change the resulting width. Returns false if unknown.
287      * <p>
288      * This information can be used to improve caching. Incorrectly returning
289      * a value of false may decrease performance, but incorrectly returning
290      * a value of true will generate incorrect layouts... so always return
291      * false if unsure.
292      * </p>
293      *
294      * @param control
295      * @return
296      */

297     static boolean independentLengthAndWidth(Control control) {
298         if (control == null) {
299             return true;
300         }
301
302         if (control instanceof Button || control instanceof ProgressBar
303                 || control instanceof Sash || control instanceof Scale
304                 || control instanceof Slider || control instanceof List JavaDoc
305                 || control instanceof Combo || control instanceof Tree) {
306             return true;
307         }
308
309         if (control instanceof Label || control instanceof Text) {
310             return (control.getStyle() & SWT.WRAP) == 0;
311         }
312
313         // Unless we're certain that the control has this property, we should
314
// return false.
315

316         return false;
317     }
318
319     /**
320      * Try to figure out how much we need to subtract from the hints that we
321      * pass into the given control's computeSize(...) method. This tries to
322      * compensate for bug 46112. To be removed once SWT provides an "official"
323      * way to compute one dimension of a control's size given the other known
324      * dimension.
325      *
326      * @param control
327      */

328     private void computeHintOffset(Control control) {
329         if (control instanceof Composite) {
330             // For composites, subtract off the trim size
331
Composite composite = (Composite) control;
332             Rectangle trim = composite.computeTrim(0, 0, 0, 0);
333
334             widthAdjustment = trim.width;
335             heightAdjustment = trim.height;
336         } else {
337             // For non-composites, subtract off 2 * the border size
338
widthAdjustment = control.getBorderWidth() * 2;
339             heightAdjustment = widthAdjustment;
340         }
341     }
342
343     /**
344      * Returns true only if the control will return a constant height for any
345      * width hint larger than the preferred width. Returns false if there is
346      * any situation in which the control does not have this property.
347      *
348      * <p>
349      * Note: this method is only important for wrapping controls, and it can
350      * safely return false for anything else. AFAIK, all SWT controls have this
351      * property, but to be safe they will only be added to the list once the
352      * property has been confirmed.
353      * </p>
354      *
355      * @param control
356      * @return
357      */

358     private static boolean isPreferredWidthMaximum(Control control) {
359         return (control instanceof ToolBar
360         //|| control instanceof CoolBar
361
|| control instanceof Label);
362     }
363
364 }
365
Popular Tags