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; 034 035import javax.swing.JComponent; 036import javax.swing.JLabel; 037import javax.swing.JPanel; 038import javax.swing.SwingConstants; 039import javax.swing.border.Border; 040 041import com.jgoodies.forms.factories.Borders; 042import com.jgoodies.forms.factories.ComponentFactory; 043import com.jgoodies.forms.factories.DefaultComponentFactory; 044import com.jgoodies.forms.layout.CellConstraints; 045import com.jgoodies.forms.layout.FormLayout; 046 047/** 048 * An general purpose panel builder that uses the {@link FormLayout} 049 * to lay out <code>JPanel</code>s. It provides convenience methods 050 * to set a default border and to add labels, titles and titled separators.<p> 051 * 052 * The PanelBuilder is the working horse for layouts when more specialized 053 * builders like the {@link ButtonBarBuilder} or {@link DefaultFormBuilder} 054 * are inappropriate.<p> 055 * 056 * The Forms tutorial includes several examples that present and compare 057 * different style to build with the PanelBuilder: static row numbers 058 * vs. row variable, explicit CellConstraints vs. builder cursor, 059 * static rows vs. dynamically added rows. Also, you may check out the 060 * Tips & Tricks section of the Forms HTML documentation.<p> 061 * 062 * The text arguments passed to the methods <code>#addLabel</code>, 063 * <code>#addTitle</code>, and <code>#addSeparator</code> can contain 064 * an optional mnemonic marker. The mnemonic and mnemonic index 065 * are indicated by a single ampersand (<tt>&</tt>). For example 066 * <tt>"&Save"</tt>, or <tt>"Save &as"</tt>. 067 * To use the ampersand itself duplicate it, for example 068 * <tt>"Look&&Feel"</tt>.<p> 069 * 070 * <strong>Example:</strong><br> 071 * This example creates a panel with 3 columns and 3 rows. 072 * <pre> 073 * FormLayout layout = new FormLayout( 074 * "right:pref, 6dlu, 50dlu, 4dlu, default", // columns 075 * "pref, 3dlu, pref, 3dlu, pref"); // rows 076 * 077 * PanelBuilder builder = new PanelBuilder(layout); 078 * CellConstraints cc = new CellConstraints(); 079 * builder.addLabel("&Title", cc.xy (1, 1)); 080 * builder.add(new JTextField(), cc.xywh(3, 1, 3, 1)); 081 * builder.addLabel("&Price", cc.xy (1, 3)); 082 * builder.add(new JTextField(), cc.xy (3, 3)); 083 * builder.addLabel("&Author", cc.xy (1, 5)); 084 * builder.add(new JTextField(), cc.xy (3, 5)); 085 * builder.add(new JButton("..."), cc.xy (5, 5)); 086 * return builder.getPanel(); 087 * </pre> 088 * 089 * @author Karsten Lentzsch 090 * @version $Revision$ 091 * 092 * @see com.jgoodies.forms.factories.ComponentFactory 093 * @see I15dPanelBuilder 094 * @see DefaultFormBuilder 095 */ 096public class PanelBuilder extends AbstractFormBuilder { 097 098 /** 099 * Refers to a factory that is used to create labels, 100 * titles and paragraph separators. 101 */ 102 private ComponentFactory componentFactory; 103 104 // Instance Creation **************************************************** 105 106 /** 107 * Constructs an instance of <code>PanelBuilder</code> for the given 108 * layout. Uses an instance of <code>JPanel</code> as layout container 109 * with the given layout as layout manager. 110 * 111 * @param layout the FormLayout to use 112 */ 113 public PanelBuilder(FormLayout layout) { 114 this(layout, new JPanel(null)); 115 } 116 117 /** 118 * Constructs an instance of <code>PanelBuilder</code> for the given 119 * FormLayout and layout container. 120 * 121 * @param layout the FormLayout to use 122 * @param panel the layout container to build on 123 */ 124 public PanelBuilder(FormLayout layout, JPanel panel) { 125 super(layout, panel); 126 } 127 128 /** 129 * Constructs an instance of <code>PanelBuilder</code> for the given 130 * panel and layout. 131 * 132 * @param panel the layout container to build on 133 * @param layout the form layout to use 134 * 135 * @deprecated Replaced by {@link #PanelBuilder(FormLayout, JPanel)}. 136 */ 137 @Deprecated 138 public PanelBuilder(JPanel panel, FormLayout layout) { 139 super(layout, panel); 140 } 141 142 // Accessors ************************************************************ 143 144 /** 145 * Returns the panel used to build the form. 146 * 147 * @return the panel used by this builder to build the form 148 */ 149 public final JPanel getPanel() { 150 return (JPanel) getContainer(); 151 } 152 153 // Borders ************************************************************** 154 155 /** 156 * Sets the panel's border. 157 * 158 * @param border the border to set 159 */ 160 public final void setBorder(Border border) { 161 getPanel().setBorder(border); 162 } 163 164 /** 165 * Sets the default dialog border. 166 * 167 * @see Borders 168 */ 169 public final void setDefaultDialogBorder() { 170 setBorder(Borders.DIALOG_BORDER); 171 } 172 173 // Adding Labels ********************************************************** 174 175 /** 176 * Adds a textual label to the form using the default constraints. 177 * 178 * <pre> 179 * addLabel("Name"); // No Mnemonic 180 * addLabel("N&ame"); // Mnemonic is 'a' 181 * addLabel("Save &as"); // Mnemonic is the second 'a' 182 * addLabel("Look&&Feel"); // No mnemonic, text is "look&feel" 183 * </pre> 184 * 185 * @param textWithMnemonic the label's text - 186 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 187 * @return the new label 188 * 189 * @see ComponentFactory 190 */ 191 public final JLabel addLabel(String textWithMnemonic) { 192 return addLabel(textWithMnemonic, cellConstraints()); 193 } 194 195 /** 196 * Adds a textual label to the form using the specified constraints. 197 * 198 * <pre> 199 * addLabel("Name", cc.xy(1, 1)); // No Mnemonic 200 * addLabel("N&ame", cc.xy(1, 1)); // Mnemonic is 'a' 201 * addLabel("Save &as", cc.xy(1, 1)); // Mnemonic is the second 'a' 202 * addLabel("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is "look&feel" 203 * </pre> 204 * 205 * @param textWithMnemonic the label's text - 206 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 207 * @param constraints the label's cell constraints 208 * @return the new label 209 * 210 * @see ComponentFactory 211 */ 212 public final JLabel addLabel(String textWithMnemonic, 213 CellConstraints constraints) { 214 JLabel label = getComponentFactory().createLabel(textWithMnemonic); 215 add(label, constraints); 216 return label; 217 } 218 219 /** 220 * Adds a textual label to the form using the specified constraints. 221 * 222 * <pre> 223 * addLabel("Name", "1, 1"); // No Mnemonic 224 * addLabel("N&ame", "1, 1"); // Mnemonic is 'a' 225 * addLabel("Save &as", "1, 1"); // Mnemonic is the second 'a' 226 * addLabel("Look&&Feel", "1, 1"); // No mnemonic, text is "look&feel" 227 * </pre> 228 * 229 * @param textWithMnemonic the label's text - 230 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 231 * @param encodedConstraints a string representation for the constraints 232 * @return the new label 233 * 234 * @see ComponentFactory 235 */ 236 public final JLabel addLabel(String textWithMnemonic, 237 String encodedConstraints) { 238 return addLabel(textWithMnemonic, 239 new CellConstraints(encodedConstraints)); 240 } 241 242 // Adding Label with related Component ************************************ 243 244 /** 245 * Adds a label and component to the panel using the given cell constraints. 246 * Sets the given label as <i>the</i> component label using 247 * {@link JLabel#setLabelFor(java.awt.Component)}. 248 * 249 * <p><strong>Note:</strong> The {@link CellConstraints} objects for the label 250 * and the component must be different. Cell constraints are implicitly 251 * cloned by the <code>FormLayout</code> when added to the container. 252 * However, in this case you may be tempted to reuse a 253 * <code>CellConstraints</code> object in the same way as with many other 254 * builder methods that require a single <code>CellConstraints</code> 255 * parameter. 256 * The pitfall is that the methods <code>CellConstraints.xy*(...)</code> 257 * just set the coordinates but do <em>not</em> create a new instance. 258 * And so the second invocation of <code>xy*(...)</code> overrides 259 * the settings performed in the first invocation before the object 260 * is cloned by the <code>FormLayout</code>.</p> 261 * 262 * <p><strong>Wrong:</strong></p> 263 * <pre> 264 * CellConstraints cc = new CellConstraints(); 265 * builder.add( 266 * nameLabel, 267 * cc.xy(1, 7), // will be modified by the code below 268 * nameField, 269 * cc.xy(3, 7) // sets the single instance to (3, 7) 270 * ); 271 * </pre> 272 * <p><strong>Correct:</strong></p> 273 * <pre> 274 * // Using a single CellConstraints instance and cloning 275 * CellConstraints cc = new CellConstraints(); 276 * builder.add( 277 * nameLabel, 278 * (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification 279 * nameField, 280 * cc.xy(3, 7) // sets this instance to (3, 7) 281 * ); 282 * 283 * // Using two CellConstraints instances 284 * CellConstraints cc1 = new CellConstraints(); 285 * CellConstraints cc2 = new CellConstraints(); 286 * builder.add( 287 * nameLabel, 288 * cc1.xy(1, 7), // sets instance 1 to (1, 7) 289 * nameField, 290 * cc2.xy(3, 7) // sets instance 2 to (3, 7) 291 * ); 292 * </pre> 293 * 294 * @param label the label to add 295 * @param labelConstraints the label's cell constraints 296 * @param component the component to add 297 * @param componentConstraints the component's cell constraints 298 * @return the added label 299 * @exception IllegalArgumentException if the same cell constraints instance 300 * is used for the label and the component 301 * 302 * @see JLabel#setLabelFor(java.awt.Component) 303 * @see DefaultFormBuilder 304 */ 305 public final JLabel add(JLabel label, CellConstraints labelConstraints, 306 Component component, CellConstraints componentConstraints) { 307 if (labelConstraints == componentConstraints) { 308 throw new IllegalArgumentException( 309 "You must provide two CellConstraints instances, " 310 + "one for the label and one for the component.\n" 311 + "Consider using #clone(). See the JavaDocs for details."); 312 } 313 314 add(label, labelConstraints); 315 add(component, componentConstraints); 316 label.setLabelFor(component); 317 return label; 318 } 319 320 /** 321 * Adds a label and component to the panel using the given cell constraints. 322 * Sets the given label as <i>the</i> component label using 323 * {@link JLabel#setLabelFor(java.awt.Component)}.<p> 324 * 325 * <strong>Note:</strong> The {@link CellConstraints} objects for the label 326 * and the component must be different. Cell constraints are implicitly 327 * cloned by the <code>FormLayout</code> when added to the container. 328 * However, in this case you may be tempted to reuse a 329 * <code>CellConstraints</code> object in the same way as with many other 330 * builder methods that require a single <code>CellConstraints</code> 331 * parameter. 332 * The pitfall is that the methods <code>CellConstraints.xy*(...)</code> 333 * just set the coordinates but do <em>not</em> create a new instance. 334 * And so the second invocation of <code>xy*(...)</code> overrides 335 * the settings performed in the first invocation before the object 336 * is cloned by the <code>FormLayout</code>.<p> 337 * 338 * <strong>Wrong:</strong><pre> 339 * builder.addLabel( 340 * "&Name:", // Mnemonic is 'N' 341 * cc.xy(1, 7), // will be modified by the code below 342 * nameField, 343 * cc.xy(3, 7) // sets the single instance to (3, 7) 344 * ); 345 * </pre> 346 * <strong>Correct:</strong><pre> 347 * // Using a single CellConstraints instance and cloning 348 * CellConstraints cc = new CellConstraints(); 349 * builder.addLabel( 350 * "&Name:", 351 * (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification 352 * nameField, 353 * cc.xy(3, 7) // sets this instance to (3, 7) 354 * ); 355 * 356 * // Using two CellConstraints instances 357 * CellConstraints cc1 = new CellConstraints(); 358 * CellConstraints cc2 = new CellConstraints(); 359 * builder.addLabel( 360 * "&Name:", // Mnemonic is 'N' 361 * cc1.xy(1, 7), // sets instance 1 to (1, 7) 362 * nameField, 363 * cc2.xy(3, 7) // sets instance 2 to (3, 7) 364 * ); 365 * </pre> 366 * 367 * @param textWithMnemonic the label's text - 368 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 369 * @param labelConstraints the label's cell constraints 370 * @param component the component to add 371 * @param componentConstraints the component's cell constraints 372 * @return the added label 373 * @exception IllegalArgumentException if the same cell constraints instance 374 * is used for the label and the component 375 * 376 * @see JLabel#setLabelFor(java.awt.Component) 377 * @see ComponentFactory 378 * @see DefaultFormBuilder 379 */ 380 public final JLabel addLabel(String textWithMnemonic, 381 CellConstraints labelConstraints, Component component, 382 CellConstraints componentConstraints) { 383 384 if (labelConstraints == componentConstraints) { 385 throw new IllegalArgumentException( 386 "You must provide two CellConstraints instances, " 387 + "one for the label and one for the component.\n" 388 + "Consider using #clone(). See the JavaDocs for details."); 389 } 390 391 JLabel label = addLabel(textWithMnemonic, labelConstraints); 392 add(component, componentConstraints); 393 label.setLabelFor(component); 394 return label; 395 } 396 397 // Adding Titles ---------------------------------------------------------- 398 399 /** 400 * Adds a title label to the form using the default constraints. 401 * 402 * <pre> 403 * addTitle("Name"); // No mnemonic 404 * addTitle("N&ame"); // Mnemonic is 'a' 405 * addTitle("Save &as"); // Mnemonic is the second 'a' 406 * addTitle("Look&&Feel"); // No mnemonic, text is Look&Feel 407 * </pre> 408 * 409 * @param textWithMnemonic the title label's text - 410 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 411 * @return the added title label 412 * 413 * @see ComponentFactory 414 */ 415 public final JLabel addTitle(String textWithMnemonic) { 416 return addTitle(textWithMnemonic, cellConstraints()); 417 } 418 419 /** 420 * Adds a title label to the form using the specified constraints. 421 * 422 * <pre> 423 * addTitle("Name", cc.xy(1, 1)); // No mnemonic 424 * addTitle("N&ame", cc.xy(1, 1)); // Mnemonic is 'a' 425 * addTitle("Save &as", cc.xy(1, 1)); // Mnemonic is the second 'a' 426 * addTitle("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is Look&Feel 427 * </pre> 428 * 429 * @param textWithMnemonic the title label's text - 430 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 431 * @param constraints the separator's cell constraints 432 * @return the added title label 433 * 434 * @see ComponentFactory 435 */ 436 public final JLabel addTitle(String textWithMnemonic, 437 CellConstraints constraints) { 438 JLabel titleLabel = getComponentFactory().createTitle(textWithMnemonic); 439 add(titleLabel, constraints); 440 return titleLabel; 441 } 442 443 /** 444 * Adds a title label to the form using the specified constraints. 445 * 446 * <pre> 447 * addTitle("Name", "1, 1"); // No mnemonic 448 * addTitle("N&ame", "1, 1"); // Mnemonic is 'a' 449 * addTitle("Save &as", "1, 1"); // Mnemonic is the second 'a' 450 * addTitle("Look&&Feel", "1, 1"); // No mnemonic, text is Look&Feel 451 * </pre> 452 * 453 * @param textWithMnemonic the title label's text - 454 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 455 * @param encodedConstraints a string representation for the constraints 456 * @return the added title label 457 * 458 * @see ComponentFactory 459 */ 460 public final JLabel addTitle(String textWithMnemonic, 461 String encodedConstraints) { 462 return addTitle(textWithMnemonic, 463 new CellConstraints(encodedConstraints)); 464 } 465 466 // Adding Separators ------------------------------------------------------ 467 468 /** 469 * Adds a titled separator to the form that spans all columns. 470 * 471 * <pre> 472 * addSeparator("Name"); // No Mnemonic 473 * addSeparator("N&ame"); // Mnemonic is 'a' 474 * addSeparator("Save &as"); // Mnemonic is the second 'a' 475 * addSeparator("Look&&Feel"); // No mnemonic, text is "look&feel" 476 * </pre> 477 * 478 * @param textWithMnemonic the separator label's text - 479 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 480 * @return the added separator 481 */ 482 public final JComponent addSeparator(String textWithMnemonic) { 483 return addSeparator(textWithMnemonic, getLayout().getColumnCount()); 484 } 485 486 /** 487 * Adds a titled separator to the form using the specified constraints. 488 * 489 * <pre> 490 * addSeparator("Name", cc.xy(1, 1)); // No Mnemonic 491 * addSeparator("N&ame", cc.xy(1, 1)); // Mnemonic is 'a' 492 * addSeparator("Save &as", cc.xy(1, 1)); // Mnemonic is the second 'a' 493 * addSeparator("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is "look&feel" 494 * </pre> 495 * 496 * @param textWithMnemonic the separator label's text - 497 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 498 * @param constraints the separator's cell constraints 499 * @return the added separator 500 */ 501 public final JComponent addSeparator(String textWithMnemonic, 502 CellConstraints constraints) { 503 int titleAlignment = isLeftToRight() ? SwingConstants.LEFT 504 : SwingConstants.RIGHT; 505 JComponent titledSeparator = getComponentFactory() 506 .createSeparator(textWithMnemonic, titleAlignment); 507 add(titledSeparator, constraints); 508 return titledSeparator; 509 } 510 511 /** 512 * Adds a titled separator to the form using the specified constraints. 513 * 514 * <pre> 515 * addSeparator("Name", "1, 1"); // No Mnemonic 516 * addSeparator("N&ame", "1, 1"); // Mnemonic is 'a' 517 * addSeparator("Save &as", "1, 1"); // Mnemonic is the second 'a' 518 * addSeparator("Look&&Feel", "1, 1"); // No mnemonic, text is "look&feel" 519 * </pre> 520 * 521 * @param textWithMnemonic the separator label's text - 522 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 523 * @param encodedConstraints a string representation for the constraints 524 * @return the added separator 525 */ 526 public final JComponent addSeparator(String textWithMnemonic, 527 String encodedConstraints) { 528 return addSeparator(textWithMnemonic, 529 new CellConstraints(encodedConstraints)); 530 } 531 532 /** 533 * Adds a titled separator to the form that spans the specified columns. 534 * 535 * <pre> 536 * addSeparator("Name", 3); // No Mnemonic 537 * addSeparator("N&ame", 3); // Mnemonic is 'a' 538 * addSeparator("Save &as", 3); // Mnemonic is the second 'a' 539 * addSeparator("Look&&Feel", 3); // No mnemonic, text is "look&feel" 540 * </pre> 541 * 542 * @param textWithMnemonic the separator label's text - 543 * may contain an ampersand (<tt>&</tt>) to mark a mnemonic 544 * @param columnSpan the number of columns the separator spans 545 * @return the added separator 546 */ 547 public final JComponent addSeparator(String textWithMnemonic, 548 int columnSpan) { 549 return addSeparator(textWithMnemonic, 550 createLeftAdjustedConstraints(columnSpan)); 551 } 552 553 // Accessing the ComponentFactory ***************************************** 554 555 /** 556 * Returns the builder's component factory. If no factory 557 * has been set before, it is lazily initialized using with an instance of 558 * {@link com.jgoodies.forms.factories.DefaultComponentFactory}. 559 * 560 * @return the component factory 561 * 562 * @see #setComponentFactory(ComponentFactory) 563 */ 564 public final ComponentFactory getComponentFactory() { 565 if (componentFactory == null) { 566 componentFactory = DefaultComponentFactory.getInstance(); 567 } 568 return componentFactory; 569 } 570 571 /** 572 * Sets a new component factory. 573 * 574 * @param newFactory the component factory to be set 575 * 576 * @see #getComponentFactory() 577 */ 578 public final void setComponentFactory(ComponentFactory newFactory) { 579 componentFactory = newFactory; 580 } 581 582}