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.builder; 032 033import java.awt.Component; 034import java.awt.ComponentOrientation; 035import java.awt.Container; 036 037import com.jgoodies.forms.factories.FormFactory; 038import com.jgoodies.forms.layout.CellConstraints; 039import com.jgoodies.forms.layout.ColumnSpec; 040import com.jgoodies.forms.layout.FormLayout; 041import com.jgoodies.forms.layout.RowSpec; 042 043/** 044 * An abstract class that minimizes the effort required to implement 045 * non-visual builders that use the {@link FormLayout}.<p> 046 * 047 * Builders hide details of the FormLayout and provide convenience behavior 048 * that assists you in constructing a form. 049 * This class provides a cell cursor that helps you traverse a form while 050 * you add components. Also, it offers several methods to append custom 051 * and logical columns and rows. 052 * 053 * @author Karsten Lentzsch 054 * @version $Revision$ 055 * 056 * @see ButtonBarBuilder 057 * @see ButtonStackBuilder 058 * @see PanelBuilder 059 * @see I15dPanelBuilder 060 * @see DefaultFormBuilder 061 */ 062public abstract class AbstractFormBuilder { 063 064 /** 065 * Holds the layout container that we are building. 066 */ 067 private final Container container; 068 069 /** 070 * Holds the instance of <code>FormLayout</code> that is used to 071 * specify, fill and layout this form. 072 */ 073 private final FormLayout layout; 074 075 /** 076 * Holds an instance of <code>CellConstraints</code> that will be used to 077 * specify the location, extent and alignments of the component to be 078 * added next. 079 */ 080 private CellConstraints currentCellConstraints; 081 082 /** 083 * Specifies if we fill the grid from left to right or right to left. 084 * This value is initialized during the construction from the layout 085 * container's component orientation. 086 * 087 * @see #isLeftToRight() 088 * @see #setLeftToRight(boolean) 089 * @see ComponentOrientation 090 */ 091 private boolean leftToRight; 092 093 // Instance Creation **************************************************** 094 095 /** 096 * Constructs an instance of <code>AbstractFormBuilder</code> 097 * for the given FormLayout and layout container. 098 * 099 * @param layout the {@link FormLayout} to use 100 * @param container the layout container 101 * 102 * @exception NullPointerException if the layout or container is null 103 */ 104 public AbstractFormBuilder(FormLayout layout, Container container) { 105 if (layout == null) { 106 throw new NullPointerException("The layout must not be null."); 107 } 108 109 if (container == null) { 110 throw new NullPointerException( 111 "The layout container must not be null."); 112 } 113 114 this.container = container; 115 this.layout = layout; 116 117 container.setLayout(layout); 118 currentCellConstraints = new CellConstraints(); 119 ComponentOrientation orientation = container.getComponentOrientation(); 120 leftToRight = orientation.isLeftToRight() 121 || !orientation.isHorizontal(); 122 } 123 124 /** 125 * Constructs an instance of <code>AbstractFormBuilder</code> for the given 126 * container and form layout. 127 * 128 * @param container the layout container 129 * @param layout the {@link FormLayout} to use 130 * 131 * @deprecated Replaced by {@link #AbstractFormBuilder(FormLayout, Container)}. 132 */ 133 @Deprecated 134 public AbstractFormBuilder(Container container, FormLayout layout) { 135 this(layout, container); 136 } 137 138 // Accessors ************************************************************ 139 140 /** 141 * Returns the container used to build the form. 142 * 143 * @return the layout container 144 */ 145 public final Container getContainer() { 146 return container; 147 } 148 149 /** 150 * Returns the instance of {@link FormLayout} used to build this form. 151 * 152 * @return the FormLayout 153 */ 154 public final FormLayout getLayout() { 155 return layout; 156 } 157 158 /** 159 * Returns the number of columns in the form. 160 * 161 * @return the number of columns 162 */ 163 public final int getColumnCount() { 164 return getLayout().getColumnCount(); 165 } 166 167 /** 168 * Returns the number of rows in the form. 169 * 170 * @return the number of rows 171 */ 172 public final int getRowCount() { 173 return getLayout().getRowCount(); 174 } 175 176 // Accessing the Cursor Direction *************************************** 177 178 /** 179 * Returns whether this builder fills the form left-to-right 180 * or right-to-left. The initial value of this property is set 181 * during the builder construction from the layout container's 182 * <code>componentOrientation</code> property. 183 * 184 * @return true indicates left-to-right, false indicates right-to-left 185 * 186 * @see #setLeftToRight(boolean) 187 * @see ComponentOrientation 188 */ 189 public final boolean isLeftToRight() { 190 return leftToRight; 191 } 192 193 /** 194 * Sets the form fill direction to left-to-right or right-to-left. 195 * The initial value of this property is set during the builder construction 196 * from the layout container's <code>componentOrientation</code> property. 197 * 198 * @param b true indicates left-to-right, false right-to-left 199 * 200 * @see #isLeftToRight() 201 * @see ComponentOrientation 202 */ 203 public final void setLeftToRight(boolean b) { 204 leftToRight = b; 205 } 206 207 // Accessing the Cursor Location and Extent ***************************** 208 209 /** 210 * Returns the cursor's column. 211 * 212 * @return the cursor's column 213 */ 214 public final int getColumn() { 215 return currentCellConstraints.gridX; 216 } 217 218 /** 219 * Sets the cursor to the given column. 220 * 221 * @param column the cursor's new column index 222 */ 223 public final void setColumn(int column) { 224 currentCellConstraints.gridX = column; 225 } 226 227 /** 228 * Returns the cursor's row. 229 * 230 * @return the cursor's row 231 */ 232 public final int getRow() { 233 return currentCellConstraints.gridY; 234 } 235 236 /** 237 * Sets the cursor to the given row. 238 * 239 * @param row the cursor's new row index 240 */ 241 public final void setRow(int row) { 242 currentCellConstraints.gridY = row; 243 } 244 245 /** 246 * Sets the cursor's column span. 247 * 248 * @param columnSpan the cursor's new column span (grid width) 249 */ 250 public final void setColumnSpan(int columnSpan) { 251 currentCellConstraints.gridWidth = columnSpan; 252 } 253 254 /** 255 * Sets the cursor's row span. 256 * 257 * @param rowSpan the cursor's new row span (grid height) 258 */ 259 public final void setRowSpan(int rowSpan) { 260 currentCellConstraints.gridHeight = rowSpan; 261 } 262 263 /** 264 * Sets the cursor's origin to the given column and row. 265 * 266 * @param column the new column index 267 * @param row the new row index 268 */ 269 public final void setOrigin(int column, int row) { 270 setColumn(column); 271 setRow(row); 272 } 273 274 /** 275 * Sets the cursor's extent to the given column span and row span. 276 * 277 * @param columnSpan the new column span (grid width) 278 * @param rowSpan the new row span (grid height) 279 */ 280 public final void setExtent(int columnSpan, int rowSpan) { 281 setColumnSpan(columnSpan); 282 setRowSpan(rowSpan); 283 } 284 285 /** 286 * Sets the cell bounds (location and extent) to the given column, row, 287 * column span and row span. 288 * 289 * @param column the new column index (grid x) 290 * @param row the new row index (grid y) 291 * @param columnSpan the new column span (grid width) 292 * @param rowSpan the new row span (grid height) 293 */ 294 public final void setBounds(int column, int row, int columnSpan, 295 int rowSpan) { 296 setColumn(column); 297 setRow(row); 298 setColumnSpan(columnSpan); 299 setRowSpan(rowSpan); 300 } 301 302 /** 303 * Moves to the next column, does the same as #nextColumn(1). 304 */ 305 public final void nextColumn() { 306 nextColumn(1); 307 } 308 309 /** 310 * Moves to the next column. 311 * 312 * @param columns number of columns to move 313 */ 314 public final void nextColumn(int columns) { 315 currentCellConstraints.gridX += columns * getColumnIncrementSign(); 316 } 317 318 /** 319 * Increases the row by one; does the same as #nextRow(1). 320 */ 321 public final void nextRow() { 322 nextRow(1); 323 } 324 325 /** 326 * Increases the row by the specified rows. 327 * 328 * @param rows number of rows to move 329 */ 330 public final void nextRow(int rows) { 331 currentCellConstraints.gridY += rows; 332 } 333 334 /** 335 * Moves to the next line: increases the row and resets the column; 336 * does the same as #nextLine(1). 337 */ 338 public final void nextLine() { 339 nextLine(1); 340 } 341 342 /** 343 * Moves the cursor down several lines: increases the row by the 344 * specified number of lines and sets the cursor to the leading column. 345 * 346 * @param lines number of rows to move 347 */ 348 public final void nextLine(int lines) { 349 nextRow(lines); 350 setColumn(getLeadingColumn()); 351 } 352 353 // Form Constraints Alignment ******************************************* 354 355 /** 356 * Sets the horizontal alignment. 357 * 358 * @param alignment the new horizontal alignment 359 */ 360 public final void setHAlignment(CellConstraints.Alignment alignment) { 361 currentCellConstraints.hAlign = alignment; 362 } 363 364 /** 365 * Sets the vertical alignment. 366 * 367 * @param alignment the new vertical alignment 368 */ 369 public final void setVAlignment(CellConstraints.Alignment alignment) { 370 currentCellConstraints.vAlign = alignment; 371 } 372 373 /** 374 * Sets the horizontal and vertical alignment. 375 * 376 * @param hAlign the new horizontal alignment 377 * @param vAlign the new vertical alignment 378 */ 379 public final void setAlignment(CellConstraints.Alignment hAlign, 380 CellConstraints.Alignment vAlign) { 381 setHAlignment(hAlign); 382 setVAlignment(vAlign); 383 } 384 385 // Appending Columns ****************************************************** 386 387 /** 388 * Appends the given column specification to the builder's layout. 389 * 390 * @param columnSpec the column specification object to append 391 * 392 * @see #appendColumn(String) 393 */ 394 public final void appendColumn(ColumnSpec columnSpec) { 395 getLayout().appendColumn(columnSpec); 396 } 397 398 /** 399 * Appends a column specification to the builder's layout 400 * that represents the given string encoding. 401 * 402 * @param encodedColumnSpec the column specification to append in encoded form 403 * 404 * @see #appendColumn(ColumnSpec) 405 */ 406 public final void appendColumn(String encodedColumnSpec) { 407 appendColumn(new ColumnSpec(encodedColumnSpec)); 408 } 409 410 /** 411 * Appends a glue column. 412 * 413 * @see #appendLabelComponentsGapColumn() 414 * @see #appendRelatedComponentsGapColumn() 415 * @see #appendUnrelatedComponentsGapColumn() 416 */ 417 public final void appendGlueColumn() { 418 appendColumn(FormFactory.GLUE_COLSPEC); 419 } 420 421 /** 422 * Appends a column that is the default gap between a label and 423 * its associated component. 424 * 425 * @since 1.0.3 426 * 427 * @see #appendGlueColumn() 428 * @see #appendRelatedComponentsGapColumn() 429 * @see #appendUnrelatedComponentsGapColumn() 430 */ 431 public final void appendLabelComponentsGapColumn() { 432 appendColumn(FormFactory.LABEL_COMPONENT_GAP_COLSPEC); 433 } 434 435 /** 436 * Appends a column that is the default gap for related components. 437 * 438 * @see #appendGlueColumn() 439 * @see #appendLabelComponentsGapColumn() 440 * @see #appendUnrelatedComponentsGapColumn() 441 */ 442 public final void appendRelatedComponentsGapColumn() { 443 appendColumn(FormFactory.RELATED_GAP_COLSPEC); 444 } 445 446 /** 447 * Appends a column that is the default gap for unrelated components. 448 * 449 * @see #appendGlueColumn() 450 * @see #appendLabelComponentsGapColumn() 451 * @see #appendRelatedComponentsGapColumn() 452 */ 453 public final void appendUnrelatedComponentsGapColumn() { 454 appendColumn(FormFactory.UNRELATED_GAP_COLSPEC); 455 } 456 457 // Appending Rows ******************************************************** 458 459 /** 460 * Appends the given row specification to the builder's layout. 461 * 462 * @param rowSpec the row specification object to append 463 * 464 * @see #appendRow(String) 465 */ 466 public final void appendRow(RowSpec rowSpec) { 467 getLayout().appendRow(rowSpec); 468 } 469 470 /** 471 * Appends a row specification to the builder's layout that represents 472 * the given string encoding. 473 * 474 * @param encodedRowSpec the row specification to append in encoded form 475 * 476 * @see #appendRow(RowSpec) 477 */ 478 public final void appendRow(String encodedRowSpec) { 479 appendRow(new RowSpec(encodedRowSpec)); 480 } 481 482 /** 483 * Appends a glue row. 484 * 485 * @see #appendRelatedComponentsGapRow() 486 * @see #appendUnrelatedComponentsGapRow() 487 * @see #appendParagraphGapRow() 488 */ 489 public final void appendGlueRow() { 490 appendRow(FormFactory.GLUE_ROWSPEC); 491 } 492 493 /** 494 * Appends a row that is the default gap for related components. 495 * 496 * @see #appendGlueRow() 497 * @see #appendUnrelatedComponentsGapRow() 498 * @see #appendParagraphGapRow() 499 */ 500 public final void appendRelatedComponentsGapRow() { 501 appendRow(FormFactory.RELATED_GAP_ROWSPEC); 502 } 503 504 /** 505 * Appends a row that is the default gap for unrelated components. 506 * 507 * @see #appendGlueRow() 508 * @see #appendRelatedComponentsGapRow() 509 * @see #appendParagraphGapRow() 510 */ 511 public final void appendUnrelatedComponentsGapRow() { 512 appendRow(FormFactory.UNRELATED_GAP_ROWSPEC); 513 } 514 515 /** 516 * Appends a row that is the default gap for paragraphs. 517 * 518 * @since 1.0.3 519 * 520 * @see #appendGlueRow() 521 * @see #appendRelatedComponentsGapRow() 522 * @see #appendUnrelatedComponentsGapRow() 523 */ 524 public final void appendParagraphGapRow() { 525 appendRow(FormFactory.PARAGRAPH_GAP_ROWSPEC); 526 } 527 528 // Adding Components **************************************************** 529 530 /** 531 * Adds a component to the panel using the given cell constraints. 532 * 533 * @param component the component to add 534 * @param cellConstraints the component's cell constraints 535 * @return the added component 536 */ 537 public final Component add(Component component, 538 CellConstraints cellConstraints) { 539 container.add(component, cellConstraints); 540 return component; 541 } 542 543 /** 544 * Adds a component to the panel using the given encoded cell constraints. 545 * 546 * @param component the component to add 547 * @param encodedCellConstraints the component's encoded cell constraints 548 * @return the added component 549 */ 550 public final Component add(Component component, 551 String encodedCellConstraints) { 552 container.add(component, new CellConstraints(encodedCellConstraints)); 553 return component; 554 } 555 556 /** 557 * Adds a component to the container using the default cell constraints. 558 * Note that when building from left to right, this method won't adjust 559 * the cell constraints if the column span is larger than 1. In this case 560 * you should use {@link #add(Component, CellConstraints)} with a cell 561 * constraints object created by {@link #createLeftAdjustedConstraints(int)}. 562 * 563 * @param component the component to add 564 * @return the added component 565 * 566 * @see #add(Component, CellConstraints) 567 * @see #createLeftAdjustedConstraints(int) 568 */ 569 public final Component add(Component component) { 570 add(component, currentCellConstraints); 571 return component; 572 } 573 574 // Misc ***************************************************************** 575 576 /** 577 * Returns the CellConstraints object that is used as a cursor and 578 * holds the current column span and row span. 579 * 580 * @return the builder's current {@link CellConstraints} object 581 */ 582 protected final CellConstraints cellConstraints() { 583 return currentCellConstraints; 584 } 585 586 /** 587 * Returns the index of the leading column.<p> 588 * 589 * Subclasses may override this method, for example, if the form 590 * has a leading gap column that should not be filled with components. 591 * 592 * @return the leading column 593 */ 594 protected int getLeadingColumn() { 595 return isLeftToRight() ? 1 : getColumnCount(); 596 } 597 598 /** 599 * Returns the sign (-1 or 1) used to increment the cursor's column 600 * when moving to the next column. 601 * 602 * @return -1 for right-to-left, 1 for left-to-right 603 */ 604 protected final int getColumnIncrementSign() { 605 return isLeftToRight() ? 1 : -1; 606 } 607 608 /** 609 * Creates and returns a <code>CellConstraints</code> object at 610 * the current cursor position that uses the given column span 611 * and is adjusted to the left. Useful when building from right to left. 612 * 613 * @param columnSpan the column span to be used in the constraints 614 * @return CellConstraints adjusted to the left hand side 615 */ 616 protected final CellConstraints createLeftAdjustedConstraints( 617 int columnSpan) { 618 int firstColumn = isLeftToRight() ? getColumn() 619 : getColumn() + 1 - columnSpan; 620 return new CellConstraints(firstColumn, getRow(), columnSpan, 621 cellConstraints().gridHeight); 622 } 623 624}