001/* 002 * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved. 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * o Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 010 * o Redistributions in binary form must reproduce the above copyright notice, 011 * this list of conditions and the following disclaimer in the documentation 012 * and/or other materials provided with the distribution. 013 * 014 * o Neither the name of JGoodies Karsten Lentzsch nor the names of 015 * its contributors may be used to endorse or promote products derived 016 * from this software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031package com.jgoodies.forms.layout; 032 033import java.awt.Component; 034import java.awt.Insets; 035import java.awt.Rectangle; 036import java.io.Serializable; 037import java.util.Locale; 038import java.util.StringTokenizer; 039 040/** 041 * Defines constraints for components that are layed out with the FormLayout. 042 * Defines the components display area: grid x, grid y, 043 * grid width (column span), grid height (row span), horizontal alignment 044 * and vertical alignment.<p> 045 * 046 * Most methods return <em>this</em> object to enable method chaining.<p> 047 * 048 * You can set optional insets in a constructor. This is useful if you 049 * need to use a pixel-size insets to align perceived component bounds 050 * with pixel data, for example an icon. Anyway, this is rarely used. 051 * The insets don't affect the size computation for columns and rows. 052 * I consider renaming the insets to offsets to better indicate the 053 * motivation for this option.<p> 054 * 055 * <strong>Examples</strong>:<br> 056 * The following cell constraints locate a component in the third 057 * column of the fifth row; column and row span are 1; the component 058 * will be aligned with the column's right-hand side and the row's 059 * bottom. 060 * <pre> 061 * CellConstraints cc = new CellConstraints(); 062 * cc.xy (3, 5); 063 * cc.xy (3, 5, CellConstraints.RIGHT, CellConstraints.BOTTOM); 064 * cc.xy (3, 5, "right, bottom"); 065 * 066 * cc.xyw (3, 5, 1); 067 * cc.xyw (3, 5, 1, CellConstraints.RIGHT, CellConstraints.BOTTOM); 068 * cc.xyw (3, 5, 1, "right, bottom"); 069 * 070 * cc.xywh(3, 5, 1, 1); 071 * cc.xywh(3, 5, 1, 1, CellConstraints.RIGHT, CellConstraints.BOTTOM); 072 * cc.xywh(3, 5, 1, 1, "right, bottom"); 073 * </pre> 074 * See also the examples in the {@link FormLayout} class comment.<p> 075 * 076 * TODO: Explain in the JavaDocs that the insets are actually offsets. 077 * And describe that these offsets are not taken into account when 078 * FormLayout computes the column and row sizes.<p> 079 * 080 * TODO: Rename the inset to offsets.<p> 081 * 082 * TODO: In the Forms 1.0.x invisible components are not taken into account 083 * when the FormLayout lays out the container. Add an optional setting for 084 * this on both the container-level and component-level. So one can specify 085 * that invisible components shall be taken into account, but may exclude 086 * individual components. Or the other way round, exclude invisible components, 087 * and include individual components. The API of both the FormLayout and 088 * CellConstraints classes shall be extended to support this option. 089 * This feature is planned for the Forms version 1.1 and is described in 090 * <a href="https://forms.dev.java.net/issues/show_bug.cgi?id=28">issue #28</a> 091 * of the Forms' issue tracker where you can track the progress. 092 * 093 * @author Karsten Lentzsch 094 * @version $Revision$ 095 */ 096@SuppressWarnings("serial") 097public final class CellConstraints implements Cloneable, Serializable { 098 099 // Alignment Constants ************************************************* 100 101 /* 102 * Implementation Note: Do not change the order of the following constants. 103 * The serialization of class Alignment is ordinal-based and relies on it. 104 */ 105 106 /** 107 * Use the column's or row's default alignment. 108 */ 109 public static final Alignment DEFAULT = new Alignment("default", 110 Alignment.BOTH); 111 112 /** 113 * Fill the cell either horizontally or vertically. 114 */ 115 public static final Alignment FILL = new Alignment("fill", Alignment.BOTH); 116 117 /** 118 * Put the component in the left. 119 */ 120 public static final Alignment LEFT = new Alignment("left", 121 Alignment.HORIZONTAL); 122 123 /** 124 * Put the component in the right. 125 */ 126 public static final Alignment RIGHT = new Alignment("right", 127 Alignment.HORIZONTAL); 128 129 /** 130 * Put the component in the center. 131 */ 132 public static final Alignment CENTER = new Alignment("center", 133 Alignment.BOTH); 134 135 /** 136 * Put the component in the top. 137 */ 138 public static final Alignment TOP = new Alignment("top", 139 Alignment.VERTICAL); 140 141 /** 142 * Put the component in the bottom. 143 */ 144 public static final Alignment BOTTOM = new Alignment("bottom", 145 Alignment.VERTICAL); 146 147 /** 148 * An array of all enumeration values used to canonicalize 149 * deserialized alignments. 150 */ 151 private static final Alignment[] VALUES = { DEFAULT, FILL, LEFT, RIGHT, 152 CENTER, TOP, BOTTOM }; 153 154 /** 155 * A reusable <code>Insets</code> object to reduce object instantiation. 156 */ 157 private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); 158 159 // Fields *************************************************************** 160 161 /** 162 * Describes the component's horizontal grid origin (starts at 1). 163 */ 164 public int gridX; 165 166 /** 167 * Describes the component's vertical grid origin (starts at 1). 168 */ 169 public int gridY; 170 171 /** 172 * Describes the component's horizontal grid extend (number of cells). 173 */ 174 public int gridWidth; 175 176 /** 177 * Describes the component's vertical grid extent (number of cells). 178 */ 179 public int gridHeight; 180 181 /** 182 * Describes the component's horizontal alignment. 183 */ 184 public Alignment hAlign; 185 186 /** 187 * Describes the component's vertical alignment. 188 */ 189 public Alignment vAlign; 190 191 /** 192 * Describes the component's <code>Insets</code> in it's display area. 193 */ 194 public Insets insets; 195 196 // Instance Creation **************************************************** 197 198 /** 199 * Constructs a default instance of <code>CellConstraints</code>. 200 */ 201 public CellConstraints() { 202 this(1, 1); 203 } 204 205 /** 206 * Constructs an instance of <code>CellConstraints</code> for the given 207 * cell position.<p> 208 * 209 * <strong>Examples:</strong><pre> 210 * new CellConstraints(1, 3); 211 * new CellConstraints(1, 3); 212 * </pre> 213 * 214 * @param gridX the component's horizontal grid origin 215 * @param gridY the component's vertical grid origin 216 */ 217 public CellConstraints(int gridX, int gridY) { 218 this(gridX, gridY, 1, 1); 219 } 220 221 /** 222 * Constructs an instance of <code>CellConstraints</code> for the given 223 * cell position, anchor, and fill.<p> 224 * 225 * <strong>Examples:</strong><pre> 226 * new CellConstraints(1, 3, CellConstraints.LEFT, CellConstraints.BOTTOM); 227 * new CellConstraints(1, 3, CellConstraints.CENTER, CellConstraints.FILL); 228 * </pre> 229 * 230 * @param gridX the component's horizontal grid origin 231 * @param gridY the component's vertical grid origin 232 * @param hAlign the component's horizontal alignment 233 * @param vAlign the component's vertical alignment 234 */ 235 public CellConstraints(int gridX, int gridY, Alignment hAlign, 236 Alignment vAlign) { 237 this(gridX, gridY, 1, 1, hAlign, vAlign, EMPTY_INSETS); 238 } 239 240 /** 241 * Constructs an instance of <code>CellConstraints</code> for the given 242 * cell position and size.<p> 243 * 244 * <strong>Examples:</strong><pre> 245 * new CellConstraints(1, 3, 2, 1); 246 * new CellConstraints(1, 3, 7, 3); 247 * </pre> 248 * 249 * @param gridX the component's horizontal grid origin 250 * @param gridY the component's vertical grid origin 251 * @param gridWidth the component's horizontal extent 252 * @param gridHeight the component's vertical extent 253 */ 254 public CellConstraints(int gridX, int gridY, int gridWidth, 255 int gridHeight) { 256 this(gridX, gridY, gridWidth, gridHeight, DEFAULT, DEFAULT); 257 } 258 259 /** 260 * Constructs an instance of <code>CellConstraints</code> for the given 261 * cell position and size, anchor, and fill.<p> 262 * 263 * <strong>Examples:</strong><pre> 264 * new CellConstraints(1, 3, 2, 1, CellConstraints.LEFT, CellConstraints.BOTTOM); 265 * new CellConstraints(1, 3, 7, 3, CellConstraints.CENTER, CellConstraints.FILL); 266 * </pre> 267 * 268 * @param gridX the component's horizontal grid origin 269 * @param gridY the component's vertical grid origin 270 * @param gridWidth the component's horizontal extent 271 * @param gridHeight the component's vertical extent 272 * @param hAlign the component's horizontal alignment 273 * @param vAlign the component's vertical alignment 274 */ 275 public CellConstraints(int gridX, int gridY, int gridWidth, int gridHeight, 276 Alignment hAlign, Alignment vAlign) { 277 this(gridX, gridY, gridWidth, gridHeight, hAlign, vAlign, EMPTY_INSETS); 278 } 279 280 /** 281 * Constructs an instance of <code>CellConstraints</code> for 282 * the complete set of available properties.<p> 283 * 284 * <strong>Examples:</strong><pre> 285 * new CellConstraints(1, 3, 2, 1, CellConstraints.LEFT, CellConstraints.BOTTOM, new Insets(0, 1, 0, 3)); 286 * new CellConstraints(1, 3, 7, 3, CellConstraints.CENTER, CellConstraints.FILL, new Insets(0, 1, 0, 0)); 287 * </pre> 288 * 289 * @param gridX the component's horizontal grid origin 290 * @param gridY the component's vertical grid origin 291 * @param gridWidth the component's horizontal extent 292 * @param gridHeight the component's vertical extent 293 * @param hAlign the component's horizontal alignment 294 * @param vAlign the component's vertical alignment 295 * @param insets the component's display area <code>Insets</code> 296 * @exception IndexOutOfBoundsException if the grid origin or extent is negative 297 * @exception NullPointerException if the horizontal or vertical alignment is null 298 * @exception IllegalArgumentException if an alignment orientation is invalid 299 */ 300 public CellConstraints(int gridX, int gridY, int gridWidth, int gridHeight, 301 Alignment hAlign, Alignment vAlign, Insets insets) { 302 this.gridX = gridX; 303 this.gridY = gridY; 304 this.gridWidth = gridWidth; 305 this.gridHeight = gridHeight; 306 this.hAlign = hAlign; 307 this.vAlign = vAlign; 308 this.insets = insets; 309 if (gridX <= 0) { 310 throw new IndexOutOfBoundsException( 311 "The grid x must be a positive number."); 312 } 313 if (gridY <= 0) { 314 throw new IndexOutOfBoundsException( 315 "The grid y must be a positive number."); 316 } 317 if (gridWidth <= 0) { 318 throw new IndexOutOfBoundsException( 319 "The grid width must be a positive number."); 320 } 321 if (gridHeight <= 0) { 322 throw new IndexOutOfBoundsException( 323 "The grid height must be a positive number."); 324 } 325 if (hAlign == null) { 326 throw new NullPointerException( 327 "The horizontal alignment must not be null."); 328 } 329 if (vAlign == null) { 330 throw new NullPointerException( 331 "The vertical alignment must not be null."); 332 } 333 ensureValidOrientations(hAlign, vAlign); 334 } 335 336 /** 337 * Constructs an instance of <code>CellConstraints</code> from 338 * the given encoded string properties.<p> 339 * 340 * <strong>Examples:</strong><pre> 341 * new CellConstraints("1, 3"); 342 * new CellConstraints("1, 3, left, bottom"); 343 * new CellConstraints("1, 3, 2, 1, left, bottom"); 344 * new CellConstraints("1, 3, 2, 1, l, b"); 345 * </pre> 346 * 347 * @param encodedConstraints the constraints encoded as string 348 */ 349 public CellConstraints(String encodedConstraints) { 350 this(); 351 initFromConstraints(encodedConstraints); 352 } 353 354 // Setters ************************************************************** 355 356 /** 357 * Sets row and column origins; sets width and height to 1; 358 * uses the default alignments.<p> 359 * 360 * <strong>Examples:</strong><pre> 361 * cc.xy(1, 1); 362 * cc.xy(1, 3); 363 * </pre> 364 * 365 * @param col the new column index 366 * @param row the new row index 367 * @return this 368 */ 369 public CellConstraints xy(int col, int row) { 370 return xywh(col, row, 1, 1); 371 } 372 373 /** 374 * Sets row and column origins; sets width and height to 1; 375 * decodes horizontal and vertical alignments from the given string.<p> 376 * 377 * <strong>Examples:</strong><pre> 378 * cc.xy(1, 3, "left, bottom"); 379 * cc.xy(1, 3, "l, b"); 380 * cc.xy(1, 3, "center, fill"); 381 * cc.xy(1, 3, "c, f"); 382 * </pre> 383 * 384 * @param col the new column index 385 * @param row the new row index 386 * @param encodedAlignments describes the horizontal and vertical alignments 387 * @return this 388 * @exception IllegalArgumentException if an alignment orientation is invalid 389 */ 390 public CellConstraints xy(int col, int row, String encodedAlignments) { 391 return xywh(col, row, 1, 1, encodedAlignments); 392 } 393 394 /** 395 * Sets the row and column origins; sets width and height to 1; 396 * set horizontal and vertical alignment using the specified objects.<p> 397 * 398 * <strong>Examples:</strong><pre> 399 * cc.xy(1, 3, CellConstraints.LEFT, CellConstraints.BOTTOM); 400 * cc.xy(1, 3, CellConstraints.CENTER, CellConstraints.FILL); 401 * </pre> 402 * 403 * @param col the new column index 404 * @param row the new row index 405 * @param colAlign horizontal component alignment 406 * @param rowAlign vertical component alignment 407 * @return this 408 */ 409 public CellConstraints xy(int col, int row, Alignment colAlign, 410 Alignment rowAlign) { 411 return xywh(col, row, 1, 1, colAlign, rowAlign); 412 } 413 414 /** 415 * Sets the row, column, width, and height; uses a height (row span) of 1 416 * and the horizontal and vertical default alignments.<p> 417 * 418 * <strong>Examples:</strong><pre> 419 * cc.xyw(1, 3, 7); 420 * cc.xyw(1, 3, 2); 421 * </pre> 422 * 423 * @param col the new column index 424 * @param row the new row index 425 * @param colSpan the column span or grid width 426 * @return this 427 */ 428 public CellConstraints xyw(int col, int row, int colSpan) { 429 return xywh(col, row, colSpan, 1, DEFAULT, DEFAULT); 430 } 431 432 /** 433 * Sets the row, column, width, and height; 434 * decodes the horizontal and vertical alignments from the given string. 435 * The row span (height) is set to 1.<p> 436 * 437 * <strong>Examples:</strong><pre> 438 * cc.xyw(1, 3, 7, "left, bottom"); 439 * cc.xyw(1, 3, 7, "l, b"); 440 * cc.xyw(1, 3, 2, "center, fill"); 441 * cc.xyw(1, 3, 2, "c, f"); 442 * </pre> 443 * 444 * @param col the new column index 445 * @param row the new row index 446 * @param colSpan the column span or grid width 447 * @param encodedAlignments describes the horizontal and vertical alignments 448 * @return this 449 * @exception IllegalArgumentException if an alignment orientation is invalid 450 */ 451 public CellConstraints xyw(int col, int row, int colSpan, 452 String encodedAlignments) { 453 return xywh(col, row, colSpan, 1, encodedAlignments); 454 } 455 456 /** 457 * Sets the row, column, width, and height; sets the horizontal 458 * and vertical alignment using the specified alignment objects. 459 * The row span (height) is set to 1.<p> 460 * 461 * <strong>Examples:</strong><pre> 462 * cc.xyw(1, 3, 2, CellConstraints.LEFT, CellConstraints.BOTTOM); 463 * cc.xyw(1, 3, 7, CellConstraints.CENTER, CellConstraints.FILL); 464 * </pre> 465 * 466 * @param col the new column index 467 * @param row the new row index 468 * @param colSpan the column span or grid width 469 * @param colAlign horizontal component alignment 470 * @param rowAlign vertical component alignment 471 * @return this 472 * @exception IllegalArgumentException if an alignment orientation is invalid 473 */ 474 public CellConstraints xyw(int col, int row, int colSpan, 475 Alignment colAlign, Alignment rowAlign) { 476 return xywh(col, row, colSpan, 1, colAlign, rowAlign); 477 } 478 479 /** 480 * Sets the row, column, width, and height; uses default alignments.<p> 481 * 482 * <strong>Examples:</strong><pre> 483 * cc.xywh(1, 3, 2, 1); 484 * cc.xywh(1, 3, 7, 3); 485 * </pre> 486 * 487 * @param col the new column index 488 * @param row the new row index 489 * @param colSpan the column span or grid width 490 * @param rowSpan the row span or grid height 491 * @return this 492 */ 493 public CellConstraints xywh(int col, int row, int colSpan, int rowSpan) { 494 return xywh(col, row, colSpan, rowSpan, DEFAULT, DEFAULT); 495 } 496 497 /** 498 * Sets the row, column, width, and height; 499 * decodes the horizontal and vertical alignments from the given string.<p> 500 * 501 * <strong>Examples:</strong><pre> 502 * cc.xywh(1, 3, 2, 1, "left, bottom"); 503 * cc.xywh(1, 3, 2, 1, "l, b"); 504 * cc.xywh(1, 3, 7, 3, "center, fill"); 505 * cc.xywh(1, 3, 7, 3, "c, f"); 506 * </pre> 507 * 508 * @param col the new column index 509 * @param row the new row index 510 * @param colSpan the column span or grid width 511 * @param rowSpan the row span or grid height 512 * @param encodedAlignments describes the horizontal and vertical alignments 513 * @return this 514 * @exception IllegalArgumentException if an alignment orientation is invalid 515 */ 516 public CellConstraints xywh(int col, int row, int colSpan, int rowSpan, 517 String encodedAlignments) { 518 CellConstraints result = xywh(col, row, colSpan, rowSpan); 519 result.setAlignments(encodedAlignments); 520 return result; 521 } 522 523 /** 524 * Sets the row, column, width, and height; sets the horizontal 525 * and vertical alignment using the specified alignment objects.<p> 526 * 527 * <strong>Examples:</strong><pre> 528 * cc.xywh(1, 3, 2, 1, CellConstraints.LEFT, CellConstraints.BOTTOM); 529 * cc.xywh(1, 3, 7, 3, CellConstraints.CENTER, CellConstraints.FILL); 530 * </pre> 531 * 532 * @param col the new column index 533 * @param row the new row index 534 * @param colSpan the column span or grid width 535 * @param rowSpan the row span or grid height 536 * @param colAlign horizontal component alignment 537 * @param rowAlign vertical component alignment 538 * @return this 539 * @exception IllegalArgumentException if an alignment orientation is invalid 540 */ 541 public CellConstraints xywh(int col, int row, int colSpan, int rowSpan, 542 Alignment colAlign, Alignment rowAlign) { 543 this.gridX = col; 544 this.gridY = row; 545 this.gridWidth = colSpan; 546 this.gridHeight = rowSpan; 547 this.hAlign = colAlign; 548 this.vAlign = rowAlign; 549 ensureValidOrientations(hAlign, vAlign); 550 return this; 551 } 552 553 // Parsing and Decoding String Descriptions ***************************** 554 555 /** 556 * Decodes and returns the grid bounds and alignments for this 557 * constraints as an array of six integers. The string representation 558 * is a comma separated sequence, one of 559 * <pre> 560 * "x, y" 561 * "x, y, w, h" 562 * "x, y, hAlign, vAlign" 563 * "x, y, w, h, hAlign, vAlign" 564 * </pre> 565 * 566 * @param encodedConstraints represents horizontal and vertical alignment 567 * @exception IllegalArgumentException if the encoded constraints do not 568 * follow the constraint syntax 569 */ 570 private void initFromConstraints(String encodedConstraints) { 571 StringTokenizer tokenizer = new StringTokenizer(encodedConstraints, 572 " ,"); 573 int argCount = tokenizer.countTokens(); 574 if (!(argCount == 2 || argCount == 4 || argCount == 6)) { 575 throw new IllegalArgumentException( 576 "You must provide 2, 4 or 6 arguments."); 577 } 578 579 Integer nextInt = decodeInt(tokenizer.nextToken()); 580 if (nextInt == null) { 581 throw new IllegalArgumentException( 582 "First cell constraint element must be a number."); 583 } 584 gridX = nextInt.intValue(); 585 if (gridX <= 0) { 586 throw new IndexOutOfBoundsException( 587 "The grid x must be a positive number."); 588 } 589 590 nextInt = decodeInt(tokenizer.nextToken()); 591 if (nextInt == null) { 592 throw new IllegalArgumentException( 593 "Second cell constraint element must be a number."); 594 } 595 gridY = nextInt.intValue(); 596 if (gridY <= 0) { 597 throw new IndexOutOfBoundsException( 598 "The grid y must be a positive number."); 599 } 600 601 if (!tokenizer.hasMoreTokens()) { 602 return; 603 } 604 605 String token = tokenizer.nextToken(); 606 nextInt = decodeInt(token); 607 if (nextInt != null) { 608 // Case: "x, y, w, h" or 609 // "x, y, w, h, hAlign, vAlign" 610 gridWidth = nextInt.intValue(); 611 if (gridWidth <= 0) { 612 throw new IndexOutOfBoundsException( 613 "The grid width must be a positive number."); 614 } 615 nextInt = decodeInt(tokenizer.nextToken()); 616 if (nextInt == null) { 617 throw new IllegalArgumentException( 618 "Fourth cell constraint element must be like third."); 619 } 620 gridHeight = nextInt.intValue(); 621 if (gridHeight <= 0) { 622 throw new IndexOutOfBoundsException( 623 "The grid height must be a positive number."); 624 } 625 626 if (!tokenizer.hasMoreTokens()) { 627 return; 628 } 629 token = tokenizer.nextToken(); 630 } 631 632 hAlign = decodeAlignment(token); 633 vAlign = decodeAlignment(tokenizer.nextToken()); 634 ensureValidOrientations(hAlign, vAlign); 635 } 636 637 /** 638 * Decodes a string description for the horizontal and vertical alignment 639 * and sets this CellConstraints' alignment values.<p> 640 * 641 * Valid horizontal alignments are: left, middle, right, default, and fill. 642 * Valid vertical alignments are: top, center, bottom, default, and fill. 643 * The anchor's string representation abbreviates the alignment: 644 * l, m, r, d, f, t, c, and b.<p> 645 * 646 * Anchor examples: 647 * "m, c" is centered, "l, t" is northwest, "m, t" is north, "r, c" east. 648 * "m, d" is horizontally centered and uses the row's default alignment. 649 * "d, t" is on top of the cell and uses the column's default alignment.<p> 650 * 651 * @param encodedAlignments represents horizontal and vertical alignment 652 * @exception IllegalArgumentException if an alignment orientation is invalid 653 */ 654 private void setAlignments(String encodedAlignments) { 655 StringTokenizer tokenizer = new StringTokenizer(encodedAlignments, 656 " ,"); 657 hAlign = decodeAlignment(tokenizer.nextToken()); 658 vAlign = decodeAlignment(tokenizer.nextToken()); 659 ensureValidOrientations(hAlign, vAlign); 660 } 661 662 /** 663 * Decodes an integer string representation and returns the 664 * associated Integer or null in case of an invalid number format. 665 * 666 * @param token the encoded integer 667 * @return the decoded Integer or null 668 */ 669 private Integer decodeInt(String token) { 670 try { 671 return Integer.decode(token); 672 } catch (NumberFormatException e) { 673 return null; 674 } 675 } 676 677 /** 678 * Parses an alignment string description and 679 * returns the corresponding alignment value. 680 * 681 * @param encodedAlignment the encoded alignment 682 * @return the associated <code>Alignment</code> instance 683 */ 684 private Alignment decodeAlignment(String encodedAlignment) { 685 return Alignment.valueOf(encodedAlignment); 686 } 687 688 /** 689 * Checks and verifies that this constraints object has valid grid 690 * index values, i. e. the display area cells are inside the form's grid. 691 * 692 * @param colCount number of columns in the grid 693 * @param rowCount number of rows in the grid 694 * @exception IndexOutOfBoundsException if the display area described 695 * by this constraints object is not inside the grid 696 */ 697 void ensureValidGridBounds(int colCount, int rowCount) { 698 if (gridX <= 0) { 699 throw new IndexOutOfBoundsException( 700 "The column index " + gridX + " must be positive."); 701 } 702 if (gridX > colCount) { 703 throw new IndexOutOfBoundsException("The column index " + gridX 704 + " must be less than or equal to " + colCount + "."); 705 } 706 if (gridX + gridWidth - 1 > colCount) { 707 throw new IndexOutOfBoundsException("The grid width " + gridWidth 708 + " must be less than or equal to " + (colCount - gridX + 1) 709 + "."); 710 } 711 if (gridY <= 0) { 712 throw new IndexOutOfBoundsException( 713 "The row index " + gridY + " must be positive."); 714 } 715 if (gridY > rowCount) { 716 throw new IndexOutOfBoundsException("The row index " + gridY 717 + " must be less than or equal to " + rowCount + "."); 718 } 719 if (gridY + gridHeight - 1 > rowCount) { 720 throw new IndexOutOfBoundsException("The grid height " + gridHeight 721 + " must be less than or equal to " + (rowCount - gridY + 1) 722 + "."); 723 } 724 } 725 726 /** 727 * Checks and verifies that the horizontal alignment is a horizontal 728 * and the vertical alignment is vertical. 729 * 730 * @param horizontalAlignment the horizontal alignment 731 * @param verticalAlignment the vertical alignment 732 * @exception IllegalArgumentException if an alignment is invalid 733 */ 734 private void ensureValidOrientations(Alignment horizontalAlignment, 735 Alignment verticalAlignment) { 736 if (!horizontalAlignment.isHorizontal()) { 737 throw new IllegalArgumentException( 738 "The horizontal alignment must be one of: left, center, right, fill, default."); 739 } 740 if (!verticalAlignment.isVertical()) { 741 throw new IllegalArgumentException( 742 "The vertical alignment must be one of: top, center, botto, fill, default."); 743 } 744 } 745 746 // Settings Component Bounds ******************************************** 747 748 /** 749 * Sets the component's bounds using the given component and cell bounds. 750 * 751 * @param c the component to set bounds 752 * @param layout the FormLayout instance that computes the bounds 753 * @param cellBounds the cell's bounds 754 * @param minWidthMeasure measures the minimum width 755 * @param minHeightMeasure measures the minimum height 756 * @param prefWidthMeasure measures the preferred width 757 * @param prefHeightMeasure measures the preferred height 758 */ 759 void setBounds(Component c, FormLayout layout, Rectangle cellBounds, 760 FormLayout.Measure minWidthMeasure, 761 FormLayout.Measure minHeightMeasure, 762 FormLayout.Measure prefWidthMeasure, 763 FormLayout.Measure prefHeightMeasure) { 764 ColumnSpec colSpec = gridWidth == 1 ? layout.getColumnSpec(gridX) 765 : null; 766 RowSpec rowSpec = gridHeight == 1 ? layout.getRowSpec(gridY) : null; 767 Alignment concreteHAlign = concreteAlignment(this.hAlign, colSpec); 768 Alignment concreteVAlign = concreteAlignment(this.vAlign, rowSpec); 769 Insets concreteInsets = this.insets != null ? this.insets 770 : EMPTY_INSETS; 771 int cellX = cellBounds.x + concreteInsets.left; 772 int cellY = cellBounds.y + concreteInsets.top; 773 int cellW = cellBounds.width - concreteInsets.left 774 - concreteInsets.right; 775 int cellH = cellBounds.height - concreteInsets.top 776 - concreteInsets.bottom; 777 int compW = componentSize(c, colSpec, cellW, minWidthMeasure, 778 prefWidthMeasure); 779 int compH = componentSize(c, rowSpec, cellH, minHeightMeasure, 780 prefHeightMeasure); 781 int x = origin(concreteHAlign, cellX, cellW, compW); 782 int y = origin(concreteVAlign, cellY, cellH, compH); 783 int w = extent(concreteHAlign, cellW, compW); 784 int h = extent(concreteVAlign, cellH, compH); 785 c.setBounds(x, y, w, h); 786 } 787 788 /** 789 * Computes and returns the concrete alignment. Takes into account 790 * the cell alignment and <i>the</i> <code>FormSpec</code> if applicable.<p> 791 * 792 * If this constraints object doesn't belong to a single column or row, 793 * the <code>formSpec</code> parameter is <code>null</code>. 794 * In this case the cell alignment is answered, but <code>DEFAULT</code> 795 * is mapped to <code>FILL</code>.<p> 796 * 797 * If the cell belongs to a single column or row, we use the cell 798 * alignment, unless it is <code>DEFAULT</code>, where the alignment 799 * is inherited from the column or row resp. 800 * 801 * @param cellAlignment this cell's alignment 802 * @param formSpec the associated column or row specification 803 * @return the concrete alignment 804 */ 805 private Alignment concreteAlignment(Alignment cellAlignment, 806 FormSpec formSpec) { 807 return formSpec == null 808 ? cellAlignment == DEFAULT ? FILL : cellAlignment 809 : usedAlignment(cellAlignment, formSpec); 810 } 811 812 /** 813 * Returns the alignment used for a given form constraints object. 814 * The cell alignment overrides the column or row default, unless 815 * it is <code>DEFAULT</code>. In the latter case, we use the 816 * column or row alignment. 817 * 818 * @param cellAlignment this cell constraint's alignment 819 * @param formSpec the associated column or row specification 820 * @return the alignment used 821 */ 822 private Alignment usedAlignment(Alignment cellAlignment, 823 FormSpec formSpec) { 824 if (cellAlignment != CellConstraints.DEFAULT) { 825 // Cell alignments other than DEFAULT override col/row alignments 826 return cellAlignment; 827 } 828 FormSpec.DefaultAlignment defaultAlignment = formSpec 829 .getDefaultAlignment(); 830 if (defaultAlignment == FormSpec.FILL_ALIGN) { 831 return FILL; 832 } 833 if (defaultAlignment == ColumnSpec.LEFT) { 834 return LEFT; 835 } else if (defaultAlignment == FormSpec.CENTER_ALIGN) { 836 return CENTER; 837 } else if (defaultAlignment == ColumnSpec.RIGHT) { 838 return RIGHT; 839 } else if (defaultAlignment == RowSpec.TOP) { 840 return TOP; 841 } else { 842 return BOTTOM; 843 } 844 } 845 846 /** 847 * Computes and returns the pixel size of the given component using the 848 * given form specification, measures, and cell size. 849 * 850 * @param component the component to measure 851 * @param formSpec the specification of the component's column/row 852 * @param minMeasure the measure for the minimum size 853 * @param prefMeasure the measure for the preferred size 854 * @param cellSize the cell size 855 * @return the component size as measured or a constant 856 */ 857 private int componentSize(Component component, FormSpec formSpec, 858 int cellSize, FormLayout.Measure minMeasure, 859 FormLayout.Measure prefMeasure) { 860 if (formSpec == null) { 861 return prefMeasure.sizeOf(component); 862 } else if (formSpec.getSize() == Sizes.MINIMUM) { 863 return minMeasure.sizeOf(component); 864 } else if (formSpec.getSize() == Sizes.PREFERRED) { 865 return prefMeasure.sizeOf(component); 866 } else { // default mode 867 return Math.min(cellSize, prefMeasure.sizeOf(component)); 868 } 869 } 870 871 /** 872 * Computes and returns the component's pixel origin. 873 * 874 * @param alignment the component's alignment 875 * @param cellOrigin the origin of the display area 876 * @param cellSize the extent of the display area 877 * @param componentSize 878 * @return the component's pixel origin 879 */ 880 private int origin(Alignment alignment, int cellOrigin, int cellSize, 881 int componentSize) { 882 if (alignment == RIGHT || alignment == BOTTOM) { 883 return cellOrigin + cellSize - componentSize; 884 } else if (alignment == CENTER) { 885 return cellOrigin + (cellSize - componentSize) / 2; 886 } else { // left, top, fill 887 return cellOrigin; 888 } 889 } 890 891 /** 892 * Returns the component's pixel extent. 893 * 894 * @param alignment the component's alignment 895 * @param cellSize the size of the display area 896 * @param componentSize the component's size 897 * @return the component's pixel extent 898 */ 899 private int extent(Alignment alignment, int cellSize, int componentSize) { 900 return alignment == FILL ? cellSize : componentSize; 901 } 902 903 // Misc ***************************************************************** 904 905 /** 906 * Creates a copy of this cell constraints object. 907 * 908 * @return a copy of this cell constraints object 909 */ 910 @Override 911 public Object clone() { 912 try { 913 CellConstraints c = (CellConstraints) super.clone(); 914 c.insets = (Insets) insets.clone(); 915 return c; 916 } catch (CloneNotSupportedException e) { 917 // This shouldn't happen, since we are Cloneable. 918 throw new InternalError(); 919 } 920 } 921 922 /** 923 * Constructs and returns a string representation of this constraints object. 924 * 925 * @return string representation of this constraints object 926 */ 927 @Override 928 public String toString() { 929 StringBuffer buffer = new StringBuffer("CellConstraints"); 930 buffer.append("[x="); 931 buffer.append(gridX); 932 buffer.append("; y="); 933 buffer.append(gridY); 934 buffer.append("; w="); 935 buffer.append(gridWidth); 936 buffer.append("; h="); 937 buffer.append(gridHeight); 938 buffer.append("; hAlign="); 939 buffer.append(hAlign); 940 buffer.append("; vAlign="); 941 buffer.append(vAlign); 942 if (!EMPTY_INSETS.equals(insets)) { 943 buffer.append("; insets="); 944 buffer.append(insets); 945 } 946 947 buffer.append(']'); 948 return buffer.toString(); 949 } 950 951 /** 952 * Returns a short string representation of this constraints object. 953 * 954 * @return a short string representation of this constraints object 955 */ 956 public String toShortString() { 957 return toShortString(null); 958 } 959 960 /** 961 * Returns a short string representation of this constraints object. 962 * This method can use the given <code>FormLayout</code> 963 * to display extra information how default alignments 964 * are mapped to concrete alignments. Therefore it asks the 965 * related column and row as specified by this constraints object. 966 * 967 * @param layout the layout to be presented as a string 968 * @return a short string representation of this constraints object 969 */ 970 public String toShortString(FormLayout layout) { 971 StringBuffer buffer = new StringBuffer("("); 972 buffer.append(formatInt(gridX)); 973 buffer.append(", "); 974 buffer.append(formatInt(gridY)); 975 buffer.append(", "); 976 buffer.append(formatInt(gridWidth)); 977 buffer.append(", "); 978 buffer.append(formatInt(gridHeight)); 979 buffer.append(", \""); 980 buffer.append(hAlign.abbreviation()); 981 if (hAlign == DEFAULT && layout != null) { 982 buffer.append('='); 983 ColumnSpec colSpec = gridWidth == 1 ? layout.getColumnSpec(gridX) 984 : null; 985 buffer.append(concreteAlignment(hAlign, colSpec).abbreviation()); 986 } 987 buffer.append(", "); 988 buffer.append(vAlign.abbreviation()); 989 if (vAlign == DEFAULT && layout != null) { 990 buffer.append('='); 991 RowSpec rowSpec = gridHeight == 1 ? layout.getRowSpec(gridY) : null; 992 buffer.append(concreteAlignment(vAlign, rowSpec).abbreviation()); 993 } 994 buffer.append("\""); 995 if (!EMPTY_INSETS.equals(insets)) { 996 buffer.append(", "); 997 buffer.append(insets); 998 } 999 1000 buffer.append(')'); 1001 return buffer.toString(); 1002 } 1003 1004 // Helper Class ********************************************************* 1005 1006 /** 1007 * An ordinal-based serializable typesafe enumeration for component 1008 * alignment types as used by the {@link FormLayout}. 1009 */ 1010 public static final class Alignment implements Serializable { 1011 1012 private static final int HORIZONTAL = 0; 1013 private static final int VERTICAL = 1; 1014 private static final int BOTH = 2; 1015 1016 private final transient String name; 1017 private final transient int orientation; 1018 1019 private Alignment(String name, int orientation) { 1020 this.name = name; 1021 this.orientation = orientation; 1022 } 1023 1024 static Alignment valueOf(String nameOrAbbreviation) { 1025 String str = nameOrAbbreviation.toLowerCase(Locale.ENGLISH); 1026 if (str.equals("d") || str.equals("default")) { 1027 return DEFAULT; 1028 } else if (str.equals("f") || str.equals("fill")) { 1029 return FILL; 1030 } else if (str.equals("c") || str.equals("center")) { 1031 return CENTER; 1032 } else if (str.equals("l") || str.equals("left")) { 1033 return LEFT; 1034 } else if (str.equals("r") || str.equals("right")) { 1035 return RIGHT; 1036 } else if (str.equals("t") || str.equals("top")) { 1037 return TOP; 1038 } else if (str.equals("b") || str.equals("bottom")) { 1039 return BOTTOM; 1040 } else { 1041 throw new IllegalArgumentException("Invalid alignment " 1042 + nameOrAbbreviation 1043 + ". Must be one of: left, center, right, top, bottom, " 1044 + "fill, default, l, c, r, t, b, f, d."); 1045 } 1046 } 1047 1048 /** 1049 * Returns this Alignment's name. 1050 * 1051 * @return this alignment's name. 1052 */ 1053 @Override 1054 public String toString() { 1055 return name; 1056 } 1057 1058 /** 1059 * Returns the first character of this Alignment's name. 1060 * Used to identify it in short format strings. 1061 * 1062 * @return the name's first character. 1063 */ 1064 public char abbreviation() { 1065 return name.charAt(0); 1066 } 1067 1068 private boolean isHorizontal() { 1069 return orientation != VERTICAL; 1070 } 1071 1072 private boolean isVertical() { 1073 return orientation != HORIZONTAL; 1074 } 1075 1076 // Serialization ********************************************************* 1077 1078 private static int nextOrdinal = 0; 1079 1080 private final int ordinal = nextOrdinal++; 1081 1082 private Object readResolve() { 1083 return VALUES[ordinal]; // Canonicalize 1084 } 1085 1086 } 1087 1088 /** 1089 * Returns an integer that has a minimum of two characters. 1090 * 1091 * @param number the number to format 1092 * @return a string representation for a number with a minimum of two chars 1093 */ 1094 private String formatInt(int number) { 1095 String str = Integer.toString(number); 1096 return number < 10 ? " " + str : str; 1097 } 1098 1099}