blob: 2006033fb5b175533fc3c1b3737aae56eb95c3f2 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing;
26
27
28import java.awt.*;
29import java.io.Serializable;
30
31/**
32 * For the convenience of layout managers,
33 * calculates information about the size and position of components.
34 * All size and position calculation methods are class methods
35 * that take arrays of SizeRequirements as arguments.
36 * The SizeRequirements class supports two types of layout:
37 *
38 * <blockquote>
39 * <dl>
40 * <dt> tiled
41 * <dd> The components are placed end-to-end,
42 * starting either at coordinate 0 (the leftmost or topmost position)
43 * or at the coordinate representing the end of the allocated span
44 * (the rightmost or bottommost position).
45 *
46 * <dt> aligned
47 * <dd> The components are aligned as specified
48 * by each component's X or Y alignment value.
49 * </dl>
50 * </blockquote>
51 *
52 * <p>
53 *
54 * Each SizeRequirements object contains information
55 * about either the width (and X alignment)
56 * or height (and Y alignment)
57 * of a single component or a group of components:
58 *
59 * <blockquote>
60 * <dl>
61 * <dt> <code>minimum</code>
62 * <dd> The smallest reasonable width/height of the component
63 * or component group, in pixels.
64 *
65 * <dt> <code>preferred</code>
66 * <dd> The natural width/height of the component
67 * or component group, in pixels.
68 *
69 * <dt> <code>maximum</code>
70 * <dd> The largest reasonable width/height of the component
71 * or component group, in pixels.
72 *
73 * <dt> <code>alignment</code>
74 * <dd> The X/Y alignment of the component
75 * or component group.
76 * </dl>
77 * </blockquote>
78 * <p>
79 * <strong>Warning:</strong>
80 * Serialized objects of this class will not be compatible with
81 * future Swing releases. The current serialization support is
82 * appropriate for short term storage or RMI between applications running
83 * the same version of Swing. As of 1.4, support for long term storage
84 * of all JavaBeans<sup><font size="-2">TM</font></sup>
85 * has been added to the <code>java.beans</code> package.
86 * Please see {@link java.beans.XMLEncoder}.
87 *
88 * @see Component#getMinimumSize
89 * @see Component#getPreferredSize
90 * @see Component#getMaximumSize
91 * @see Component#getAlignmentX
92 * @see Component#getAlignmentY
93 *
94 * @author Timothy Prinzing
95 */
96public class SizeRequirements implements Serializable {
97
98 /**
99 * The minimum size required.
100 * For a component <code>comp</code>, this should be equal to either
101 * <code>comp.getMinimumSize().width</code> or
102 * <code>comp.getMinimumSize().height</code>.
103 */
104 public int minimum;
105
106 /**
107 * The preferred (natural) size.
108 * For a component <code>comp</code>, this should be equal to either
109 * <code>comp.getPreferredSize().width</code> or
110 * <code>comp.getPreferredSize().height</code>.
111 */
112 public int preferred;
113
114 /**
115 * The maximum size allowed.
116 * For a component <code>comp</code>, this should be equal to either
117 * <code>comp.getMaximumSize().width</code> or
118 * <code>comp.getMaximumSize().height</code>.
119 */
120 public int maximum;
121
122 /**
123 * The alignment, specified as a value between 0.0 and 1.0,
124 * inclusive.
125 * To specify centering, the alignment should be 0.5.
126 */
127 public float alignment;
128
129 /**
130 * Creates a SizeRequirements object with the minimum, preferred,
131 * and maximum sizes set to zero and an alignment value of 0.5
132 * (centered).
133 */
134 public SizeRequirements() {
135 minimum = 0;
136 preferred = 0;
137 maximum = 0;
138 alignment = 0.5f;
139 }
140
141 /**
142 * Creates a SizeRequirements object with the specified minimum, preferred,
143 * and maximum sizes and the specified alignment.
144 *
145 * @param min the minimum size >= 0
146 * @param pref the preferred size >= 0
147 * @param max the maximum size >= 0
148 * @param a the alignment >= 0.0f && <= 1.0f
149 */
150 public SizeRequirements(int min, int pref, int max, float a) {
151 minimum = min;
152 preferred = pref;
153 maximum = max;
154 alignment = a > 1.0f ? 1.0f : a < 0.0f ? 0.0f : a;
155 }
156
157 /**
158 * Returns a string describing the minimum, preferred, and maximum
159 * size requirements, along with the alignment.
160 *
161 * @return the string
162 */
163 public String toString() {
164 return "[" + minimum + "," + preferred + "," + maximum + "]@" + alignment;
165 }
166
167 /**
168 * Determines the total space necessary to
169 * place a set of components end-to-end. The needs
170 * of each component in the set are represented by an entry in the
171 * passed-in SizeRequirements array.
172 * The returned SizeRequirements object has an alignment of 0.5
173 * (centered). The space requirement is never more than
174 * Integer.MAX_VALUE.
175 *
176 * @param children the space requirements for a set of components.
177 * The vector may be of zero length, which will result in a
178 * default SizeRequirements object instance being passed back.
179 * @return the total space requirements.
180 */
181 public static SizeRequirements getTiledSizeRequirements(SizeRequirements[]
182 children) {
183 SizeRequirements total = new SizeRequirements();
184 for (int i = 0; i < children.length; i++) {
185 SizeRequirements req = children[i];
186 total.minimum = (int) Math.min((long) total.minimum + (long) req.minimum, Integer.MAX_VALUE);
187 total.preferred = (int) Math.min((long) total.preferred + (long) req.preferred, Integer.MAX_VALUE);
188 total.maximum = (int) Math.min((long) total.maximum + (long) req.maximum, Integer.MAX_VALUE);
189 }
190 return total;
191 }
192
193 /**
194 * Determines the total space necessary to
195 * align a set of components. The needs
196 * of each component in the set are represented by an entry in the
197 * passed-in SizeRequirements array. The total space required will
198 * never be more than Integer.MAX_VALUE.
199 *
200 * @param children the set of child requirements. If of zero length,
201 * the returns result will be a default instance of SizeRequirements.
202 * @return the total space requirements.
203 */
204 public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[]
205 children) {
206 SizeRequirements totalAscent = new SizeRequirements();
207 SizeRequirements totalDescent = new SizeRequirements();
208 for (int i = 0; i < children.length; i++) {
209 SizeRequirements req = children[i];
210
211 int ascent = (int) (req.alignment * req.minimum);
212 int descent = req.minimum - ascent;
213 totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
214 totalDescent.minimum = Math.max(descent, totalDescent.minimum);
215
216 ascent = (int) (req.alignment * req.preferred);
217 descent = req.preferred - ascent;
218 totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
219 totalDescent.preferred = Math.max(descent, totalDescent.preferred);
220
221 ascent = (int) (req.alignment * req.maximum);
222 descent = req.maximum - ascent;
223 totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
224 totalDescent.maximum = Math.max(descent, totalDescent.maximum);
225 }
226 int min = (int) Math.min((long) totalAscent.minimum + (long) totalDescent.minimum, Integer.MAX_VALUE);
227 int pref = (int) Math.min((long) totalAscent.preferred + (long) totalDescent.preferred, Integer.MAX_VALUE);
228 int max = (int) Math.min((long) totalAscent.maximum + (long) totalDescent.maximum, Integer.MAX_VALUE);
229 float alignment = 0.0f;
230 if (min > 0) {
231 alignment = (float) totalAscent.minimum / min;
232 alignment = alignment > 1.0f ? 1.0f : alignment < 0.0f ? 0.0f : alignment;
233 }
234 return new SizeRequirements(min, pref, max, alignment);
235 }
236
237 /**
238 * Creates a set of offset/span pairs representing how to
239 * lay out a set of components end-to-end.
240 * This method requires that you specify
241 * the total amount of space to be allocated,
242 * the size requirements for each component to be placed
243 * (specified as an array of SizeRequirements), and
244 * the total size requirement of the set of components.
245 * You can get the total size requirement
246 * by invoking the getTiledSizeRequirements method. The components
247 * will be tiled in the forward direction with offsets increasing from 0.
248 *
249 * @param allocated the total span to be allocated >= 0.
250 * @param total the total of the children requests. This argument
251 * is optional and may be null.
252 * @param children the size requirements for each component.
253 * @param offsets the offset from 0 for each child where
254 * the spans were allocated (determines placement of the span).
255 * @param spans the span allocated for each child to make the
256 * total target span.
257 */
258 public static void calculateTiledPositions(int allocated,
259 SizeRequirements total,
260 SizeRequirements[] children,
261 int[] offsets,
262 int[] spans) {
263 calculateTiledPositions(allocated, total, children, offsets, spans, true);
264 }
265
266 /**
267 * Creates a set of offset/span pairs representing how to
268 * lay out a set of components end-to-end.
269 * This method requires that you specify
270 * the total amount of space to be allocated,
271 * the size requirements for each component to be placed
272 * (specified as an array of SizeRequirements), and
273 * the total size requirement of the set of components.
274 * You can get the total size requirement
275 * by invoking the getTiledSizeRequirements method.
276 *
277 * This method also requires a flag indicating whether components
278 * should be tiled in the forward direction (offsets increasing
279 * from 0) or reverse direction (offsets decreasing from the end
280 * of the allocated space). The forward direction represents
281 * components tiled from left to right or top to bottom. The
282 * reverse direction represents components tiled from right to left
283 * or bottom to top.
284 *
285 * @param allocated the total span to be allocated >= 0.
286 * @param total the total of the children requests. This argument
287 * is optional and may be null.
288 * @param children the size requirements for each component.
289 * @param offsets the offset from 0 for each child where
290 * the spans were allocated (determines placement of the span).
291 * @param spans the span allocated for each child to make the
292 * total target span.
293 * @param forward tile with offsets increasing from 0 if true
294 * and with offsets decreasing from the end of the allocated space
295 * if false.
296 * @since 1.4
297 */
298 public static void calculateTiledPositions(int allocated,
299 SizeRequirements total,
300 SizeRequirements[] children,
301 int[] offsets,
302 int[] spans,
303 boolean forward) {
304 // The total argument turns out to be a bad idea since the
305 // total of all the children can overflow the integer used to
306 // hold the total. The total must therefore be calculated and
307 // stored in long variables.
308 long min = 0;
309 long pref = 0;
310 long max = 0;
311 for (int i = 0; i < children.length; i++) {
312 min += children[i].minimum;
313 pref += children[i].preferred;
314 max += children[i].maximum;
315 }
316 if (allocated >= pref) {
317 expandedTile(allocated, min, pref, max, children, offsets, spans, forward);
318 } else {
319 compressedTile(allocated, min, pref, max, children, offsets, spans, forward);
320 }
321 }
322
323 private static void compressedTile(int allocated, long min, long pref, long max,
324 SizeRequirements[] request,
325 int[] offsets, int[] spans,
326 boolean forward) {
327
328 // ---- determine what we have to work with ----
329 float totalPlay = Math.min(pref - allocated, pref - min);
330 float factor = (pref - min == 0) ? 0.0f : totalPlay / (pref - min);
331
332 // ---- make the adjustments ----
333 int totalOffset;
334 if( forward ) {
335 // lay out with offsets increasing from 0
336 totalOffset = 0;
337 for (int i = 0; i < spans.length; i++) {
338 offsets[i] = totalOffset;
339 SizeRequirements req = request[i];
340 float play = factor * (req.preferred - req.minimum);
341 spans[i] = (int)(req.preferred - play);
342 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
343 }
344 } else {
345 // lay out with offsets decreasing from the end of the allocation
346 totalOffset = allocated;
347 for (int i = 0; i < spans.length; i++) {
348 SizeRequirements req = request[i];
349 float play = factor * (req.preferred - req.minimum);
350 spans[i] = (int)(req.preferred - play);
351 offsets[i] = totalOffset - spans[i];
352 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
353 }
354 }
355 }
356
357 private static void expandedTile(int allocated, long min, long pref, long max,
358 SizeRequirements[] request,
359 int[] offsets, int[] spans,
360 boolean forward) {
361
362 // ---- determine what we have to work with ----
363 float totalPlay = Math.min(allocated - pref, max - pref);
364 float factor = (max - pref == 0) ? 0.0f : totalPlay / (max - pref);
365
366 // ---- make the adjustments ----
367 int totalOffset;
368 if( forward ) {
369 // lay out with offsets increasing from 0
370 totalOffset = 0;
371 for (int i = 0; i < spans.length; i++) {
372 offsets[i] = totalOffset;
373 SizeRequirements req = request[i];
374 int play = (int)(factor * (req.maximum - req.preferred));
375 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
376 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
377 }
378 } else {
379 // lay out with offsets decreasing from the end of the allocation
380 totalOffset = allocated;
381 for (int i = 0; i < spans.length; i++) {
382 SizeRequirements req = request[i];
383 int play = (int)(factor * (req.maximum - req.preferred));
384 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
385 offsets[i] = totalOffset - spans[i];
386 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
387 }
388 }
389 }
390
391 /**
392 * Creates a bunch of offset/span pairs specifying how to
393 * lay out a set of components with the specified alignments.
394 * The resulting span allocations will overlap, with each one
395 * fitting as well as possible into the given total allocation.
396 * This method requires that you specify
397 * the total amount of space to be allocated,
398 * the size requirements for each component to be placed
399 * (specified as an array of SizeRequirements), and
400 * the total size requirements of the set of components
401 * (only the alignment field of which is actually used).
402 * You can get the total size requirement by invoking
403 * getAlignedSizeRequirements.
404 *
405 * Normal alignment will be done with an alignment value of 0.0f
406 * representing the left/top edge of a component.
407 *
408 * @param allocated the total span to be allocated >= 0.
409 * @param total the total of the children requests.
410 * @param children the size requirements for each component.
411 * @param offsets the offset from 0 for each child where
412 * the spans were allocated (determines placement of the span).
413 * @param spans the span allocated for each child to make the
414 * total target span.
415 */
416 public static void calculateAlignedPositions(int allocated,
417 SizeRequirements total,
418 SizeRequirements[] children,
419 int[] offsets,
420 int[] spans) {
421 calculateAlignedPositions( allocated, total, children, offsets, spans, true );
422 }
423
424 /**
425 * Creates a set of offset/span pairs specifying how to
426 * lay out a set of components with the specified alignments.
427 * The resulting span allocations will overlap, with each one
428 * fitting as well as possible into the given total allocation.
429 * This method requires that you specify
430 * the total amount of space to be allocated,
431 * the size requirements for each component to be placed
432 * (specified as an array of SizeRequirements), and
433 * the total size requirements of the set of components
434 * (only the alignment field of which is actually used)
435 * You can get the total size requirement by invoking
436 * getAlignedSizeRequirements.
437 *
438 * This method also requires a flag indicating whether normal or
439 * reverse alignment should be performed. With normal alignment
440 * the value 0.0f represents the left/top edge of the component
441 * to be aligned. With reverse alignment, 0.0f represents the
442 * right/bottom edge.
443 *
444 * @param allocated the total span to be allocated >= 0.
445 * @param total the total of the children requests.
446 * @param children the size requirements for each component.
447 * @param offsets the offset from 0 for each child where
448 * the spans were allocated (determines placement of the span).
449 * @param spans the span allocated for each child to make the
450 * total target span.
451 * @param normal when true, the alignment value 0.0f means
452 * left/top; when false, it means right/bottom.
453 * @since 1.4
454 */
455 public static void calculateAlignedPositions(int allocated,
456 SizeRequirements total,
457 SizeRequirements[] children,
458 int[] offsets,
459 int[] spans,
460 boolean normal) {
461 float totalAlignment = normal ? total.alignment : 1.0f - total.alignment;
462 int totalAscent = (int)(allocated * totalAlignment);
463 int totalDescent = allocated - totalAscent;
464 for (int i = 0; i < children.length; i++) {
465 SizeRequirements req = children[i];
466 float alignment = normal ? req.alignment : 1.0f - req.alignment;
467 int maxAscent = (int)(req.maximum * alignment);
468 int maxDescent = req.maximum - maxAscent;
469 int ascent = Math.min(totalAscent, maxAscent);
470 int descent = Math.min(totalDescent, maxDescent);
471
472 offsets[i] = totalAscent - ascent;
473 spans[i] = (int) Math.min((long) ascent + (long) descent, Integer.MAX_VALUE);
474 }
475 }
476
477 // This method was used by the JTable - which now uses a different technique.
478 /**
479 * Adjust a specified array of sizes by a given amount.
480 *
481 * @param delta an int specifying the size difference
482 * @param children an array of SizeRequirements objects
483 * @return an array of ints containing the final size for each item
484 */
485 public static int[] adjustSizes(int delta, SizeRequirements[] children) {
486 return new int[0];
487 }
488}