1 /* 2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 28 import java.awt.*; 29 import 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™ 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 */ 96 public 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 }