001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing;
026
027 import java.awt.*;
028 import java.io.Serializable;
029
030 /**
031 * For the convenience of layout managers,
032 * calculates information about the size and position of components.
033 * All size and position calculation methods are class methods
034 * that take arrays of SizeRequirements as arguments.
035 * The SizeRequirements class supports two types of layout:
036 *
037 * <blockquote>
038 * <dl>
039 * <dt> tiled
040 * <dd> The components are placed end-to-end,
041 * starting either at coordinate 0 (the leftmost or topmost position)
042 * or at the coordinate representing the end of the allocated span
043 * (the rightmost or bottommost position).
044 *
045 * <dt> aligned
046 * <dd> The components are aligned as specified
047 * by each component's X or Y alignment value.
048 * </dl>
049 * </blockquote>
050 *
051 * <p>
052 *
053 * Each SizeRequirements object contains information
054 * about either the width (and X alignment)
055 * or height (and Y alignment)
056 * of a single component or a group of components:
057 *
058 * <blockquote>
059 * <dl>
060 * <dt> <code>minimum</code>
061 * <dd> The smallest reasonable width/height of the component
062 * or component group, in pixels.
063 *
064 * <dt> <code>preferred</code>
065 * <dd> The natural width/height of the component
066 * or component group, in pixels.
067 *
068 * <dt> <code>maximum</code>
069 * <dd> The largest reasonable width/height of the component
070 * or component group, in pixels.
071 *
072 * <dt> <code>alignment</code>
073 * <dd> The X/Y alignment of the component
074 * or component group.
075 * </dl>
076 * </blockquote>
077 * <p>
078 * <strong>Warning:</strong>
079 * Serialized objects of this class will not be compatible with
080 * future Swing releases. The current serialization support is
081 * appropriate for short term storage or RMI between applications running
082 * the same version of Swing. As of 1.4, support for long term storage
083 * of all JavaBeans<sup><font size="-2">TM</font></sup>
084 * has been added to the <code>java.beans</code> package.
085 * Please see {@link java.beans.XMLEncoder}.
086 *
087 * @see Component#getMinimumSize
088 * @see Component#getPreferredSize
089 * @see Component#getMaximumSize
090 * @see Component#getAlignmentX
091 * @see Component#getAlignmentY
092 *
093 * @version 1.40 05/05/07
094 * @author Timothy Prinzing
095 */
096 public class SizeRequirements implements Serializable {
097
098 /**
099 * 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 + "]@"
165 + alignment;
166 }
167
168 /**
169 * Determines the total space necessary to
170 * place a set of components end-to-end. The needs
171 * of each component in the set are represented by an entry in the
172 * passed-in SizeRequirements array.
173 * The returned SizeRequirements object has an alignment of 0.5
174 * (centered). The space requirement is never more than
175 * Integer.MAX_VALUE.
176 *
177 * @param children the space requirements for a set of components.
178 * The vector may be of zero length, which will result in a
179 * default SizeRequirements object instance being passed back.
180 * @return the total space requirements.
181 */
182 public static SizeRequirements getTiledSizeRequirements(
183 SizeRequirements[] children) {
184 SizeRequirements total = new SizeRequirements();
185 for (int i = 0; i < children.length; i++) {
186 SizeRequirements req = children[i];
187 total.minimum = (int) Math.min((long) total.minimum
188 + (long) req.minimum, Integer.MAX_VALUE);
189 total.preferred = (int) Math.min((long) total.preferred
190 + (long) req.preferred, Integer.MAX_VALUE);
191 total.maximum = (int) Math.min((long) total.maximum
192 + (long) req.maximum, Integer.MAX_VALUE);
193 }
194 return total;
195 }
196
197 /**
198 * Determines the total space necessary to
199 * align a set of components. The needs
200 * of each component in the set are represented by an entry in the
201 * passed-in SizeRequirements array. The total space required will
202 * never be more than Integer.MAX_VALUE.
203 *
204 * @param children the set of child requirements. If of zero length,
205 * the returns result will be a default instance of SizeRequirements.
206 * @return the total space requirements.
207 */
208 public static SizeRequirements getAlignedSizeRequirements(
209 SizeRequirements[] children) {
210 SizeRequirements totalAscent = new SizeRequirements();
211 SizeRequirements totalDescent = new SizeRequirements();
212 for (int i = 0; i < children.length; i++) {
213 SizeRequirements req = children[i];
214
215 int ascent = (int) (req.alignment * req.minimum);
216 int descent = req.minimum - ascent;
217 totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
218 totalDescent.minimum = Math.max(descent,
219 totalDescent.minimum);
220
221 ascent = (int) (req.alignment * req.preferred);
222 descent = req.preferred - ascent;
223 totalAscent.preferred = Math.max(ascent,
224 totalAscent.preferred);
225 totalDescent.preferred = Math.max(descent,
226 totalDescent.preferred);
227
228 ascent = (int) (req.alignment * req.maximum);
229 descent = req.maximum - ascent;
230 totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
231 totalDescent.maximum = Math.max(descent,
232 totalDescent.maximum);
233 }
234 int min = (int) Math.min((long) totalAscent.minimum
235 + (long) totalDescent.minimum, Integer.MAX_VALUE);
236 int pref = (int) Math.min((long) totalAscent.preferred
237 + (long) totalDescent.preferred, Integer.MAX_VALUE);
238 int max = (int) Math.min((long) totalAscent.maximum
239 + (long) totalDescent.maximum, Integer.MAX_VALUE);
240 float alignment = 0.0f;
241 if (min > 0) {
242 alignment = (float) totalAscent.minimum / min;
243 alignment = alignment > 1.0f ? 1.0f
244 : alignment < 0.0f ? 0.0f : alignment;
245 }
246 return new SizeRequirements(min, pref, max, alignment);
247 }
248
249 /**
250 * Creates a set of offset/span pairs representing how to
251 * lay out a set of components end-to-end.
252 * This method requires that you specify
253 * the total amount of space to be allocated,
254 * the size requirements for each component to be placed
255 * (specified as an array of SizeRequirements), and
256 * the total size requirement of the set of components.
257 * You can get the total size requirement
258 * by invoking the getTiledSizeRequirements method. The components
259 * will be tiled in the forward direction with offsets increasing from 0.
260 *
261 * @param allocated the total span to be allocated >= 0.
262 * @param total the total of the children requests. This argument
263 * is optional and may be null.
264 * @param children the size requirements for each component.
265 * @param offsets the offset from 0 for each child where
266 * the spans were allocated (determines placement of the span).
267 * @param spans the span allocated for each child to make the
268 * total target span.
269 */
270 public static void calculateTiledPositions(int allocated,
271 SizeRequirements total, SizeRequirements[] children,
272 int[] offsets, int[] spans) {
273 calculateTiledPositions(allocated, total, children, offsets,
274 spans, true);
275 }
276
277 /**
278 * Creates a set of offset/span pairs representing how to
279 * lay out a set of components end-to-end.
280 * This method requires that you specify
281 * the total amount of space to be allocated,
282 * the size requirements for each component to be placed
283 * (specified as an array of SizeRequirements), and
284 * the total size requirement of the set of components.
285 * You can get the total size requirement
286 * by invoking the getTiledSizeRequirements method.
287 *
288 * This method also requires a flag indicating whether components
289 * should be tiled in the forward direction (offsets increasing
290 * from 0) or reverse direction (offsets decreasing from the end
291 * of the allocated space). The forward direction represents
292 * components tiled from left to right or top to bottom. The
293 * reverse direction represents components tiled from right to left
294 * or bottom to top.
295 *
296 * @param allocated the total span to be allocated >= 0.
297 * @param total the total of the children requests. This argument
298 * is optional and may be null.
299 * @param children the size requirements for each component.
300 * @param offsets the offset from 0 for each child where
301 * the spans were allocated (determines placement of the span).
302 * @param spans the span allocated for each child to make the
303 * total target span.
304 * @param forward tile with offsets increasing from 0 if true
305 * and with offsets decreasing from the end of the allocated space
306 * if false.
307 * @since 1.4
308 */
309 public static void calculateTiledPositions(int allocated,
310 SizeRequirements total, SizeRequirements[] children,
311 int[] offsets, int[] spans, boolean forward) {
312 // The total argument turns out to be a bad idea since the
313 // total of all the children can overflow the integer used to
314 // hold the total. The total must therefore be calculated and
315 // stored in long variables.
316 long min = 0;
317 long pref = 0;
318 long max = 0;
319 for (int i = 0; i < children.length; i++) {
320 min += children[i].minimum;
321 pref += children[i].preferred;
322 max += children[i].maximum;
323 }
324 if (allocated >= pref) {
325 expandedTile(allocated, min, pref, max, children, offsets,
326 spans, forward);
327 } else {
328 compressedTile(allocated, min, pref, max, children,
329 offsets, spans, forward);
330 }
331 }
332
333 private static void compressedTile(int allocated, long min,
334 long pref, long max, SizeRequirements[] request,
335 int[] offsets, int[] spans, boolean forward) {
336
337 // ---- determine what we have to work with ----
338 float totalPlay = Math.min(pref - allocated, pref - min);
339 float factor = (pref - min == 0) ? 0.0f : totalPlay
340 / (pref - min);
341
342 // ---- make the adjustments ----
343 int totalOffset;
344 if (forward) {
345 // lay out with offsets increasing from 0
346 totalOffset = 0;
347 for (int i = 0; i < spans.length; i++) {
348 offsets[i] = totalOffset;
349 SizeRequirements req = request[i];
350 float play = factor * (req.preferred - req.minimum);
351 spans[i] = (int) (req.preferred - play);
352 totalOffset = (int) Math.min((long) totalOffset
353 + (long) spans[i], Integer.MAX_VALUE);
354 }
355 } else {
356 // lay out with offsets decreasing from the end of the allocation
357 totalOffset = allocated;
358 for (int i = 0; i < spans.length; i++) {
359 SizeRequirements req = request[i];
360 float play = factor * (req.preferred - req.minimum);
361 spans[i] = (int) (req.preferred - play);
362 offsets[i] = totalOffset - spans[i];
363 totalOffset = (int) Math.max((long) totalOffset
364 - (long) spans[i], 0);
365 }
366 }
367 }
368
369 private static void expandedTile(int allocated, long min,
370 long pref, long max, SizeRequirements[] request,
371 int[] offsets, int[] spans, boolean forward) {
372
373 // ---- determine what we have to work with ----
374 float totalPlay = Math.min(allocated - pref, max - pref);
375 float factor = (max - pref == 0) ? 0.0f : totalPlay
376 / (max - pref);
377
378 // ---- make the adjustments ----
379 int totalOffset;
380 if (forward) {
381 // lay out with offsets increasing from 0
382 totalOffset = 0;
383 for (int i = 0; i < spans.length; i++) {
384 offsets[i] = totalOffset;
385 SizeRequirements req = request[i];
386 int play = (int) (factor * (req.maximum - req.preferred));
387 spans[i] = (int) Math.min((long) req.preferred
388 + (long) play, Integer.MAX_VALUE);
389 totalOffset = (int) Math.min((long) totalOffset
390 + (long) spans[i], Integer.MAX_VALUE);
391 }
392 } else {
393 // lay out with offsets decreasing from the end of the allocation
394 totalOffset = allocated;
395 for (int i = 0; i < spans.length; i++) {
396 SizeRequirements req = request[i];
397 int play = (int) (factor * (req.maximum - req.preferred));
398 spans[i] = (int) Math.min((long) req.preferred
399 + (long) play, Integer.MAX_VALUE);
400 offsets[i] = totalOffset - spans[i];
401 totalOffset = (int) Math.max((long) totalOffset
402 - (long) spans[i], 0);
403 }
404 }
405 }
406
407 /**
408 * Creates a bunch of offset/span pairs specifying how to
409 * lay out a set of components with the specified alignments.
410 * The resulting span allocations will overlap, with each one
411 * fitting as well as possible into the given total allocation.
412 * This method requires that you specify
413 * the total amount of space to be allocated,
414 * the size requirements for each component to be placed
415 * (specified as an array of SizeRequirements), and
416 * the total size requirements of the set of components
417 * (only the alignment field of which is actually used).
418 * You can get the total size requirement by invoking
419 * getAlignedSizeRequirements.
420 *
421 * Normal alignment will be done with an alignment value of 0.0f
422 * representing the left/top edge of a component.
423 *
424 * @param allocated the total span to be allocated >= 0.
425 * @param total the total of the children requests.
426 * @param children the size requirements for each component.
427 * @param offsets the offset from 0 for each child where
428 * the spans were allocated (determines placement of the span).
429 * @param spans the span allocated for each child to make the
430 * total target span.
431 */
432 public static void calculateAlignedPositions(int allocated,
433 SizeRequirements total, SizeRequirements[] children,
434 int[] offsets, int[] spans) {
435 calculateAlignedPositions(allocated, total, children, offsets,
436 spans, true);
437 }
438
439 /**
440 * Creates a set of offset/span pairs specifying how to
441 * lay out a set of components with the specified alignments.
442 * The resulting span allocations will overlap, with each one
443 * fitting as well as possible into the given total allocation.
444 * This method requires that you specify
445 * the total amount of space to be allocated,
446 * the size requirements for each component to be placed
447 * (specified as an array of SizeRequirements), and
448 * the total size requirements of the set of components
449 * (only the alignment field of which is actually used)
450 * You can get the total size requirement by invoking
451 * getAlignedSizeRequirements.
452 *
453 * This method also requires a flag indicating whether normal or
454 * reverse alignment should be performed. With normal alignment
455 * the value 0.0f represents the left/top edge of the component
456 * to be aligned. With reverse alignment, 0.0f represents the
457 * right/bottom edge.
458 *
459 * @param allocated the total span to be allocated >= 0.
460 * @param total the total of the children requests.
461 * @param children the size requirements for each component.
462 * @param offsets the offset from 0 for each child where
463 * the spans were allocated (determines placement of the span).
464 * @param spans the span allocated for each child to make the
465 * total target span.
466 * @param normal when true, the alignment value 0.0f means
467 * left/top; when false, it means right/bottom.
468 * @since 1.4
469 */
470 public static void calculateAlignedPositions(int allocated,
471 SizeRequirements total, SizeRequirements[] children,
472 int[] offsets, int[] spans, boolean normal) {
473 float totalAlignment = normal ? total.alignment
474 : 1.0f - total.alignment;
475 int totalAscent = (int) (allocated * totalAlignment);
476 int totalDescent = allocated - totalAscent;
477 for (int i = 0; i < children.length; i++) {
478 SizeRequirements req = children[i];
479 float alignment = normal ? req.alignment
480 : 1.0f - req.alignment;
481 int maxAscent = (int) (req.maximum * alignment);
482 int maxDescent = req.maximum - maxAscent;
483 int ascent = Math.min(totalAscent, maxAscent);
484 int descent = Math.min(totalDescent, maxDescent);
485
486 offsets[i] = totalAscent - ascent;
487 spans[i] = (int) Math.min((long) ascent + (long) descent,
488 Integer.MAX_VALUE);
489 }
490 }
491
492 // This method was used by the JTable - which now uses a different technique.
493 /**
494 * Adjust a specified array of sizes by a given amount.
495 *
496 * @param delta an int specifying the size difference
497 * @param children an array of SizeRequirements objects
498 * @return an array of ints containing the final size for each item
499 */
500 public static int[] adjustSizes(int delta,
501 SizeRequirements[] children) {
502 return new int[0];
503 }
504 }
|