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