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 SizeRequirements result = new SizeRequirements(); 146 for (int i = 0; i < children.length; i++) 147 { 148 result.minimum += children[i].minimum; 149 result.preferred += children[i].preferred; 150 result.maximum += children[i].maximum; 151 } 152 return result; 153 } 154 155 /** 156 * Calculates how much space is nessecary to place a set of components 157 * aligned according to their alignment value. 158 * The size requirements of the components is specified in 159 * <code>children</code>. 160 * 161 * @param children the SizeRequirements of each of the components 162 * 163 * @return the SizeRequirements that describe how much space is needed 164 * to place the components aligned 165 */ 166 public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[] children)167 getAlignedSizeRequirements(SizeRequirements[] children) 168 { 169 float minLeft = 0; 170 float minRight = 0; 171 float prefLeft = 0; 172 float prefRight = 0; 173 float maxLeft = 0; 174 float maxRight = 0; 175 for (int i = 0; i < children.length; i++) 176 { 177 float myMinLeft = children[i].minimum * children[i].alignment; 178 float myMinRight = children[i].minimum - myMinLeft; 179 minLeft = Math.max(myMinLeft, minLeft); 180 minRight = Math.max(myMinRight, minRight); 181 float myPrefLeft = children[i].preferred * children[i].alignment; 182 float myPrefRight = children[i].preferred - myPrefLeft; 183 prefLeft = Math.max(myPrefLeft, prefLeft); 184 prefRight = Math.max(myPrefRight, prefRight); 185 float myMaxLeft = children[i].maximum * children[i].alignment; 186 float myMaxRight = children[i].maximum - myMaxLeft; 187 maxLeft = Math.max(myMaxLeft, maxLeft); 188 maxRight = Math.max(myMaxRight, maxRight); 189 } 190 int minSize = (int) (minLeft + minRight); 191 int prefSize = (int) (prefLeft + prefRight); 192 int maxSize = (int) (maxLeft + maxRight); 193 float align = prefLeft / (prefRight + prefLeft); 194 if (Float.isNaN(align)) 195 align = 0; 196 return new SizeRequirements(minSize, prefSize, maxSize, align); 197 } 198 199 /** 200 * Calculate the offsets and spans of the components, when they should 201 * be placed end-to-end. 202 * 203 * You must specify the amount of allocated space in 204 * <code>allocated</code>, the total size requirements of the set of 205 * components in <code>total</code> (this can be calculated using 206 * {@link #getTiledSizeRequirements} and the size requirements of the 207 * components in <code>children</code>. 208 * 209 * The calculated offset and span values for each component are then 210 * stored in the arrays <code>offsets</code> and <code>spans</code>. 211 * 212 * The components are placed in the forward direction, beginning with 213 * an offset of 0. 214 * 215 * @param allocated the amount of allocated space 216 * @param total the total size requirements of the components 217 * @param children the size requirement of each component 218 * @param offsets will hold the offset values for each component 219 * @param spans will hold the span values for each component 220 */ calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)221 public static void calculateTiledPositions(int allocated, 222 SizeRequirements total, 223 SizeRequirements[] children, 224 int[] offsets, int[] spans) 225 { 226 calculateTiledPositions(allocated, total, children, offsets, spans, true); 227 } 228 229 /** 230 * Calculate the offsets and spans of the components, when they should 231 * be placed end-to-end. 232 * 233 * You must specify the amount of allocated space in 234 * <code>allocated</code>, the total size requirements of the set of 235 * components in <code>total</code> (this can be calculated using 236 * {@link #getTiledSizeRequirements} and the size requirements of the 237 * components in <code>children</code>. 238 * 239 * The calculated offset and span values for each component are then 240 * stored in the arrays <code>offsets</code> and <code>spans</code>. 241 * 242 * Depending on the value of <code>forward</code> the components are 243 * placed in the forward direction (left-right or top-bottom), where 244 * the offsets begin with 0, or in the reverse direction 245 * (right-left or bottom-top). 246 * 247 * @param allocated the amount of allocated space 248 * @param total the total size requirements of the components 249 * @param children the size requirement of each component 250 * @param offsets will hold the offset values for each component 251 * @param spans will hold the span values for each component 252 * @param forward whether the components should be placed in the forward 253 * direction (left-right or top-bottom) or reverse direction 254 * (right-left or bottom-top) 255 */ calculateTiledPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans, boolean forward)256 public static void calculateTiledPositions(int allocated, 257 SizeRequirements total, 258 SizeRequirements[] children, 259 int[] offsets, int[] spans, 260 boolean forward) 261 { 262 int span = 0; 263 if (forward) 264 { 265 int offset = 0; 266 for (int i = 0; i < children.length; i++) 267 { 268 offsets[i] = offset; 269 spans[i] = children[i].preferred; 270 span += spans[i]; 271 offset += children[i].preferred; 272 } 273 } 274 else 275 { 276 int offset = allocated; 277 for (int i = 0; i < children.length; i++) 278 { 279 offset -= children[i].preferred; 280 offsets[i] = offset; 281 span += spans[i]; 282 spans[i] = children[i].preferred; 283 } 284 } 285 // Adjust spans so that we exactly fill the allocated region. If 286 if (span > allocated) 287 adjustSmaller(allocated, children, spans, span); 288 else if (span < allocated) 289 adjustGreater(allocated, children, spans, span); 290 291 // Adjust offsets. 292 if (forward) 293 { 294 int offset = 0; 295 for (int i = 0; i < children.length; i++) 296 { 297 offsets[i] = offset; 298 offset += spans[i]; 299 } 300 } 301 else 302 { 303 int offset = allocated; 304 for (int i = 0; i < children.length; i++) 305 { 306 offset -= spans[i]; 307 offsets[i] = offset; 308 } 309 } 310 } 311 adjustSmaller(int allocated, SizeRequirements[] children, int[] spans, int span)312 private static void adjustSmaller(int allocated, SizeRequirements[] children, 313 int[] spans, int span) 314 { 315 // Sum up (prefSize - minSize) over all children 316 int sumDelta = 0; 317 for (int i = 0; i < children.length; i++) 318 sumDelta += children[i].preferred - children[i].minimum; 319 320 // If we have sumDelta == 0, then all components have prefSize == maxSize 321 // and we can't do anything about it. 322 if (sumDelta == 0) 323 return; 324 325 // Adjust all sizes according to their preferred and minimum sizes. 326 for (int i = 0; i < children.length; i++) 327 { 328 double factor = ((double) (children[i].preferred - children[i].minimum)) 329 / ((double) sumDelta); 330 // In case we have a sumDelta of 0, the factor should also be 0. 331 if (Double.isNaN(factor)) 332 factor = 0; 333 spans[i] -= factor * (span - allocated); 334 } 335 } 336 adjustGreater(int allocated, SizeRequirements[] children, int[] spans, int span)337 private static void adjustGreater(int allocated, SizeRequirements[] children, 338 int[] spans, int span) 339 { 340 // Sum up (maxSize - prefSize) over all children 341 int sumDelta = 0; 342 for (int i = 0; i < children.length; i++) 343 { 344 sumDelta += children[i].maximum - children[i].preferred; 345 if (sumDelta < 0) 346 sumDelta = Integer.MAX_VALUE; 347 } 348 349 // If we have sumDelta == 0, then all components have prefSize == maxSize 350 // and we can't do anything about it. 351 if (sumDelta == 0) 352 return; 353 354 // Adjust all sizes according to their preferred and minimum sizes. 355 for (int i = 0; i < children.length; i++) 356 { 357 double factor = ((double) (children[i].maximum - children[i].preferred)) 358 / ((double) sumDelta); 359 spans[i] -= factor * (span - allocated); 360 } 361 } 362 363 /** 364 * Calculate the offsets and spans of the components, when they should 365 * be placed end-to-end. 366 * 367 * You must specify the amount of allocated space in 368 * <code>allocated</code>, the total size requirements of the set of 369 * components in <code>total</code> (this can be calculated using 370 * {@link #getTiledSizeRequirements} and the size requirements of the 371 * components in <code>children</code>. 372 * 373 * The calculated offset and span values for each component are then 374 * stored in the arrays <code>offsets</code> and <code>spans</code>. 375 * 376 * The components are tiled in the forward direction, beginning with 377 * an offset of 0. 378 * 379 * @param allocated the amount of allocated space 380 * @param total the total size requirements of the components 381 * @param children the size requirement of each component 382 * @param offsets will hold the offset values for each component 383 * @param spans will hold the span values for each component 384 */ calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offsets, int[] spans)385 public static void calculateAlignedPositions(int allocated, 386 SizeRequirements total, 387 SizeRequirements[] children, 388 int[] offsets, int[] spans) 389 { 390 calculateAlignedPositions(allocated, total, children, offsets, spans, 391 true); 392 } 393 394 /** 395 * Calculate the offsets and spans of the components, when they should 396 * be placed end-to-end. 397 * 398 * You must specify the amount of allocated space in 399 * <code>allocated</code>, the total size requirements of the set of 400 * components in <code>total</code> (this can be calculated using 401 * {@link #getTiledSizeRequirements} and the size requirements of the 402 * components in <code>children</code>. 403 * 404 * The calculated offset and span values for each component are then 405 * stored in the arrays <code>offsets</code> and <code>spans</code>. 406 * 407 * Depending on the value of <code>forward</code> the components are 408 * placed in the forward direction (left-right or top-bottom), where 409 * the offsets begin with 0, or in the reverse direction 410 * (right-left or bottom-top). 411 * 412 * @param allocated the amount of allocated space 413 * @param total the total size requirements of the components 414 * @param children the size requirement of each component 415 * @param spans will hold the span values for each component 416 * @param forward whether the components should be placed in the forward 417 * direction (left-right or top-bottom) or reverse direction 418 * (right-left or bottom-top) 419 */ calculateAlignedPositions(int allocated, SizeRequirements total, SizeRequirements[] children, int[] offset, int[] spans, boolean forward)420 public static void calculateAlignedPositions(int allocated, 421 SizeRequirements total, 422 SizeRequirements[] children, 423 int[] offset, int[] spans, 424 boolean forward) 425 { 426 // First we compute the position of the baseline. 427 float baseline = allocated * total.alignment; 428 429 // Now we can layout the components along the baseline. 430 for (int i = 0; i < children.length; i++) 431 { 432 float align = children[i].alignment; 433 // Try to fit the component into the available space. 434 int[] spanAndOffset = new int[2]; 435 if (align < .5F || baseline == 0) 436 adjustFromRight(children[i], baseline, allocated, spanAndOffset); 437 else 438 adjustFromLeft(children[i], baseline, allocated, spanAndOffset); 439 spans[i] = spanAndOffset[0]; 440 offset[i] = spanAndOffset[1]; 441 } 442 } 443 444 /** 445 * Adjusts the span and offset of a component for the aligned layout. 446 * 447 * @param reqs 448 * @param baseline 449 * @param allocated 450 * @param spanAndOffset 451 */ adjustFromRight(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)452 private static void adjustFromRight(SizeRequirements reqs, float baseline, 453 int allocated, int[] spanAndOffset) 454 { 455 float right = allocated - baseline; 456 // If the resulting span exceeds the maximum of the component, then adjust 457 // accordingly. 458 float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment); 459 if (right / (1.F - reqs.alignment) > reqs.maximum) 460 right = maxRight; 461 // If we have not enough space on the left side, then adjust accordingly. 462 if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline) 463 right = ((float) (allocated - baseline)) 464 / reqs.alignment * (1.F - reqs.alignment); 465 466 spanAndOffset[0] = (int) (right / (1.F - reqs.alignment)); 467 spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment); 468 } 469 470 /** 471 * Adjusts the span and offset of a component for the aligned layout. 472 * 473 * @param reqs 474 * @param baseline 475 * @param allocated 476 * @param spanAndOffset 477 */ adjustFromLeft(SizeRequirements reqs, float baseline, int allocated, int[] spanAndOffset)478 private static void adjustFromLeft(SizeRequirements reqs, float baseline, 479 int allocated, int[] spanAndOffset) 480 { 481 float left = baseline; 482 // If the resulting span exceeds the maximum of the component, then adjust 483 // accordingly. 484 float maxLeft = ((float) reqs.maximum) * reqs.alignment; 485 if (left / reqs.alignment > reqs.maximum) 486 left = maxLeft; 487 // If we have not enough space on the right side, then adjust accordingly. 488 if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline) 489 left = ((float) (allocated - baseline)) 490 / (1.F - reqs.alignment) * reqs.alignment; 491 492 spanAndOffset[0] = (int) (left / reqs.alignment); 493 spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment); 494 } 495 496 /** 497 * Returns an array of new preferred sizes for the children based on 498 * <code>delta</code>. <code>delta</code> specifies a change in the 499 * allocated space. The sizes of the children will be shortened or 500 * lengthened to accomodate the new allocation. 501 * 502 * @param delta the change of the size of the total allocation for 503 * the components 504 * @param children the size requirements of each component 505 * 506 * @return the new preferred sizes for each component 507 */ adjustSizes(int delta, SizeRequirements[] children)508 public static int[] adjustSizes(int delta, SizeRequirements[] children) 509 { 510 return null; // TODO 511 } 512 } 513