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