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.Component;
034: import java.awt.ComponentOrientation;
035: import java.awt.Container;
036:
037: import com.jgoodies.forms.factories.FormFactory;
038: import com.jgoodies.forms.layout.CellConstraints;
039: import com.jgoodies.forms.layout.ColumnSpec;
040: import com.jgoodies.forms.layout.FormLayout;
041: import com.jgoodies.forms.layout.RowSpec;
042:
043: /**
044: * An abstract class that minimizes the effort required to implement
045: * non-visual builders that use the {@link FormLayout}.<p>
046: *
047: * Builders hide details of the FormLayout and provide convenience behavior
048: * that assists you in constructing a form.
049: * This class provides a cell cursor that helps you traverse a form while
050: * you add components. Also, it offers several methods to append custom
051: * and logical columns and rows.
052: *
053: * @author Karsten Lentzsch
054: * @version $Revision: 1.3 $
055: *
056: * @see ButtonBarBuilder
057: * @see ButtonStackBuilder
058: * @see PanelBuilder
059: * @see I15dPanelBuilder
060: * @see DefaultFormBuilder
061: */
062: public abstract class AbstractFormBuilder {
063:
064: /**
065: * Holds the layout container that we are building.
066: */
067: private final Container container;
068:
069: /**
070: * Holds the instance of <code>FormLayout</code> that is used to
071: * specifiy, fill and layout this form.
072: */
073: private final FormLayout layout;
074:
075: /**
076: * Holds an instance of <code>CellConstraints</code> that will be used to
077: * specify the location, extent and alignments of the component to be
078: * added next.
079: */
080: private CellConstraints currentCellConstraints;
081:
082: /**
083: * Specifies if we fill the grid from left to right or right to left.
084: * This value is initialized during the construction from the layout
085: * container's component orientation.
086: *
087: * @see #isLeftToRight()
088: * @see #setLeftToRight(boolean)
089: * @see ComponentOrientation
090: */
091: private boolean leftToRight;
092:
093: // Instance Creation ****************************************************
094:
095: /**
096: * Constructs a <code>AbstractFormBuilder</code>
097: * for the given FormLayout and layout container.
098: *
099: * @param layout the {@link FormLayout} to use
100: * @param container the layout container
101: *
102: * @throws NullPointerException if the layout or container is null
103: */
104: public AbstractFormBuilder(FormLayout layout, Container container) {
105: if (layout == null)
106: throw new NullPointerException(
107: "The layout must not be null.");
108:
109: if (container == null)
110: throw new NullPointerException(
111: "The layout container must not be null.");
112:
113: this .container = container;
114: this .layout = layout;
115:
116: container.setLayout(layout);
117: currentCellConstraints = new CellConstraints();
118: ComponentOrientation orientation = container
119: .getComponentOrientation();
120: leftToRight = orientation.isLeftToRight()
121: || !orientation.isHorizontal();
122: }
123:
124: // Accessors ************************************************************
125:
126: /**
127: * Returns the container used to build the form.
128: *
129: * @return the layout container
130: */
131: public final Container getContainer() {
132: return container;
133: }
134:
135: /**
136: * Returns the instance of {@link FormLayout} used to build this form.
137: *
138: * @return the FormLayout
139: */
140: public final FormLayout getLayout() {
141: return layout;
142: }
143:
144: /**
145: * Returns the number of columns in the form.
146: *
147: * @return the number of columns
148: */
149: public final int getColumnCount() {
150: return getLayout().getColumnCount();
151: }
152:
153: /**
154: * Returns the number of rows in the form.
155: *
156: * @return the number of rows
157: */
158: public final int getRowCount() {
159: return getLayout().getRowCount();
160: }
161:
162: // Accessing the Cursor Direction ***************************************
163:
164: /**
165: * Returns whether this builder fills the form left-to-right
166: * or right-to-left. The initial value of this property is set
167: * during the builder construction from the layout container's
168: * <code>componentOrientation</code> property.
169: *
170: * @return true indicates left-to-right, false indicates right-to-left
171: *
172: * @see #setLeftToRight(boolean)
173: * @see ComponentOrientation
174: */
175: public final boolean isLeftToRight() {
176: return leftToRight;
177: }
178:
179: /**
180: * Sets the form fill direction to left-to-right or right-to-left.
181: * The initial value of this property is set during the builder construction
182: * from the layout container's <code>componentOrientation</code> property.
183: *
184: * @param b true indicates left-to-right, false right-to-left
185: *
186: * @see #isLeftToRight()
187: * @see ComponentOrientation
188: */
189: public final void setLeftToRight(boolean b) {
190: leftToRight = b;
191: }
192:
193: // Accessing the Cursor Location and Extent *****************************
194:
195: /**
196: * Returns the cursor's column.
197: *
198: * @return the cursor's column
199: */
200: public final int getColumn() {
201: return currentCellConstraints.gridX;
202: }
203:
204: /**
205: * Sets the cursor to the given column.
206: *
207: * @param column the cursor's new column index
208: */
209: public final void setColumn(int column) {
210: currentCellConstraints.gridX = column;
211: }
212:
213: /**
214: * Returns the cursor's row.
215: *
216: * @return the cursor's row
217: */
218: public final int getRow() {
219: return currentCellConstraints.gridY;
220: }
221:
222: /**
223: * Sets the cursor to the given row.
224: *
225: * @param row the cursor's new row index
226: */
227: public final void setRow(int row) {
228: currentCellConstraints.gridY = row;
229: }
230:
231: /**
232: * Sets the cursor's column span.
233: *
234: * @param columnSpan the cursor's new column span (grid width)
235: */
236: public final void setColumnSpan(int columnSpan) {
237: currentCellConstraints.gridWidth = columnSpan;
238: }
239:
240: /**
241: * Sets the cursor's row span.
242: *
243: * @param rowSpan the cursor's new row span (grid height)
244: */
245: public final void setRowSpan(int rowSpan) {
246: currentCellConstraints.gridHeight = rowSpan;
247: }
248:
249: /**
250: * Sets the cursor's origin to the given column and row.
251: *
252: * @param column the new column index
253: * @param row the new row index
254: */
255: public final void setOrigin(int column, int row) {
256: setColumn(column);
257: setRow(row);
258: }
259:
260: /**
261: * Sets the cursor's extent to the given column span and row span.
262: *
263: * @param columnSpan the new column span (grid width)
264: * @param rowSpan the new row span (grid height)
265: */
266: public final void setExtent(int columnSpan, int rowSpan) {
267: setColumnSpan(columnSpan);
268: setRowSpan(rowSpan);
269: }
270:
271: /**
272: * Sets the cell bounds (location and extent) to the given column, row,
273: * column span and row span.
274: *
275: * @param column the new column index (grid x)
276: * @param row the new row index (grid y)
277: * @param columnSpan the new column span (grid width)
278: * @param rowSpan the new row span (grid height)
279: */
280: public final void setBounds(int column, int row, int columnSpan,
281: int rowSpan) {
282: setColumn(column);
283: setRow(row);
284: setColumnSpan(columnSpan);
285: setRowSpan(rowSpan);
286: }
287:
288: /**
289: * Moves to the next column, does the same as #nextColumn(1).
290: */
291: public final void nextColumn() {
292: nextColumn(1);
293: }
294:
295: /**
296: * Moves to the next column.
297: *
298: * @param columns number of columns to move
299: */
300: public final void nextColumn(int columns) {
301: currentCellConstraints.gridX += columns
302: * getColumnIncrementSign();
303: }
304:
305: /**
306: * Increases the row by one; does the same as #nextRow(1).
307: */
308: public final void nextRow() {
309: nextRow(1);
310: }
311:
312: /**
313: * Increases the row by the specified rows.
314: *
315: * @param rows number of rows to move
316: */
317: public final void nextRow(int rows) {
318: currentCellConstraints.gridY += rows;
319: }
320:
321: /**
322: * Moves to the next line: increases the row and resets the column;
323: * does the same as #nextLine(1).
324: */
325: public final void nextLine() {
326: nextLine(1);
327: }
328:
329: /**
330: * Moves the cursor down several lines: increases the row by the
331: * specified number of lines and sets the cursor to the leading column.
332: *
333: * @param lines number of rows to move
334: */
335: public final void nextLine(int lines) {
336: nextRow(lines);
337: setColumn(getLeadingColumn());
338: }
339:
340: // Form Constraints Alignment *******************************************
341:
342: /**
343: * Sets the horizontal alignment.
344: *
345: * @param alignment the new horizontal alignment
346: */
347: public final void setHAlignment(CellConstraints.Alignment alignment) {
348: currentCellConstraints.hAlign = alignment;
349: }
350:
351: /**
352: * Sets the vertical alignment.
353: *
354: * @param alignment the new vertical alignment
355: */
356: public final void setVAlignment(CellConstraints.Alignment alignment) {
357: currentCellConstraints.vAlign = alignment;
358: }
359:
360: /**
361: * Sets the horizontal and vertical alignment.
362: *
363: * @param hAlign the new horizontal alignment
364: * @param vAlign the new vertical alignment
365: */
366: public final void setAlignment(CellConstraints.Alignment hAlign,
367: CellConstraints.Alignment vAlign) {
368: setHAlignment(hAlign);
369: setVAlignment(vAlign);
370: }
371:
372: // Appending Columns ******************************************************
373:
374: /**
375: * Appends the given column specification to the builder's layout.
376: *
377: * @param columnSpec the column specification object to append
378: *
379: * @see #appendColumn(String)
380: */
381: public final void appendColumn(ColumnSpec columnSpec) {
382: getLayout().appendColumn(columnSpec);
383: }
384:
385: /**
386: * Appends a column specification to the builder's layout
387: * that represents the given string encoding.
388: *
389: * @param encodedColumnSpec the column specification to append in encoded form
390: *
391: * @see #appendColumn(ColumnSpec)
392: */
393: public final void appendColumn(String encodedColumnSpec) {
394: appendColumn(new ColumnSpec(encodedColumnSpec));
395: }
396:
397: /**
398: * Appends a glue column.
399: *
400: * @see #appendLabelComponentsGapColumn()
401: * @see #appendRelatedComponentsGapColumn()
402: * @see #appendUnrelatedComponentsGapColumn()
403: */
404: public final void appendGlueColumn() {
405: appendColumn(FormFactory.GLUE_COLSPEC);
406: }
407:
408: /**
409: * Appends a column that is the default gap between a label and
410: * its associated component.
411: *
412: * @since 1.0.3
413: *
414: * @see #appendGlueColumn()
415: * @see #appendRelatedComponentsGapColumn()
416: * @see #appendUnrelatedComponentsGapColumn()
417: */
418: public final void appendLabelComponentsGapColumn() {
419: appendColumn(FormFactory.LABEL_COMPONENT_GAP_COLSPEC);
420: }
421:
422: /**
423: * Appends a column that is the default gap for related components.
424: *
425: * @see #appendGlueColumn()
426: * @see #appendLabelComponentsGapColumn()
427: * @see #appendUnrelatedComponentsGapColumn()
428: */
429: public final void appendRelatedComponentsGapColumn() {
430: appendColumn(FormFactory.RELATED_GAP_COLSPEC);
431: }
432:
433: /**
434: * Appends a column that is the default gap for unrelated components.
435: *
436: * @see #appendGlueColumn()
437: * @see #appendLabelComponentsGapColumn()
438: * @see #appendRelatedComponentsGapColumn()
439: */
440: public final void appendUnrelatedComponentsGapColumn() {
441: appendColumn(FormFactory.UNRELATED_GAP_COLSPEC);
442: }
443:
444: // Appending Rows ********************************************************
445:
446: /**
447: * Appends the given row specification to the builder's layout.
448: *
449: * @param rowSpec the row specification object to append
450: *
451: * @see #appendRow(String)
452: */
453: public final void appendRow(RowSpec rowSpec) {
454: getLayout().appendRow(rowSpec);
455: }
456:
457: /**
458: * Appends a row specification to the builder's layout that represents
459: * the given string encoding.
460: *
461: * @param encodedRowSpec the row specification to append in encoded form
462: *
463: * @see #appendRow(RowSpec)
464: */
465: public final void appendRow(String encodedRowSpec) {
466: appendRow(new RowSpec(encodedRowSpec));
467: }
468:
469: /**
470: * Appends a glue row.
471: *
472: * @see #appendRelatedComponentsGapRow()
473: * @see #appendUnrelatedComponentsGapRow()
474: * @see #appendParagraphGapRow()
475: */
476: public final void appendGlueRow() {
477: appendRow(FormFactory.GLUE_ROWSPEC);
478: }
479:
480: /**
481: * Appends a row that is the default gap for related components.
482: *
483: * @see #appendGlueRow()
484: * @see #appendUnrelatedComponentsGapRow()
485: * @see #appendParagraphGapRow()
486: */
487: public final void appendRelatedComponentsGapRow() {
488: appendRow(FormFactory.RELATED_GAP_ROWSPEC);
489: }
490:
491: /**
492: * Appends a row that is the default gap for unrelated components.
493: *
494: * @see #appendGlueRow()
495: * @see #appendRelatedComponentsGapRow()
496: * @see #appendParagraphGapRow()
497: */
498: public final void appendUnrelatedComponentsGapRow() {
499: appendRow(FormFactory.UNRELATED_GAP_ROWSPEC);
500: }
501:
502: /**
503: * Appends a row that is the default gap for paragraphs.
504: *
505: * @since 1.0.3
506: *
507: * @see #appendGlueRow()
508: * @see #appendRelatedComponentsGapRow()
509: * @see #appendUnrelatedComponentsGapRow()
510: */
511: public final void appendParagraphGapRow() {
512: appendRow(FormFactory.PARAGRAPH_GAP_ROWSPEC);
513: }
514:
515: // Adding Components ****************************************************
516:
517: /**
518: * Adds a component to the panel using the given cell constraints.
519: *
520: * @param component the component to add
521: * @param cellConstraints the component's cell constraints
522: * @return the added component
523: */
524: public final Component add(Component component,
525: CellConstraints cellConstraints) {
526: container.add(component, cellConstraints);
527: return component;
528: }
529:
530: /**
531: * Adds a component to the panel using the given encoded cell constraints.
532: *
533: * @param component the component to add
534: * @param encodedCellConstraints the component's encoded cell constraints
535: * @return the added component
536: */
537: public final Component add(Component component,
538: String encodedCellConstraints) {
539: container.add(component, new CellConstraints(
540: encodedCellConstraints));
541: return component;
542: }
543:
544: /**
545: * Adds a component to the container using the default cell constraints.
546: * Note that when building from left to right, this method won't adjust
547: * the cell constraints if the column span is larger than 1. In this case
548: * you should use {@link #add(Component, CellConstraints)} with a cell
549: * constraints object created by {@link #createLeftAdjustedConstraints(int)}.
550: *
551: * @param component the component to add
552: * @return the added component
553: *
554: * @see #add(Component, CellConstraints)
555: * @see #createLeftAdjustedConstraints(int)
556: */
557: public final Component add(Component component) {
558: add(component, currentCellConstraints);
559: return component;
560: }
561:
562: // Misc *****************************************************************
563:
564: /**
565: * Returns the CellConstraints object that is used as a cursor and
566: * holds the current column span and row span.
567: *
568: * @return the builder's current {@link CellConstraints} object
569: */
570: protected final CellConstraints cellConstraints() {
571: return currentCellConstraints;
572: }
573:
574: /**
575: * Returns the index of the leading column.<p>
576: *
577: * Subclasses may override this method, for example, if the form
578: * has a leading gap column that should not be filled with components.
579: *
580: * @return the leading column
581: */
582: protected int getLeadingColumn() {
583: return isLeftToRight() ? 1 : getColumnCount();
584: }
585:
586: /**
587: * Returns the sign (-1 or 1) used to increment the cursor's column
588: * when moving to the next column.
589: *
590: * @return -1 for right-to-left, 1 for left-to-right
591: */
592: protected final int getColumnIncrementSign() {
593: return isLeftToRight() ? 1 : -1;
594: }
595:
596: /**
597: * Creates and returns a <code>CellConstraints</code> object at
598: * the current cursor position that uses the given column span
599: * and is adjusted to the left. Useful when building from right to left.
600: *
601: * @param columnSpan the column span to be used in the constraints
602: * @return CellConstraints adjusted to the left hand side
603: */
604: protected final CellConstraints createLeftAdjustedConstraints(
605: int columnSpan) {
606: int firstColumn = isLeftToRight() ? getColumn() : getColumn()
607: + 1 - columnSpan;
608: return new CellConstraints(firstColumn, getRow(), columnSpan,
609: cellConstraints().gridHeight);
610: }
611:
612: }
|