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