/*
* Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of JGoodies Karsten Lentzsch nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jgoodies.forms.builder;
import java.awt.Component;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.factories.ComponentFactory;
import com.jgoodies.forms.factories.DefaultComponentFactory;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
/**
* An general purpose panel builder that uses the {@link FormLayout}
* to lay out <code>JPanel</code>s. It provides convenience methods
* to set a default border and to add labels, titles and titled separators.<p>
*
* The PanelBuilder is the working horse for layouts when more specialized
* builders like the {@link ButtonBarBuilder} or {@link DefaultFormBuilder}
* are inappropriate.<p>
*
* The Forms tutorial includes several examples that present and compare
* different style to build with the PanelBuilder: static row numbers
* vs. row variable, explicit CellConstraints vs. builder cursor,
* static rows vs. dynamically added rows. Also, you may check out the
* Tips & Tricks section of the Forms HTML documentation.<p>
*
* The text arguments passed to the methods <code>#addLabel</code>,
* <code>#addTitle</code>, and <code>#addSeparator</code> can contain
* an optional mnemonic marker. The mnemonic and mnemonic index
* are indicated by a single ampersand (<tt>&</tt>). For example
* <tt>"&Save"</tt>, or <tt>"Save &as"</tt>.
* To use the ampersand itself duplicate it, for example
* <tt>"Look&&Feel"</tt>.<p>
*
* <strong>Example:</strong><br>
* This example creates a panel with 3 columns and 3 rows.
* <pre>
* FormLayout layout = new FormLayout(
* "right:pref, 6dlu, 50dlu, 4dlu, default", // columns
* "pref, 3dlu, pref, 3dlu, pref"); // rows
*
* PanelBuilder builder = new PanelBuilder(layout);
* CellConstraints cc = new CellConstraints();
* builder.addLabel("&Title", cc.xy (1, 1));
* builder.add(new JTextField(), cc.xywh(3, 1, 3, 1));
* builder.addLabel("&Price", cc.xy (1, 3));
* builder.add(new JTextField(), cc.xy (3, 3));
* builder.addLabel("&Author", cc.xy (1, 5));
* builder.add(new JTextField(), cc.xy (3, 5));
* builder.add(new JButton("..."), cc.xy (5, 5));
* return builder.getPanel();
* </pre>
*
* @author Karsten Lentzsch
* @version $Revision$
*
* @see com.jgoodies.forms.factories.ComponentFactory
* @see I15dPanelBuilder
* @see DefaultFormBuilder
*/
public class PanelBuilder extends AbstractFormBuilder {
/**
* Refers to a factory that is used to create labels,
* titles and paragraph separators.
*/
private ComponentFactory componentFactory;
// Instance Creation ****************************************************
/**
* Constructs an instance of <code>PanelBuilder</code> for the given
* layout. Uses an instance of <code>JPanel</code> as layout container
* with the given layout as layout manager.
*
* @param layout the FormLayout to use
*/
public PanelBuilder(FormLayout layout) {
this(layout, new JPanel(null));
}
/**
* Constructs an instance of <code>PanelBuilder</code> for the given
* FormLayout and layout container.
*
* @param layout the FormLayout to use
* @param panel the layout container to build on
*/
public PanelBuilder(FormLayout layout, JPanel panel) {
super(layout, panel);
}
/**
* Constructs an instance of <code>PanelBuilder</code> for the given
* panel and layout.
*
* @param panel the layout container to build on
* @param layout the form layout to use
*
* @deprecated Replaced by {@link #PanelBuilder(FormLayout, JPanel)}.
*/
public PanelBuilder(JPanel panel, FormLayout layout) {
super(layout, panel);
}
// Accessors ************************************************************
/**
* Returns the panel used to build the form.
*
* @return the panel used by this builder to build the form
*/
public final JPanel getPanel() {
return (JPanel) getContainer();
}
// Borders **************************************************************
/**
* Sets the panel's border.
*
* @param border the border to set
*/
public final void setBorder(Border border) {
getPanel().setBorder(border);
}
/**
* Sets the default dialog border.
*
* @see Borders
*/
public final void setDefaultDialogBorder() {
setBorder(Borders.DIALOG_BORDER);
}
// Adding Labels **********************************************************
/**
* Adds a textual label to the form using the default constraints.<p>
*
* <pre>
* addLabel("Name"); // No Mnemonic
* addLabel("N&ame"); // Mnemonic is 'a'
* addLabel("Save &as"); // Mnemonic is the second 'a'
* addLabel("Look&&Feel"); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @return the new label
*
* @see ComponentFactory
*/
public final JLabel addLabel(String textWithMnemonic) {
return addLabel(textWithMnemonic, cellConstraints());
}
/**
* Adds a textual label to the form using the specified constraints.<p>
*
* <pre>
* addLabel("Name", cc.xy(1, 1)); // No Mnemonic
* addLabel("N&ame", cc.xy(1, 1)); // Mnemonic is 'a'
* addLabel("Save &as", cc.xy(1, 1)); // Mnemonic is the second 'a'
* addLabel("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param constraints the label's cell constraints
* @return the new label
*
* @see ComponentFactory
*/
public final JLabel addLabel(String textWithMnemonic,
CellConstraints constraints) {
JLabel label = getComponentFactory().createLabel(textWithMnemonic);
add(label, constraints);
return label;
}
/**
* Adds a textual label to the form using the specified constraints.<p>
*
* <pre>
* addLabel("Name", "1, 1"); // No Mnemonic
* addLabel("N&ame", "1, 1"); // Mnemonic is 'a'
* addLabel("Save &as", "1, 1"); // Mnemonic is the second 'a'
* addLabel("Look&&Feel", "1, 1"); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param encodedConstraints a string representation for the constraints
* @return the new label
*
* @see ComponentFactory
*/
public final JLabel addLabel(String textWithMnemonic,
String encodedConstraints) {
return addLabel(textWithMnemonic, new CellConstraints(
encodedConstraints));
}
// Adding Label with related Component ************************************
/**
* Adds a label and component to the panel using the given cell constraints.
* Sets the given label as <i>the</i> component label using
* {@link JLabel#setLabelFor(java.awt.Component)}.<p>
*
* <strong>Note:</strong> The {@link CellConstraints} objects for the label
* and the component must be different. Cell constraints are implicitly
* cloned by the <code>FormLayout</code> when added to the container.
* However, in this case you may be tempted to reuse a
* <code>CellConstraints</code> object in the same way as with many other
* builder methods that require a single <code>CellConstraints</code>
* parameter.
* The pitfall is that the methods <code>CellConstraints.xy*(...)</code>
* just set the coordinates but do <em>not</em> create a new instance.
* And so the second invocation of <code>xy*(...)</code> overrides
* the settings performed in the first invocation before the object
* is cloned by the <code>FormLayout</code>.<p>
*
* <strong>Wrong:</strong><pre>
* CellConstraints cc = new CellConstraints();
* builder.add(
* nameLabel,
* cc.xy(1, 7), // will be modified by the code below
* nameField,
* cc.xy(3, 7) // sets the single instance to (3, 7)
* );
* </pre>
* <strong>Correct:</strong><pre>
* // Using a single CellConstraints instance and cloning
* CellConstraints cc = new CellConstraints();
* builder.add(
* nameLabel,
* (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification
* nameField,
* cc.xy(3, 7) // sets this instance to (3, 7)
* );
*
* // Using two CellConstraints instances
* CellConstraints cc1 = new CellConstraints();
* CellConstraints cc2 = new CellConstraints();
* builder.add(
* nameLabel,
* cc1.xy(1, 7), // sets instance 1 to (1, 7)
* nameField,
* cc2.xy(3, 7) // sets instance 2 to (3, 7)
* );
* </pre>
*
* @param label the label to add
* @param labelConstraints the label's cell constraints
* @param component the component to add
* @param componentConstraints the component's cell constraints
* @return the added label
* @exception IllegalArgumentException if the same cell constraints instance
* is used for the label and the component
*
* @see JLabel#setLabelFor(java.awt.Component)
* @see DefaultFormBuilder
*/
public final JLabel add(JLabel label, CellConstraints labelConstraints,
Component component, CellConstraints componentConstraints) {
if (labelConstraints == componentConstraints) {
throw new IllegalArgumentException(
"You must provide two CellConstraints instances, "
+ "one for the label and one for the component.\n"
+ "Consider using #clone(). See the JavaDocs for details.");
}
add(label, labelConstraints);
add(component, componentConstraints);
label.setLabelFor(component);
return label;
}
/**
* Adds a label and component to the panel using the given cell constraints.
* Sets the given label as <i>the</i> component label using
* {@link JLabel#setLabelFor(java.awt.Component)}.<p>
*
* <strong>Note:</strong> The {@link CellConstraints} objects for the label
* and the component must be different. Cell constraints are implicitly
* cloned by the <code>FormLayout</code> when added to the container.
* However, in this case you may be tempted to reuse a
* <code>CellConstraints</code> object in the same way as with many other
* builder methods that require a single <code>CellConstraints</code>
* parameter.
* The pitfall is that the methods <code>CellConstraints.xy*(...)</code>
* just set the coordinates but do <em>not</em> create a new instance.
* And so the second invocation of <code>xy*(...)</code> overrides
* the settings performed in the first invocation before the object
* is cloned by the <code>FormLayout</code>.<p>
*
* <strong>Wrong:</strong><pre>
* builder.addLabel(
* "&Name:", // Mnemonic is 'N'
* cc.xy(1, 7), // will be modified by the code below
* nameField,
* cc.xy(3, 7) // sets the single instance to (3, 7)
* );
* </pre>
* <strong>Correct:</strong><pre>
* // Using a single CellConstraints instance and cloning
* CellConstraints cc = new CellConstraints();
* builder.addLabel(
* "&Name:",
* (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification
* nameField,
* cc.xy(3, 7) // sets this instance to (3, 7)
* );
*
* // Using two CellConstraints instances
* CellConstraints cc1 = new CellConstraints();
* CellConstraints cc2 = new CellConstraints();
* builder.addLabel(
* "&Name:", // Mnemonic is 'N'
* cc1.xy(1, 7), // sets instance 1 to (1, 7)
* nameField,
* cc2.xy(3, 7) // sets instance 2 to (3, 7)
* );
* </pre>
*
* @param textWithMnemonic the label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param labelConstraints the label's cell constraints
* @param component the component to add
* @param componentConstraints the component's cell constraints
* @return the added label
* @exception IllegalArgumentException if the same cell constraints instance
* is used for the label and the component
*
* @see JLabel#setLabelFor(java.awt.Component)
* @see ComponentFactory
* @see DefaultFormBuilder
*/
public final JLabel addLabel(String textWithMnemonic,
CellConstraints labelConstraints, Component component,
CellConstraints componentConstraints) {
if (labelConstraints == componentConstraints) {
throw new IllegalArgumentException(
"You must provide two CellConstraints instances, "
+ "one for the label and one for the component.\n"
+ "Consider using #clone(). See the JavaDocs for details.");
}
JLabel label = addLabel(textWithMnemonic, labelConstraints);
add(component, componentConstraints);
label.setLabelFor(component);
return label;
}
// Adding Titles ----------------------------------------------------------
/**
* Adds a title label to the form using the default constraints.<p>
*
* <pre>
* addTitle("Name"); // No mnemonic
* addTitle("N&ame"); // Mnemonic is 'a'
* addTitle("Save &as"); // Mnemonic is the second 'a'
* addTitle("Look&&Feel"); // No mnemonic, text is Look&Feel
* </pre>
*
* @param textWithMnemonic the title label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @return the added title label
*
* @see ComponentFactory
*/
public final JLabel addTitle(String textWithMnemonic) {
return addTitle(textWithMnemonic, cellConstraints());
}
/**
* Adds a title label to the form using the specified constraints.<p>
*
* <pre>
* addTitle("Name", cc.xy(1, 1)); // No mnemonic
* addTitle("N&ame", cc.xy(1, 1)); // Mnemonic is 'a'
* addTitle("Save &as", cc.xy(1, 1)); // Mnemonic is the second 'a'
* addTitle("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is Look&Feel
* </pre>
*
* @param textWithMnemonic the title label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param constraints the separator's cell constraints
* @return the added title label
*
* @see ComponentFactory
*/
public final JLabel addTitle(String textWithMnemonic,
CellConstraints constraints) {
JLabel titleLabel = getComponentFactory().createTitle(textWithMnemonic);
add(titleLabel, constraints);
return titleLabel;
}
/**
* Adds a title label to the form using the specified constraints.<p>
*
* <pre>
* addTitle("Name", "1, 1"); // No mnemonic
* addTitle("N&ame", "1, 1"); // Mnemonic is 'a'
* addTitle("Save &as", "1, 1"); // Mnemonic is the second 'a'
* addTitle("Look&&Feel", "1, 1"); // No mnemonic, text is Look&Feel
* </pre>
*
* @param textWithMnemonic the title label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param encodedConstraints a string representation for the constraints
* @return the added title label
*
* @see ComponentFactory
*/
public final JLabel addTitle(String textWithMnemonic,
String encodedConstraints) {
return addTitle(textWithMnemonic, new CellConstraints(
encodedConstraints));
}
// Adding Separators ------------------------------------------------------
/**
* Adds a titled separator to the form that spans all columns.<p>
*
* <pre>
* addSeparator("Name"); // No Mnemonic
* addSeparator("N&ame"); // Mnemonic is 'a'
* addSeparator("Save &as"); // Mnemonic is the second 'a'
* addSeparator("Look&&Feel"); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the separator label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @return the added separator
*/
public final JComponent addSeparator(String textWithMnemonic) {
return addSeparator(textWithMnemonic, getLayout().getColumnCount());
}
/**
* Adds a titled separator to the form using the specified constraints.<p>
*
* <pre>
* addSeparator("Name", cc.xy(1, 1)); // No Mnemonic
* addSeparator("N&ame", cc.xy(1, 1)); // Mnemonic is 'a'
* addSeparator("Save &as", cc.xy(1, 1)); // Mnemonic is the second 'a'
* addSeparator("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the separator label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param constraints the separator's cell constraints
* @return the added separator
*/
public final JComponent addSeparator(String textWithMnemonic,
CellConstraints constraints) {
int titleAlignment = isLeftToRight() ? SwingConstants.LEFT
: SwingConstants.RIGHT;
JComponent titledSeparator = getComponentFactory().createSeparator(
textWithMnemonic, titleAlignment);
add(titledSeparator, constraints);
return titledSeparator;
}
/**
* Adds a titled separator to the form using the specified constraints.<p>
*
* <pre>
* addSeparator("Name", "1, 1"); // No Mnemonic
* addSeparator("N&ame", "1, 1"); // Mnemonic is 'a'
* addSeparator("Save &as", "1, 1"); // Mnemonic is the second 'a'
* addSeparator("Look&&Feel", "1, 1"); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the separator label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param encodedConstraints a string representation for the constraints
* @return the added separator
*/
public final JComponent addSeparator(String textWithMnemonic,
String encodedConstraints) {
return addSeparator(textWithMnemonic, new CellConstraints(
encodedConstraints));
}
/**
* Adds a titled separator to the form that spans the specified columns.<p>
*
* <pre>
* addSeparator("Name", 3); // No Mnemonic
* addSeparator("N&ame", 3); // Mnemonic is 'a'
* addSeparator("Save &as", 3); // Mnemonic is the second 'a'
* addSeparator("Look&&Feel", 3); // No mnemonic, text is "look&feel"
* </pre>
*
* @param textWithMnemonic the separator label's text -
* may contain an ampersand (<tt>&</tt>) to mark a mnemonic
* @param columnSpan the number of columns the separator spans
* @return the added separator
*/
public final JComponent addSeparator(String textWithMnemonic, int columnSpan) {
return addSeparator(textWithMnemonic,
createLeftAdjustedConstraints(columnSpan));
}
// Accessing the ComponentFactory *****************************************
/**
* Returns the builder's component factory. If no factory
* has been set before, it is lazily initialized using with an instance of
* {@link com.jgoodies.forms.factories.DefaultComponentFactory}.
*
* @return the component factory
*
* @see #setComponentFactory(ComponentFactory)
*/
public final ComponentFactory getComponentFactory() {
if (componentFactory == null) {
componentFactory = DefaultComponentFactory.getInstance();
}
return componentFactory;
}
/**
* Sets a new component factory.
*
* @param newFactory the component factory to be set
*
* @see #getComponentFactory()
*/
public final void setComponentFactory(ComponentFactory newFactory) {
componentFactory = newFactory;
}
}