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.util.ResourceBundle; 035 036import javax.swing.JComponent; 037import javax.swing.JLabel; 038import javax.swing.JPanel; 039 040import com.jgoodies.forms.factories.FormFactory; 041import com.jgoodies.forms.layout.ConstantSize; 042import com.jgoodies.forms.layout.FormLayout; 043import com.jgoodies.forms.layout.RowSpec; 044 045/** 046 * Provides a means to build form-oriented panels quickly and consistently 047 * using the {@link FormLayout}. This builder combines frequently used 048 * panel building steps: add a new row, add a label, proceed to the next 049 * data column, then add a component.<p> 050 * 051 * The extra value lies in the <code>#append</code> methods that 052 * append gap rows and component rows if necessary and then add 053 * the given components. They are built upon the superclass behavior 054 * <code>#appendRow</code> and the set of <code>#add</code> methods. 055 * A set of component appenders allows to add a textual label and 056 * associated component in a single step.<p> 057 * 058 * This builder can map resource keys to internationalized (i15d) texts 059 * when creating text labels, titles and titled separators. Therefore 060 * you must specify a <code>ResourceBundle</code> in the constructor. 061 * The builder methods throw an <code>IllegalStateException</code> if one 062 * of the mapping builder methods is invoked and no bundle has been set.<p> 063 * 064 * You can configure the build process by setting a leading column, 065 * enabling the row grouping and by modifying the gaps between normal 066 * lines and between paragraphs. The leading column will be honored 067 * if the cursor proceeds to the next row. All appended components 068 * start in the specified lead column, except appended separators that 069 * span all columns.<p> 070 * 071 * It is tempting to use the DefaultFormBuilder all the time and 072 * to let it add rows automatically. Use a simpler style if it increases 073 * the code readability. Explicit row specifications and cell constraints 074 * make your layout easier to understand - but harder to maintain. 075 * See also the accompanying tutorial sources and the Tips & Tricks 076 * that are part of the Forms documentation.<p> 077 * 078 * Sometimes a form consists of many standardized rows but has a few 079 * rows that require a customization. The DefaultFormBuilder can do everything 080 * that the superclasses {@link com.jgoodies.forms.builder.AbstractFormBuilder} 081 * and {@link com.jgoodies.forms.builder.PanelBuilder} can do; 082 * among other things: appending new rows and moving the cursor. 083 * Again, ask yourself if the DefaultFormBuilder is the appropriate builder. 084 * As a rule of thumb you should have more components than builder commands. 085 * There are different ways to add custom rows. Find below example code 086 * that presents and compares the pros and cons of three approaches.<p> 087 * 088 * The texts used in methods <code>#append(String, ...)</code> and 089 * <code>#appendTitle(String)</code> as well as the localized texts used in 090 * methods <code>#appendI15d</code> and <code>#appendI15dTitle</code> 091 * can contain an optional mnemonic marker. The mnemonic and mnemonic index 092 * are indicated by a single ampersand (<tt>&</tt>). 093 * For example <tt>"&Save"</tt>, or 094 * <tt>"Save &as"</tt>. To use the ampersand itself, 095 * duplicate it, for example <tt>"Look&&Feel"</tt>.<p> 096 * 097 * <strong>Example:</strong> 098 * <pre> 099 * public void build() { 100 * FormLayout layout = new FormLayout( 101 * "right:max(40dlu;pref), 3dlu, 80dlu, 7dlu, " // 1st major column 102 * + "right:max(40dlu;pref), 3dlu, 80dlu", // 2nd major column 103 * ""); // add rows dynamically 104 * DefaultFormBuilder builder = new DefaultFormBuilder(layout); 105 * builder.setDefaultDialogBorder(); 106 * 107 * builder.appendSeparator("Flange"); 108 * 109 * builder.append("Identifier", identifierField); 110 * builder.nextLine(); 111 * 112 * builder.append("PTI [kW]", new JTextField()); 113 * builder.append("Power [kW]", new JTextField()); 114 * 115 * builder.append("s [mm]", new JTextField()); 116 * builder.nextLine(); 117 * 118 * builder.appendSeparator("Diameters"); 119 * 120 * builder.append("da [mm]", new JTextField()); 121 * builder.append("di [mm]", new JTextField()); 122 * 123 * builder.append("da2 [mm]", new JTextField()); 124 * builder.append("di2 [mm]", new JTextField()); 125 * 126 * builder.append("R [mm]", new JTextField()); 127 * builder.append("D [mm]", new JTextField()); 128 * 129 * builder.appendSeparator("Criteria"); 130 * 131 * builder.append("Location", buildLocationComboBox()); 132 * builder.append("k-factor", new JTextField()); 133 * 134 * builder.appendSeparator("Bolts"); 135 * 136 * builder.append("Material", ViewerUIFactory.buildMaterialComboBox()); 137 * builder.nextLine(); 138 * 139 * builder.append("Numbers", new JTextField()); 140 * builder.nextLine(); 141 * 142 * builder.append("ds [mm]", new JTextField()); 143 * } 144 * </pre><p> 145 * 146 * <strong>Custom Row Example:</strong> 147 * <pre> 148 * public JComponent buildPanel() { 149 * initComponents(); 150 * 151 * FormLayout layout = new FormLayout( 152 * "right:pref, 3dlu, default:grow", 153 * ""); 154 * DefaultFormBuilder builder = new DefaultFormBuilder(layout); 155 * builder.setDefaultDialogBorder(); 156 * builder.setRowGroupingEnabled(true); 157 * 158 * CellConstraints cc = new CellConstraints(); 159 * 160 * // In this approach, we add a gap and a custom row. 161 * // The advantage of this approach is, that we can express 162 * // the row spec and comment area cell constraints freely. 163 * // The disadvantage is the misalignment of the leading label. 164 * // Also the row's height may be inconsistent with other rows. 165 * builder.appendSeparator("Single Custom Row"); 166 * builder.append("Name", name1Field); 167 * builder.appendRow(builder.getLineGapSpec()); 168 * builder.appendRow(new RowSpec("top:31dlu")); // Assumes line is 14, gap is 3 169 * builder.nextLine(2); 170 * builder.append("Comment"); 171 * builder.add(new JScrollPane(comment1Area), 172 * cc.xy(builder.getColumn(), builder.getRow(), "fill, fill")); 173 * builder.nextLine(); 174 * 175 * // In this approach, we append a standard row with gap before it. 176 * // The advantage is, that the leading label is aligned well. 177 * // The disadvantage is that the comment area now spans 178 * // multiple cells and is slightly less flexible. 179 * // Also the row's height may be inconsistent with other rows. 180 * builder.appendSeparator("Standard + Custom Row"); 181 * builder.append("Name", name2Field); 182 * builder.append("Comment"); 183 * builder.appendRow(new RowSpec("17dlu")); // Assumes line is 14, gap is 3 184 * builder.add(new JScrollPane(comment2Area), 185 * cc.xywh(builder.getColumn(), builder.getRow(), 1, 2)); 186 * builder.nextLine(2); 187 * 188 * // In this approach, we append two standard rows with associated gaps. 189 * // The advantage is, that the leading label is aligned well, 190 * // and the height is consistent with other rows. 191 * // The disadvantage is that the comment area now spans 192 * // multiple cells and is slightly less flexible. 193 * builder.appendSeparator("Two Standard Rows"); 194 * builder.append("Name", name3Field); 195 * builder.append("Comment"); 196 * builder.nextLine(); 197 * builder.append(""); 198 * builder.nextRow(-2); 199 * builder.add(new JScrollPane(comment3Area), 200 * cc.xywh(builder.getColumn(), builder.getRow(), 1, 3)); 201 * 202 * return builder.getPanel(); 203 * } 204 * </pre><p> 205 * 206 * TODO: Consider adding a method for appending a component that spans the 207 * remaining columns in the current row. Method name candidates are 208 * <code>#appendFullSpan</code> and <code>#appendRemaining</code>. 209 * 210 * @author Karsten Lentzsch 211 * @version $Revision$ 212 * @since 1.0.3 213 * 214 * @see com.jgoodies.forms.builder.AbstractFormBuilder 215 * @see com.jgoodies.forms.factories.FormFactory 216 * @see com.jgoodies.forms.layout.FormLayout 217 */ 218public final class DefaultFormBuilder extends I15dPanelBuilder { 219 220 /** 221 * Holds the row specification that is reused to describe 222 * the constant gaps between component lines. 223 */ 224 private RowSpec lineGapSpec = FormFactory.LINE_GAP_ROWSPEC; 225 226 /** 227 * Holds the row specification that describes the constant gaps 228 * between paragraphs. 229 */ 230 private RowSpec paragraphGapSpec = FormFactory.PARAGRAPH_GAP_ROWSPEC; 231 232 /** 233 * Holds the offset of the leading column - often 0 or 1. 234 * 235 * @see #getLeadingColumnOffset() 236 * @see #setLeadingColumnOffset(int) 237 * @see #getLeadingColumn() 238 */ 239 private int leadingColumnOffset = 0; 240 241 /** 242 * Determines whether new data rows are being grouped or not. 243 * 244 * @see #isRowGroupingEnabled() 245 * @see #setRowGroupingEnabled(boolean) 246 */ 247 private boolean rowGroupingEnabled = false; 248 249 // Instance Creation **************************************************** 250 251 /** 252 * Constructs an instance of <code>DefaultFormBuilder</code> for the given 253 * layout. 254 * 255 * @param layout the <code>FormLayout</code> to be used 256 */ 257 public DefaultFormBuilder(FormLayout layout) { 258 this(new JPanel(null), layout); 259 } 260 261 /** 262 * Constructs an instance of <code>DefaultFormBuilder</code> for the given 263 * panel and layout. 264 * 265 * @param layout the <code>FormLayout</code> to be used 266 * @param panel the layout container 267 */ 268 public DefaultFormBuilder(FormLayout layout, JPanel panel) { 269 this(panel, layout, null); 270 } 271 272 /** 273 * Constructs an instance of <code>DefaultFormBuilder</code> for the given 274 * layout and resource bundle. 275 * 276 * @param layout the <code>FormLayout</code> to be used 277 * @param bundle the <code>ResourceBundle</code> used to lookup i15d 278 * strings 279 */ 280 public DefaultFormBuilder(FormLayout layout, ResourceBundle bundle) { 281 this(new JPanel(null), layout, bundle); 282 } 283 284 /** 285 * Constructs an instance of <code>DefaultFormBuilder</code> for the given 286 * panel, layout and resource bundle. 287 * 288 * @param layout the <code>FormLayout</code> to be used 289 * @param panel the layout container 290 * @param bundle the <code>ResourceBundle</code> used to lookup i15d 291 * strings 292 */ 293 public DefaultFormBuilder(FormLayout layout, ResourceBundle bundle, 294 JPanel panel) { 295 super(layout, bundle, panel); 296 } 297 298 /** 299 * Constructs an instance of <code>DefaultFormBuilder</code> for the given 300 * panel and layout. 301 * 302 * @param panel the layout container 303 * @param layout the <code>FormLayout</code> to be used 304 * 305 * @deprecated Replaced by {@link #DefaultFormBuilder(FormLayout, JPanel)}. 306 */ 307 @Deprecated 308 public DefaultFormBuilder(JPanel panel, FormLayout layout) { 309 this(layout, null, panel); 310 } 311 312 /** 313 * Constructs an instance of <code>DefaultFormBuilder</code> for the given 314 * panel, layout and resource bundle. 315 * 316 * @param panel the layout container 317 * @param layout the <code>FormLayout</code> to be used 318 * @param bundle the <code>ResourceBundle</code> used to lookup i15d 319 * strings 320 * 321 * @deprecated Replaced by {@link #DefaultFormBuilder(FormLayout, ResourceBundle, JPanel)}. 322 */ 323 @Deprecated 324 public DefaultFormBuilder(JPanel panel, FormLayout layout, 325 ResourceBundle bundle) { 326 super(layout, bundle, panel); 327 } 328 329 // Settings Gap Sizes *************************************************** 330 331 /** 332 * Returns the row specification that is used to separate component lines. 333 * 334 * @return the <code>RowSpec</code> that is used to separate lines 335 */ 336 public RowSpec getLineGapSpec() { 337 return lineGapSpec; 338 } 339 340 /** 341 * Sets the size of gaps between component lines using the given 342 * constant size.<p> 343 * 344 * <strong>Examples:</strong><pre> 345 * builder.setLineGapSize(Sizes.ZERO); 346 * builder.setLineGapSize(Sizes.DLUY9); 347 * builder.setLineGapSize(Sizes.pixel(1)); 348 * </pre> 349 * 350 * @param lineGapSize the <code>ConstantSize</code> that describes 351 * the size of the gaps between component lines 352 */ 353 public void setLineGapSize(ConstantSize lineGapSize) { 354 RowSpec rowSpec = FormFactory.createGapRowSpec(lineGapSize); 355 this.lineGapSpec = rowSpec; 356 } 357 358 /** 359 * Sets the size of gaps between paragraphs using the given 360 * constant size.<p> 361 * 362 * <strong>Examples:</strong><pre> 363 * builder.setParagraphGapSize(Sizes.DLUY14); 364 * builder.setParagraphGapSize(Sizes.dluY(22)); 365 * builder.setParagraphGapSize(Sizes.pixel(42)); 366 * </pre> 367 * 368 * @param paragraphGapSize the <code>ConstantSize</code> that describes 369 * the size of the gaps between paragraphs 370 */ 371 public void setParagraphGapSize(ConstantSize paragraphGapSize) { 372 RowSpec rowSpec = FormFactory.createGapRowSpec(paragraphGapSize); 373 this.paragraphGapSpec = rowSpec; 374 } 375 376 /** 377 * Returns the offset of the leading column, often 0 or 1. 378 * 379 * @return the offset of the leading column 380 */ 381 public int getLeadingColumnOffset() { 382 return leadingColumnOffset; 383 } 384 385 /** 386 * Sets the offset of the leading column, often 0 or 1. 387 * 388 * @param columnOffset the new offset of the leading column 389 */ 390 public void setLeadingColumnOffset(int columnOffset) { 391 this.leadingColumnOffset = columnOffset; 392 } 393 394 /** 395 * Returns whether new data rows are being grouped or not. 396 * 397 * @return true indicates grouping enabled, false disabled 398 */ 399 public boolean isRowGroupingEnabled() { 400 return rowGroupingEnabled; 401 } 402 403 /** 404 * Enables or disables the grouping of new data rows. 405 * 406 * @param enabled indicates grouping enabled, false disabled 407 */ 408 public void setRowGroupingEnabled(boolean enabled) { 409 rowGroupingEnabled = enabled; 410 } 411 412 // Filling Columns ****************************************************** 413 414 /** 415 * Adds a component to the panel using the default constraints 416 * with a column span of 1. Then proceeds to the next data column. 417 * 418 * @param component the component to add 419 */ 420 public void append(Component component) { 421 append(component, 1); 422 } 423 424 /** 425 * Adds a component to the panel using the default constraints with 426 * the given columnSpan. Proceeds to the next data column. 427 * 428 * @param component the component to append 429 * @param columnSpan the column span used to add 430 */ 431 public void append(Component component, int columnSpan) { 432 ensureCursorColumnInGrid(); 433 ensureHasGapRow(lineGapSpec); 434 ensureHasComponentLine(); 435 436 add(component, createLeftAdjustedConstraints(columnSpan)); 437 nextColumn(columnSpan + 1); 438 } 439 440 /** 441 * Adds two components to the panel; each component will span a single 442 * data column. Proceeds to the next data column. 443 * 444 * @param c1 the first component to add 445 * @param c2 the second component to add 446 */ 447 public void append(Component c1, Component c2) { 448 append(c1); 449 append(c2); 450 } 451 452 /** 453 * Adds three components to the panel; each component will span a single 454 * data column. Proceeds to the next data column. 455 * 456 * @param c1 the first component to add 457 * @param c2 the second component to add 458 * @param c3 the third component to add 459 */ 460 public void append(Component c1, Component c2, Component c3) { 461 append(c1); 462 append(c2); 463 append(c3); 464 } 465 466 // Appending Labels with optional components ------------------------------ 467 468 /** 469 * Adds a text label to the panel and proceeds to the next column. 470 * 471 * @param textWithMnemonic the label's text - may mark a mnemonic 472 * @return the added label 473 */ 474 public JLabel append(String textWithMnemonic) { 475 JLabel label = getComponentFactory().createLabel(textWithMnemonic); 476 append(label); 477 return label; 478 } 479 480 /** 481 * Adds a text label and component to the panel. 482 * Then proceeds to the next data column.<p> 483 * 484 * The created label is labelling the given component; so the component 485 * gets the focus if the (optional) label mnemonic is pressed. 486 * 487 * @param textWithMnemonic the label's text - may mark a mnemonic 488 * @param component the component to add 489 * @return the added label 490 */ 491 public JLabel append(String textWithMnemonic, Component component) { 492 return append(textWithMnemonic, component, 1); 493 } 494 495 /** 496 * Adds a text label and component to the panel; the component will span 497 * the specified number columns. Proceeds to the next data column, 498 * and goes to the next line if the boolean flag is set.<p> 499 * 500 * The created label is labelling the given component; so the component 501 * gets the focus if the (optional) label mnemonic is pressed. 502 * 503 * @param textWithMnemonic the label's text - may mark a mnemonic 504 * @param c the component to add 505 * @param nextLine true forces a next line 506 * @return the added label 507 * @see JLabel#setLabelFor(java.awt.Component) 508 */ 509 public JLabel append(String textWithMnemonic, Component c, 510 boolean nextLine) { 511 JLabel label = append(textWithMnemonic, c); 512 if (nextLine) { 513 nextLine(); 514 } 515 return label; 516 } 517 518 /** 519 * Adds a text label and component to the panel; the component will span 520 * the specified number columns. Proceeds to the next data column.<p> 521 * 522 * The created label is labelling the given component; so the component 523 * gets the focus if the (optional) label mnemonic is pressed. 524 * 525 * @param textWithMnemonic the label's text - may mark a mnemonic 526 * @param c the component to add 527 * @param columnSpan number of columns the component shall span 528 * @return the added label 529 * @see JLabel#setLabelFor(java.awt.Component) 530 */ 531 public JLabel append(String textWithMnemonic, Component c, int columnSpan) { 532 JLabel label = append(textWithMnemonic); 533 label.setLabelFor(c); 534 append(c, columnSpan); 535 return label; 536 } 537 538 /** 539 * Adds a text label and two components to the panel; each component 540 * will span a single column. Proceeds to the next data column.<p> 541 * 542 * The created label is labelling the first component; so the component 543 * gets the focus if the (optional) label mnemonic is pressed. 544 * 545 * @param textWithMnemonic the label's text - may mark a mnemonic 546 * @param c1 the first component to add 547 * @param c2 the second component to add 548 * @return the added label 549 */ 550 public JLabel append(String textWithMnemonic, Component c1, Component c2) { 551 JLabel label = append(textWithMnemonic, c1); 552 append(c2); 553 return label; 554 } 555 556 /** 557 * Adds a text label and two components to the panel; each component 558 * will span a single column. Proceeds to the next data column.<p> 559 * 560 * The created label is labelling the first component; so the component 561 * gets the focus if the (optional) label mnemonic is pressed. 562 * 563 * @param textWithMnemonic the label's text - may mark a mnemonic 564 * @param c1 the first component to add 565 * @param c2 the second component to add 566 * @param colSpan the column span for the second component 567 * @return the created label 568 */ 569 public JLabel append(String textWithMnemonic, Component c1, Component c2, 570 int colSpan) { 571 JLabel label = append(textWithMnemonic, c1); 572 append(c2, colSpan); 573 return label; 574 } 575 576 /** 577 * Adds a text label and three components to the panel; each component 578 * will span a single column. Proceeds to the next data column.<p> 579 * 580 * The created label is labelling the first component; so the component 581 * gets the focus if the (optional) label mnemonic is pressed. 582 * 583 * @param textWithMnemonic the label's text - may mark a mnemonic 584 * @param c1 the first component to add 585 * @param c2 the second component to add 586 * @param c3 the third component to add 587 * @return the added label 588 */ 589 public JLabel append(String textWithMnemonic, Component c1, Component c2, 590 Component c3) { 591 JLabel label = append(textWithMnemonic, c1, c2); 592 append(c3); 593 return label; 594 } 595 596 /** 597 * Adds a text label and four components to the panel; each component 598 * will span a single column. Proceeds to the next data column.<p> 599 * 600 * The created label is labelling the first component; so the component 601 * gets the focus if the (optional) label mnemonic is pressed. 602 * 603 * @param textWithMnemonic the label's text - may mark a mnemonic 604 * @param c1 the first component to add 605 * @param c2 the second component to add 606 * @param c3 the third component to add 607 * @param c4 the fourth component to add 608 * @return the added label 609 */ 610 public JLabel append(String textWithMnemonic, Component c1, Component c2, 611 Component c3, Component c4) { 612 JLabel label = append(textWithMnemonic, c1, c2, c3); 613 append(c4); 614 return label; 615 } 616 617 // Appending internationalized labels with optional components ------------ 618 619 /** 620 * Adds an internationalized (i15d) text label to the panel using 621 * the given resource key and proceeds to the next column. 622 * 623 * @param resourceKey the resource key for the the label's text 624 * @return the added label 625 */ 626 public JLabel appendI15d(String resourceKey) { 627 return append(getI15dString(resourceKey)); 628 } 629 630 /** 631 * Adds an internationalized (i15d) text label and component 632 * to the panel. Then proceeds to the next data column.<p> 633 * 634 * The created label is labelling the given component; so the component 635 * gets the focus if the (optional) label mnemonic is pressed. 636 * 637 * @param resourceKey the resource key for the text to add 638 * @param component the component to add 639 * @return the added label 640 */ 641 public JLabel appendI15d(String resourceKey, Component component) { 642 return append(getI15dString(resourceKey), component, 1); 643 } 644 645 /** 646 * Adds an internationalized (i15d) text label and component 647 * to the panel. Then proceeds to the next data column. 648 * Goes to the next line if the boolean flag is set.<p> 649 * 650 * The created label is labelling the first component; so the component 651 * gets the focus if the (optional) label mnemonic is pressed. 652 * 653 * @param resourceKey the resource key for the text to add 654 * @param component the component to add 655 * @param nextLine true forces a next line 656 * @return the added label 657 */ 658 public JLabel appendI15d(String resourceKey, Component component, 659 boolean nextLine) { 660 return append(getI15dString(resourceKey), component, nextLine); 661 } 662 663 /** 664 * Adds an internationalized (i15d) text label to the panel using 665 * the given resource key; then proceeds to the next data column 666 * and adds a component with the given column span. 667 * Proceeds to the next data column.<p> 668 * 669 * The created label is labelling the first component; so the component 670 * gets the focus if the (optional) label mnemonic is pressed. 671 * 672 * @param resourceKey the resource key for the text to add 673 * @param c the component to add 674 * @param columnSpan number of columns the component shall span 675 * @return the added label 676 */ 677 public JLabel appendI15d(String resourceKey, Component c, int columnSpan) { 678 return append(getI15dString(resourceKey), c, columnSpan); 679 } 680 681 /** 682 * Adds an internationalized (i15d) text label and two components 683 * to the panel; each component will span a single column. 684 * Proceeds to the next data column.<p> 685 * 686 * The created label is labelling the first component; so the component 687 * gets the focus if the (optional) label mnemonic is pressed. 688 * 689 * @param resourceKey the resource key for the text to add 690 * @param c1 the first component to add 691 * @param c2 the second component to add 692 * @return the added label 693 */ 694 public JLabel appendI15d(String resourceKey, Component c1, Component c2) { 695 return append(getI15dString(resourceKey), c1, c2); 696 } 697 698 /** 699 * Adds an internationalized (i15d) text label and two components 700 * to the panel; each component will span a single column. 701 * Proceeds to the next data column.<p> 702 * 703 * The created label is labelling the first component; so the component 704 * gets the focus if the (optional) label mnemonic is pressed. 705 * 706 * @param resourceKey the resource key for the text to add 707 * @param c1 the first component to add 708 * @param c2 the second component to add 709 * @param colSpan the column span for the second component 710 * @return the added label 711 */ 712 public JLabel appendI15d(String resourceKey, Component c1, Component c2, 713 int colSpan) { 714 return append(getI15dString(resourceKey), c1, c2, colSpan); 715 } 716 717 /** 718 * Adds an internationalized (i15d) text label and three components 719 * to the panel; each component will span a single column. 720 * Proceeds to the next data column.<p> 721 * 722 * The created label is labelling the first component; so the component 723 * gets the focus if the (optional) label mnemonic is pressed. 724 * 725 * @param resourceKey the resource key for the text to add 726 * @param c1 the first component to add 727 * @param c2 the second component to add 728 * @param c3 the third component to add 729 * @return the added label 730 */ 731 public JLabel appendI15d(String resourceKey, Component c1, Component c2, 732 Component c3) { 733 return append(getI15dString(resourceKey), c1, c2, c3); 734 } 735 736 /** 737 * Adds an internationalized (i15d) text label and four components 738 * to the panel; each component will span a single column. 739 * Proceeds to the next data column.<p> 740 * 741 * The created label is labelling the first component; so the component 742 * gets the focus if the (optional) label mnemonic is pressed. 743 * 744 * @param resourceKey the resource key for the text to add 745 * @param c1 the first component to add 746 * @param c2 the second component to add 747 * @param c3 the third component to add 748 * @param c4 the third component to add 749 * @return the added label 750 */ 751 public JLabel appendI15d(String resourceKey, Component c1, Component c2, 752 Component c3, Component c4) { 753 return append(getI15dString(resourceKey), c1, c2, c3, c4); 754 } 755 756 // Adding Titles ---------------------------------------------------------- 757 758 /** 759 * Adds a title label to the panel and proceeds to the next column. 760 * 761 * @param textWithMnemonic the label's text - may mark a mnemonic 762 * @return the added title label 763 */ 764 public JLabel appendTitle(String textWithMnemonic) { 765 JLabel titleLabel = getComponentFactory().createTitle(textWithMnemonic); 766 append(titleLabel); 767 return titleLabel; 768 } 769 770 /** 771 * Adds an internationalized title label to the panel and 772 * proceeds to the next column. 773 * 774 * @param resourceKey the resource key for the title's text 775 * @return the added title label 776 */ 777 public JLabel appendI15dTitle(String resourceKey) { 778 return appendTitle(getI15dString(resourceKey)); 779 } 780 781 // Appending Separators --------------------------------------------------- 782 783 /** 784 * Adds a separator without text that spans all columns. 785 * 786 * @return the added titled separator 787 */ 788 public JComponent appendSeparator() { 789 return appendSeparator(""); 790 } 791 792 /** 793 * Adds a separator with the given text that spans all columns. 794 * 795 * @param text the separator title text 796 * @return the added titled separator 797 */ 798 public JComponent appendSeparator(String text) { 799 ensureCursorColumnInGrid(); 800 ensureHasGapRow(paragraphGapSpec); 801 ensureHasComponentLine(); 802 803 setColumn(super.getLeadingColumn()); 804 int columnSpan = getColumnCount(); 805 setColumnSpan(getColumnCount()); 806 JComponent titledSeparator = addSeparator(text); 807 setColumnSpan(1); 808 nextColumn(columnSpan); 809 return titledSeparator; 810 } 811 812 /** 813 * Appends an internationalized titled separator for 814 * the given resource key that spans all columns. 815 * 816 * @param resourceKey the resource key for the separator title's text 817 * @return the added titled separator 818 */ 819 public JComponent appendI15dSeparator(String resourceKey) { 820 return appendSeparator(getI15dString(resourceKey)); 821 } 822 823 // Overriding Superclass Behavior *************************************** 824 825 /** 826 * Returns the leading column. Unlike the superclass this method 827 * honors the column offset. 828 * 829 * @return the leading column 830 */ 831 @Override 832 protected int getLeadingColumn() { 833 int column = super.getLeadingColumn(); 834 return column + getLeadingColumnOffset() * getColumnIncrementSign(); 835 } 836 837 // Adding Rows ********************************************************** 838 839 /** 840 * Ensures that the cursor is in the grid. In case it's beyond the 841 * form's right hand side, the cursor is moved to the leading column 842 * of the next line. 843 */ 844 private void ensureCursorColumnInGrid() { 845 if (isLeftToRight() && getColumn() > getColumnCount() 846 || !isLeftToRight() && getColumn() < 1) { 847 nextLine(); 848 } 849 } 850 851 /** 852 * Ensures that we have a gap row before the next component row. 853 * Checks if the current row is the given <code>RowSpec</code> 854 * and appends this row spec if necessary. 855 * 856 * @param gapRowSpec the row specification to check for 857 */ 858 private void ensureHasGapRow(RowSpec gapRowSpec) { 859 if (getRow() == 1 || getRow() <= getRowCount()) { 860 return; 861 } 862 863 if (getRow() <= getRowCount()) { 864 RowSpec rowSpec = getCursorRowSpec(); 865 if (rowSpec == gapRowSpec) { 866 return; 867 } 868 } 869 appendRow(gapRowSpec); 870 nextLine(); 871 } 872 873 /** 874 * Ensures that the form has a component row. Adds a component row 875 * if the cursor is beyond the form's bottom. 876 */ 877 private void ensureHasComponentLine() { 878 if (getRow() <= getRowCount()) { 879 return; 880 } 881 appendRow(FormFactory.PREF_ROWSPEC); 882 if (isRowGroupingEnabled()) { 883 getLayout().addGroupedRow(getRow()); 884 } 885 } 886 887 /** 888 * Looks up and returns the row specification of the current row. 889 * 890 * @return the row specification of the current row 891 */ 892 private RowSpec getCursorRowSpec() { 893 return getLayout().getRowSpec(getRow()); 894 } 895 896}