1 /* 2 * Copyright (c) 1997, 2014, 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 * @since 1.2 96 */ 97 @SuppressWarnings("serial") // Same-version serialization only 98 public class SizeRequirements implements Serializable { 99 100 /** 101 * The minimum size required. 102 * For a component <code>comp</code>, this should be equal to either 103 * <code>comp.getMinimumSize().width</code> or 104 * <code>comp.getMinimumSize().height</code>. 105 */ 106 public int minimum; 107 108 /** 109 * The preferred (natural) size. 110 * For a component <code>comp</code>, this should be equal to either 111 * <code>comp.getPreferredSize().width</code> or 112 * <code>comp.getPreferredSize().height</code>. 113 */ 114 public int preferred; 115 116 /** 117 * The maximum size allowed. 118 * For a component <code>comp</code>, this should be equal to either 119 * <code>comp.getMaximumSize().width</code> or 120 * <code>comp.getMaximumSize().height</code>. 121 */ 122 public int maximum; 123 124 /** 125 * The alignment, specified as a value between 0.0 and 1.0, 126 * inclusive. 127 * To specify centering, the alignment should be 0.5. 128 */ 129 public float alignment; 130 131 /** 132 * Creates a SizeRequirements object with the minimum, preferred, 133 * and maximum sizes set to zero and an alignment value of 0.5 134 * (centered). 135 */ 136 public SizeRequirements() { 137 minimum = 0; 138 preferred = 0; 139 maximum = 0; 140 alignment = 0.5f; 141 } 142 143 /** 144 * Creates a SizeRequirements object with the specified minimum, preferred, 145 * and maximum sizes and the specified alignment. 146 * 147 * @param min the minimum size >= 0 148 * @param pref the preferred size >= 0 149 * @param max the maximum size >= 0 150 * @param a the alignment >= 0.0f && <= 1.0f 151 */ 152 public SizeRequirements(int min, int pref, int max, float a) { 153 minimum = min; 154 preferred = pref; 155 maximum = max; 156 alignment = a > 1.0f ? 1.0f : a < 0.0f ? 0.0f : a; 157 } 158 159 /** 160 * Returns a string describing the minimum, preferred, and maximum 161 * size requirements, along with the alignment. 162 * 163 * @return the string 164 */ 165 public String toString() { 166 return "[" + minimum + "," + preferred + "," + maximum + "]@" + alignment; 167 } 168 169 /** 170 * Determines the total space necessary to 171 * place a set of components end-to-end. The needs 172 * of each component in the set are represented by an entry in the 173 * passed-in SizeRequirements array. 174 * The returned SizeRequirements object has an alignment of 0.5 175 * (centered). The space requirement is never more than 176 * Integer.MAX_VALUE. 177 * 178 * @param children the space requirements for a set of components. 179 * The vector may be of zero length, which will result in a 180 * default SizeRequirements object instance being passed back. 181 * @return the total space requirements. 182 */ 183 public static SizeRequirements getTiledSizeRequirements(SizeRequirements[] 184 children) { 185 SizeRequirements total = new SizeRequirements(); 186 for (int i = 0; i < children.length; i++) { 187 SizeRequirements req = children[i]; 188 total.minimum = (int) Math.min((long) total.minimum + (long) req.minimum, Integer.MAX_VALUE); 189 total.preferred = (int) Math.min((long) total.preferred + (long) req.preferred, Integer.MAX_VALUE); 190 total.maximum = (int) Math.min((long) total.maximum + (long) req.maximum, Integer.MAX_VALUE); 191 } 192 return total; 193 } 194 195 /** 196 * Determines the total space necessary to 197 * align a set of components. The needs 198 * of each component in the set are represented by an entry in the 199 * passed-in SizeRequirements array. The total space required will 200 * never be more than Integer.MAX_VALUE. 201 * 202 * @param children the set of child requirements. If of zero length, 203 * the returns result will be a default instance of SizeRequirements. 204 * @return the total space requirements. 205 */ 206 public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[] 207 children) { 208 SizeRequirements totalAscent = new SizeRequirements(); 209 SizeRequirements totalDescent = new SizeRequirements(); 210 for (int i = 0; i < children.length; i++) { 211 SizeRequirements req = children[i]; 212 213 int ascent = (int) (req.alignment * req.minimum); 214 int descent = req.minimum - ascent; 215 totalAscent.minimum = Math.max(ascent, totalAscent.minimum); 216 totalDescent.minimum = Math.max(descent, totalDescent.minimum); 217 218 ascent = (int) (req.alignment * req.preferred); 219 descent = req.preferred - ascent; 220 totalAscent.preferred = Math.max(ascent, totalAscent.preferred); 221 totalDescent.preferred = Math.max(descent, totalDescent.preferred); 222 223 ascent = (int) (req.alignment * req.maximum); 224 descent = req.maximum - ascent; 225 totalAscent.maximum = Math.max(ascent, totalAscent.maximum); 226 totalDescent.maximum = Math.max(descent, totalDescent.maximum); 227 } 228 int min = (int) Math.min((long) totalAscent.minimum + (long) totalDescent.minimum, Integer.MAX_VALUE); 229 int pref = (int) Math.min((long) totalAscent.preferred + (long) totalDescent.preferred, Integer.MAX_VALUE); 230 int max = (int) Math.min((long) totalAscent.maximum + (long) totalDescent.maximum, Integer.MAX_VALUE); 231 float alignment = 0.0f; 232 if (min > 0) { 233 alignment = (float) totalAscent.minimum / min; 234 alignment = alignment > 1.0f ? 1.0f : alignment < 0.0f ? 0.0f : alignment; 235 } 236 return new SizeRequirements(min, pref, max, alignment); 237 } 238 239 /** 240 * Creates a set of offset/span pairs representing how to 241 * lay out a set of components end-to-end. 242 * This method requires that you specify 243 * the total amount of space to be allocated, 244 * the size requirements for each component to be placed 245 * (specified as an array of SizeRequirements), and 246 * the total size requirement of the set of components. 247 * You can get the total size requirement 248 * by invoking the getTiledSizeRequirements method. The components 249 * will be tiled in the forward direction with offsets increasing from 0. 250 * 251 * @param allocated the total span to be allocated >= 0. 252 * @param total the total of the children requests. This argument 253 * is optional and may be null. 254 * @param children the size requirements for each component. 255 * @param offsets the offset from 0 for each child where 256 * the spans were allocated (determines placement of the span). 257 * @param spans the span allocated for each child to make the 258 * total target span. 259 */ 260 public static void calculateTiledPositions(int allocated, 261 SizeRequirements total, 262 SizeRequirements[] children, 263 int[] offsets, 264 int[] spans) { 265 calculateTiledPositions(allocated, total, children, offsets, spans, true); 266 } 267 268 /** 269 * Creates a set of offset/span pairs representing how to 270 * lay out a set of components end-to-end. 271 * This method requires that you specify 272 * the total amount of space to be allocated, 273 * the size requirements for each component to be placed 274 * (specified as an array of SizeRequirements), and 275 * the total size requirement of the set of components. 276 * You can get the total size requirement 277 * by invoking the getTiledSizeRequirements method. 278 * 279 * This method also requires a flag indicating whether components 280 * should be tiled in the forward direction (offsets increasing 281 * from 0) or reverse direction (offsets decreasing from the end 282 * of the allocated space). The forward direction represents 283 * components tiled from left to right or top to bottom. The 284 * reverse direction represents components tiled from right to left 285 * or bottom to top. 286 * 287 * @param allocated the total span to be allocated >= 0. 288 * @param total the total of the children requests. This argument 289 * is optional and may be null. 290 * @param children the size requirements for each component. 291 * @param offsets the offset from 0 for each child where 292 * the spans were allocated (determines placement of the span). 293 * @param spans the span allocated for each child to make the 294 * total target span. 295 * @param forward tile with offsets increasing from 0 if true 296 * and with offsets decreasing from the end of the allocated space 297 * if false. 298 * @since 1.4 299 */ 300 public static void calculateTiledPositions(int allocated, 301 SizeRequirements total, 302 SizeRequirements[] children, 303 int[] offsets, 304 int[] spans, 305 boolean forward) { 306 // The total argument turns out to be a bad idea since the 307 // total of all the children can overflow the integer used to 308 // hold the total. The total must therefore be calculated and 309 // stored in long variables. 310 long min = 0; 311 long pref = 0; 312 long max = 0; 313 for (int i = 0; i < children.length; i++) { 314 min += children[i].minimum; 315 pref += children[i].preferred; 316 max += children[i].maximum; 317 } 318 if (allocated >= pref) { 319 expandedTile(allocated, min, pref, max, children, offsets, spans, forward); 320 } else { 321 compressedTile(allocated, min, pref, max, children, offsets, spans, forward); 322 } 323 } 324 325 private static void compressedTile(int allocated, long min, long pref, long max, 326 SizeRequirements[] request, 327 int[] offsets, int[] spans, 328 boolean forward) { 329 330 // ---- determine what we have to work with ---- 331 float totalPlay = Math.min(pref - allocated, pref - min); 332 float factor = (pref - min == 0) ? 0.0f : totalPlay / (pref - min); 333 334 // ---- make the adjustments ---- 335 int totalOffset; 336 if( forward ) { 337 // lay out with offsets increasing from 0 338 totalOffset = 0; 339 for (int i = 0; i < spans.length; i++) { 340 offsets[i] = totalOffset; 341 SizeRequirements req = request[i]; 342 float play = factor * (req.preferred - req.minimum); 343 spans[i] = (int)(req.preferred - play); 344 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE); 345 } 346 } else { 347 // lay out with offsets decreasing from the end of the allocation 348 totalOffset = allocated; 349 for (int i = 0; i < spans.length; i++) { 350 SizeRequirements req = request[i]; 351 float play = factor * (req.preferred - req.minimum); 352 spans[i] = (int)(req.preferred - play); 353 offsets[i] = totalOffset - spans[i]; 354 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0); 355 } 356 } 357 } 358 359 private static void expandedTile(int allocated, long min, long pref, long max, 360 SizeRequirements[] request, 361 int[] offsets, int[] spans, 362 boolean forward) { 363 364 // ---- determine what we have to work with ---- 365 float totalPlay = Math.min(allocated - pref, max - pref); 366 float factor = (max - pref == 0) ? 0.0f : totalPlay / (max - pref); 367 368 // ---- make the adjustments ---- 369 int totalOffset; 370 if( forward ) { 371 // lay out with offsets increasing from 0 372 totalOffset = 0; 373 for (int i = 0; i < spans.length; i++) { 374 offsets[i] = totalOffset; 375 SizeRequirements req = request[i]; 376 int play = (int)(factor * (req.maximum - req.preferred)); 377 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE); 378 totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE); 379 } 380 } else { 381 // lay out with offsets decreasing from the end of the allocation 382 totalOffset = allocated; 383 for (int i = 0; i < spans.length; i++) { 384 SizeRequirements req = request[i]; 385 int play = (int)(factor * (req.maximum - req.preferred)); 386 spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE); 387 offsets[i] = totalOffset - spans[i]; 388 totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0); 389 } 390 } 391 } 392 393 /** 394 * Creates a bunch of offset/span pairs specifying how to 395 * lay out a set of components with the specified alignments. 396 * The resulting span allocations will overlap, with each one 397 * fitting as well as possible into the given total allocation. 398 * This method requires that you specify 399 * the total amount of space to be allocated, 400 * the size requirements for each component to be placed 401 * (specified as an array of SizeRequirements), and 402 * the total size requirements of the set of components 403 * (only the alignment field of which is actually used). 404 * You can get the total size requirement by invoking 405 * getAlignedSizeRequirements. 406 * 407 * Normal alignment will be done with an alignment value of 0.0f 408 * representing the left/top edge of a component. 409 * 410 * @param allocated the total span to be allocated >= 0. 411 * @param total the total of the children requests. 412 * @param children the size requirements for each component. 413 * @param offsets the offset from 0 for each child where 414 * the spans were allocated (determines placement of the span). 415 * @param spans the span allocated for each child to make the 416 * total target span. 417 */ 418 public static void calculateAlignedPositions(int allocated, 419 SizeRequirements total, 420 SizeRequirements[] children, 421 int[] offsets, 422 int[] spans) { 423 calculateAlignedPositions( allocated, total, children, offsets, spans, true ); 424 } 425 426 /** 427 * Creates a set of offset/span pairs specifying how to 428 * lay out a set of components with the specified alignments. 429 * The resulting span allocations will overlap, with each one 430 * fitting as well as possible into the given total allocation. 431 * This method requires that you specify 432 * the total amount of space to be allocated, 433 * the size requirements for each component to be placed 434 * (specified as an array of SizeRequirements), and 435 * the total size requirements of the set of components 436 * (only the alignment field of which is actually used) 437 * You can get the total size requirement by invoking 438 * getAlignedSizeRequirements. 439 * 440 * This method also requires a flag indicating whether normal or 441 * reverse alignment should be performed. With normal alignment 442 * the value 0.0f represents the left/top edge of the component 443 * to be aligned. With reverse alignment, 0.0f represents the 444 * right/bottom edge. 445 * 446 * @param allocated the total span to be allocated >= 0. 447 * @param total the total of the children requests. 448 * @param children the size requirements for each component. 449 * @param offsets the offset from 0 for each child where 450 * the spans were allocated (determines placement of the span). 451 * @param spans the span allocated for each child to make the 452 * total target span. 453 * @param normal when true, the alignment value 0.0f means 454 * left/top; when false, it means right/bottom. 455 * @since 1.4 456 */ 457 public static void calculateAlignedPositions(int allocated, 458 SizeRequirements total, 459 SizeRequirements[] children, 460 int[] offsets, 461 int[] spans, 462 boolean normal) { 463 float totalAlignment = normal ? total.alignment : 1.0f - total.alignment; 464 int totalAscent = (int)(allocated * totalAlignment); 465 int totalDescent = allocated - totalAscent; 466 for (int i = 0; i < children.length; i++) { 467 SizeRequirements req = children[i]; 468 float alignment = normal ? req.alignment : 1.0f - req.alignment; 469 int maxAscent = (int)(req.maximum * alignment); 470 int maxDescent = req.maximum - maxAscent; 471 int ascent = Math.min(totalAscent, maxAscent); 472 int descent = Math.min(totalDescent, maxDescent); 473 474 offsets[i] = totalAscent - ascent; 475 spans[i] = (int) Math.min((long) ascent + (long) descent, Integer.MAX_VALUE); 476 } 477 } 478 479 // This method was used by the JTable - which now uses a different technique. 480 /** 481 * Adjust a specified array of sizes by a given amount. 482 * 483 * @param delta an int specifying the size difference 484 * @param children an array of SizeRequirements objects 485 * @return an array of ints containing the final size for each item 486 */ 487 public static int[] adjustSizes(int delta, SizeRequirements[] children) { 488 return new int[0]; 489 } 490 }