1 /* SizeRequirements.java -- 2 Copyright (C) 2002, 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package javax.swing; 39 40 import java.io.Serializable; 41 42 /** 43 * This class calculates information about the size and position requirements 44 * of components. 45 * 46 * Two types of layout are supported: 47 * <ul> 48 * <li>Tiled: the components are placed at position top-left or bottom-right 49 * position within their allocated space</li> 50 * <li>Aligned: the components are placed aligned in their allocated space 51 * according to their alignment value</li> 52 * </ul> 53 * 54 * @author Andrew Selkirk 55 * @author Roman Kennke (roman@kennke.org) 56 */ 57 public class SizeRequirements implements Serializable 58 { 59 /** 60 * The serialVersionUID. 61 */ 62 private static final long serialVersionUID = 9217749429906736553L; 63 64 /** 65 * The minimum reasonable width or height of a component. 66 */ 67 public int minimum; 68 69 /** 70 * The preferred width or height of a component. 71 */ 72 public int preferred; 73 74 /** 75 * The maximum reasonable width or height of a component. 76 */ 77 public int maximum; 78 79 /** 80 * The horizontal or vertical alignment of a component. 81 */ 82 public float alignment; 83 84 /** 85 * Creates a SizeRequirements object with minimum, preferred and 86 * maximum size set to zero, and an alignment value of 0.5. 87 */ SizeRequirements()88 public SizeRequirements() 89 { 90 this (0, 0, 0, 0.5F); 91 } 92 93 /** 94 * Creates a SizeRequirements object with the specified minimum, 95 * preferred, maximum and alignment values. 96 * 97 * @param min the minimum reasonable size of the component 98 * @param pref the preferred size of the component 99 * @param max the maximum size of the component 100 * @param align the alignment of the component 101 */ SizeRequirements(int min, int pref, int max, float align)102 public SizeRequirements(int min, int pref, int max, float align) 103 { 104 minimum = min; 105 preferred = pref; 106 maximum = max; 107 alignment = align; 108 } 109 110 /** 111 * Returns a String representation of this SizeRequirements object, 112 * containing information about the minimum, preferred, maximum and 113 * alignment value. 114 * 115 * @return a String representation of this SizeRequirements object 116 */ toString()117 public String toString() 118 { 119 StringBuilder b = new StringBuilder(); 120 b.append("<["); 121 b.append(minimum); 122 b.append(','); 123 b.append(preferred); 124 b.append(','); 125 b.append(maximum); 126 b.append("]@"); 127 b.append(alignment); 128 b.append('>'); 129 return b.toString(); 130 } 131 132 /** 133 * Calculates how much space is nessecary to place a set of components 134 * end-to-end. The size requirements of the components is specified 135 * in <code>children</code>. 136 * 137 * @param children the SizeRequirements of each of the components 138 * 139 * @return the SizeRequirements that describe how much space is needed 140 * to place the components end-to-end 141 */ 142 public static SizeRequirements getTiledSizeRequirements(SizeRequirements[] children)143 getTiledSizeRequirements(SizeRequirements[] children) 144 { 145 long minimum = 0; 146 long preferred = 0; 147 long maximum = 0; 148 for (int i = 0; i < children.length; i++) 149 { 150 minimum += children[i].minimum; 151 preferred += children[i].preferred; 152 maximum += children[i].maximum; 153 } 154 // Overflow check. 155 if (minimum > Integer.MAX_VALUE) 156 minimum = Integer.MAX_VALUE; 157 if (preferred > Integer.MAX_VALUE) 158 preferred = Integer.MAX_VALUE; 159 if (maximum > Integer.MAX_VALUE) 160 maximum = Integer.MAX_VALUE; 161 SizeRequirements result = new SizeRequirements((int) minimum, 162 (int) preferred, 163 (int) maximum, 164 0.5F); 165 return result; 166 } 167 168 /** 169 * Calculates how much space is nessecary to place a set of components 170 * aligned according to their alignment value. 171 * The size requirements of the components is specified in 172 * <code>children</code>. 173 * 174 * @param children the SizeRequirements of each of the components 175 * 176 * @return the SizeRequirements that describe how much space is needed 177 * to place the components aligned 178 */ 179 public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[] children)180 getAlignedSizeRequirements(SizeRequirements[] children) 181 { 182 float minLeft = 0; 183 float minRight = 0; 184 float prefLeft = 0; 185 float prefRight = 0; 186 float maxLeft = 0; 187 float maxRight = 0; 188 for (int i = 0; i < children.length; i++) 189 { 190 float myMinLeft = children[i].minimum * children[i].alignment; 191 float myMinRight = children[i].minimum - myMinLeft; 192 minLeft = Math.max(myMinLeft, minLeft); 193 minRight = Math.max(myMinRight, minRight); 194 float myPrefLeft = children[i].preferred * children[i].alignment; 195 float myPrefRight = children[i].preferred - myPrefLeft; 196 prefLeft = Math.max(myPrefLeft, prefLeft); 197 prefRight = Math.max(myPrefRight, prefRight); 198 float myMaxLeft = children[i].maximum * children[i].alignment; 199 float myMaxRight = children[i].maximum - myMaxLeft; 200 maxLeft = Math.max(myMaxLeft, maxLeft); 201 maxRight = Math.max(myMaxRight, maxRight); 202 } 203 int minSize = (int) (minLeft + minRight); 204 int prefSize = (int) (prefLeft + prefRight); 205 int maxSize = (int) (maxLeft + maxRight); 206 float align = prefLeft / (prefRight + prefLeft); 207 if (Float.isNaN(align)) 208 align = 0; 209 return new SizeRequirements(minSize, prefSize, maxSize, align); 210 } 211 212 /** 213 * Calculate the offsets and spans of the components, when they should 214 * be placed end-to-end. 215 * 216 * You must specify the amount of allocated space in 217 * <code>allocated</code>, the total size requirements of the set of 218 * components in <code>total</code> (this can be calculated using 219 * {@link #getTiledSizeRequirements} and the size requirements of the 220 * components in <code>children</code>. 221 * 222 * The calculated offset and span values for each component are then 223 * stored in the arrays <code>offsets</code> and <code>spans</code>. 224 * 225 * The components are placed in the forward direction, beginning with 226 * an offset of 0. 227 * 228 * @param allocated the amount of allocated space 229 * @param total the total size requirements of the components 230 * @param children the size requirement of each component 231 * @param offsets will hold the offset values for each component 232 * @param spans will hold the span values for each component 233 */ calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)234 public static void calculateTiledPositions(int allocated, 235 SizeRequirements total, 236 SizeRequirements[] children, 237 int[] offsets, int[] spans) 238 { 239 calculateTiledPositions(allocated, total, children, offsets, spans, true); 240 } 241 242 /** 243 * Calculate the offsets and spans of the components, when they should 244 * be placed end-to-end. 245 * 246 * You must specify the amount of allocated space in 247 * <code>allocated</code>, the total size requirements of the set of 248 * components in <code>total</code> (this can be calculated using 249 * {@link #getTiledSizeRequirements} and the size requirements of the 250 * components in <code>children</code>. 251 * 252 * The calculated offset and span values for each component are then 253 * stored in the arrays <code>offsets</code> and <code>spans</code>. 254 * 255 * Depending on the value of <code>forward</code> the components are 256 * placed in the forward direction (left-right or top-bottom), where 257 * the offsets begin with 0, or in the reverse direction 258 * (right-left or bottom-top). 259 * 260 * @param allocated the amount of allocated space 261 * @param total the total size requirements of the components 262 * @param children the size requirement of each component 263 * @param offsets will hold the offset values for each component 264 * @param spans will hold the span values for each component 265 * @param forward whether the components should be placed in the forward 266 * direction (left-right or top-bottom) or reverse direction 267 * (right-left or bottom-top) 268 */ calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans, boolean forward)269 public static void calculateTiledPositions(int allocated, 270 SizeRequirements total, 271 SizeRequirements[] children, 272 int[] offsets, int[] spans, 273 boolean forward) 274 { 275 int span = 0; 276 if (forward) 277 { 278 int offset = 0; 279 for (int i = 0; i < children.length; i++) 280 { 281 offsets[i] = offset; 282 spans[i] = children[i].preferred; 283 span += spans[i]; 284 offset += children[i].preferred; 285 } 286 } 287 else 288 { 289 int offset = allocated; 290 for (int i = 0; i < children.length; i++) 291 { 292 offset -= children[i].preferred; 293 offsets[i] = offset; 294 span += spans[i]; 295 spans[i] = children[i].preferred; 296 } 297 } 298 // Adjust spans so that we exactly fill the allocated region. If 299 if (span > allocated) 300 adjustSmaller(allocated, children, spans, span); 301 else if (span < allocated) 302 adjustGreater(allocated, children, spans, span); 303 304 // Adjust offsets. 305 if (forward) 306 { 307 int offset = 0; 308 for (int i = 0; i < children.length; i++) 309 { 310 offsets[i] = offset; 311 offset += spans[i]; 312 } 313 } 314 else 315 { 316 int offset = allocated; 317 for (int i = 0; i < children.length; i++) 318 { 319 offset -= spans[i]; 320 offsets[i] = offset; 321 } 322 } 323 } 324 adjustSmaller(int allocated, SizeRequirements[] children, int[] spans, int span)325 private static void adjustSmaller(int allocated, SizeRequirements[] children, 326 int[] spans, int span) 327 { 328 // Sum up (prefSize - minSize) over all children 329 int sumDelta = 0; 330 for (int i = 0; i < children.length; i++) 331 sumDelta += children[i].preferred - children[i].minimum; 332 333 // If we have sumDelta == 0, then all components have prefSize == maxSize 334 // and we can't do anything about it. 335 if (sumDelta == 0) 336 return; 337 338 // Adjust all sizes according to their preferred and minimum sizes. 339 for (int i = 0; i < children.length; i++) 340 { 341 double factor = ((double) (children[i].preferred - children[i].minimum)) 342 / ((double) sumDelta); 343 // In case we have a sumDelta of 0, the factor should also be 0. 344 if (Double.isNaN(factor)) 345 factor = 0; 346 spans[i] -= factor * (span - allocated); 347 } 348 } 349 adjustGreater(int allocated, SizeRequirements[] children, int[] spans, int span)350 private static void adjustGreater(int allocated, SizeRequirements[] children, 351 int[] spans, int span) 352 { 353 // Sum up (maxSize - prefSize) over all children 354 long sumDelta = 0; 355 for (int i = 0; i < children.length; i++) 356 { 357 sumDelta += children[i].maximum - children[i].preferred; 358 } 359 360 // If we have sumDelta == 0, then all components have prefSize == maxSize 361 // and we can't do anything about it. 362 if (sumDelta == 0) 363 return; 364 365 // Adjust all sizes according to their preferred and minimum sizes. 366 for (int i = 0; i < children.length; i++) 367 { 368 double factor = ((double) (children[i].maximum - children[i].preferred)) 369 / ((double) sumDelta); 370 spans[i] += factor * (allocated - span); 371 } 372 } 373 374 /** 375 * Calculate the offsets and spans of the components, when they should 376 * be placed end-to-end. 377 * 378 * You must specify the amount of allocated space in 379 * <code>allocated</code>, the total size requirements of the set of 380 * components in <code>total</code> (this can be calculated using 381 * {@link #getTiledSizeRequirements} and the size requirements of the 382 * components in <code>children</code>. 383 * 384 * The calculated offset and span values for each component are then 385 * stored in the arrays <code>offsets</code> and <code>spans</code>. 386 * 387 * The components are tiled in the forward direction, beginning with 388 * an offset of 0. 389 * 390 * @param allocated the amount of allocated space 391 * @param total the total size requirements of the components 392 * @param children the size requirement of each component 393 * @param offsets will hold the offset values for each component 394 * @param spans will hold the span values for each component 395 */ calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)396 public static void calculateAlignedPositions(int allocated, 397 SizeRequirements total, 398 SizeRequirements[] children, 399 int[] offsets, int[] spans) 400 { 401 calculateAlignedPositions(allocated, total, children, offsets, spans, 402 true); 403 } 404 405 /** 406 * Calculate the offsets and spans of the components, when they should 407 * be placed end-to-end. 408 * 409 * You must specify the amount of allocated space in 410 * <code>allocated</code>, the total size requirements of the set of 411 * components in <code>total</code> (this can be calculated using 412 * {@link #getTiledSizeRequirements} and the size requirements of the 413 * components in <code>children</code>. 414 * 415 * The calculated offset and span values for each component are then 416 * stored in the arrays <code>offsets</code> and <code>spans</code>. 417 * 418 * Depending on the value of <code>forward</code> the components are 419 * placed in the forward direction (left-right or top-bottom), where 420 * the offsets begin with 0, or in the reverse direction 421 * (right-left or bottom-top). 422 * 423 * @param allocated the amount of allocated space 424 * @param total the total size requirements of the components 425 * @param children the size requirement of each component 426 * @param spans will hold the span values for each component 427 * @param forward whether the components should be placed in the forward 428 * direction (left-right or top-bottom) or reverse direction 429 * (right-left or bottom-top) 430 */ calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offset, int[] spans, boolean forward)431 public static void calculateAlignedPositions(int allocated, 432 SizeRequirements total, 433 SizeRequirements[] children, 434 int[] offset, int[] spans, 435 boolean forward) 436 { 437 // First we compute the position of the baseline. 438 float baseline = allocated * total.alignment; 439 440 // Now we can layout the components along the baseline. 441 for (int i = 0; i < children.length; i++) 442 { 443 float align = children[i].alignment; 444 // Try to fit the component into the available space. 445 int[] spanAndOffset = new int[2]; 446 if (align < .5F || baseline == 0) 447 adjustFromRight(children[i], baseline, allocated, spanAndOffset); 448 else 449 adjustFromLeft(children[i], baseline, allocated, spanAndOffset); 450 spans[i] = spanAndOffset[0]; 451 offset[i] = spanAndOffset[1]; 452 } 453 } 454 455 /** 456 * Adjusts the span and offset of a component for the aligned layout. 457 * 458 * @param reqs 459 * @param baseline 460 * @param allocated 461 * @param spanAndOffset 462 */ adjustFromRight(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)463 private static void adjustFromRight(SizeRequirements reqs, float baseline, 464 int allocated, int[] spanAndOffset) 465 { 466 float right = allocated - baseline; 467 // If the resulting span exceeds the maximum of the component, then adjust 468 // accordingly. 469 float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment); 470 if (right / (1.F - reqs.alignment) > reqs.maximum) 471 right = maxRight; 472 // If we have not enough space on the left side, then adjust accordingly. 473 if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline) 474 right = ((float) (allocated - baseline)) 475 / reqs.alignment * (1.F - reqs.alignment); 476 477 spanAndOffset[0] = (int) (right / (1.F - reqs.alignment)); 478 spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment); 479 } 480 481 /** 482 * Adjusts the span and offset of a component for the aligned layout. 483 * 484 * @param reqs 485 * @param baseline 486 * @param allocated 487 * @param spanAndOffset 488 */ adjustFromLeft(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)489 private static void adjustFromLeft(SizeRequirements reqs, float baseline, 490 int allocated, int[] spanAndOffset) 491 { 492 float left = baseline; 493 // If the resulting span exceeds the maximum of the component, then adjust 494 // accordingly. 495 float maxLeft = ((float) reqs.maximum) * reqs.alignment; 496 if (left / reqs.alignment > reqs.maximum) 497 left = maxLeft; 498 // If we have not enough space on the right side, then adjust accordingly. 499 if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline) 500 left = ((float) (allocated - baseline)) 501 / (1.F - reqs.alignment) * reqs.alignment; 502 503 spanAndOffset[0] = (int) (left / reqs.alignment); 504 spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment); 505 } 506 507 /** 508 * Returns an array of new preferred sizes for the children based on 509 * <code>delta</code>. <code>delta</code> specifies a change in the 510 * allocated space. The sizes of the children will be shortened or 511 * lengthened to accomodate the new allocation. 512 * 513 * @param delta the change of the size of the total allocation for 514 * the components 515 * @param children the size requirements of each component 516 * 517 * @return the new preferred sizes for each component 518 */ adjustSizes(int delta, SizeRequirements[] children)519 public static int[] adjustSizes(int delta, SizeRequirements[] children) 520 { 521 return null; // TODO 522 } 523 } 524