001    package org.springframework.richclient.form.builder;
002    
003    import com.jgoodies.forms.factories.FormFactory;
004    import com.jgoodies.forms.layout.CellConstraints;
005    import com.jgoodies.forms.layout.FormLayout;
006    import com.jgoodies.forms.layout.RowSpec;
007    import org.springframework.binding.form.FormModel;
008    import org.springframework.binding.form.support.NestedPropertyChangeListener;
009    import org.springframework.richclient.form.binding.Binding;
010    import org.springframework.richclient.form.binding.BindingFactory;
011    import org.springframework.richclient.form.binding.swing.SwingBindingFactory;
012    
013    import javax.swing.*;
014    import javax.swing.border.Border;
015    import java.beans.PropertyChangeEvent;
016    import java.beans.PropertyChangeListener;
017    import java.util.ArrayList;
018    import java.util.Collections;
019    import java.util.List;
020    import java.util.Map;
021    
022    /**
023     * <p>
024     * Layout builder based on the <a href="http://www.jgoodies.com">JGoodies</a> Framework.
025     * </p>
026     * <p/>
027     * <p>
028     * Shortcuts have been provided to directly place a local and a component. For this, you provide the coordinates
029     * for the label (x, y), and the component will be places (x + 2, y). If width or height spanning is provided,
030     * this will be applied to the component.
031     * </p>
032     * <p/>
033     * <pre>
034     * FormBuilder builder = new FormBuilder(getBindingFactory(), myLayout);
035     * //... add everything ...
036     * JPanel myFirstPanel = builder.getPanel();
037     * // set new layout, new panel
038     * builder.setPanel(mySecondLayout);
039     * //... add everything ...
040     * JPanel mySecondPanel = builder.getPanel();
041     * </pre>
042     *
043     * @author jh
044     */
045    public class FormLayoutFormBuilder extends AbstractFormBuilder
046    {
047    
048        public static final String ALIGN_LEFT_TOP = "left, top";
049        public static final String ALIGN_LEFT_CENTER = "left, center";
050        public static final String ALIGN_LEFT_BOTTOM = "left, bottom";
051        public static final String ALIGN_RIGHT_TOP = "right, top";
052        public static final String ALIGN_RIGHT_CENTER = "right, center";
053        public static final String ALIGN_RIGHT_BOTTOM = "right, bottom";
054    
055        private FormLayout layout;
056        private JPanel panel;
057        private CellConstraints cc;
058        private String labelAttributes = ALIGN_LEFT_CENTER;
059        private String componentAttributes = null;
060    
061        private int row;
062    
063        private static final int DEFAULT_ROW_INCREMENT = 2;
064    
065        /**
066         * Constructor.
067         *
068         * @param bindingFactory BindingFactory.
069         * @param layout         JGoodies FormLayout
070         */
071        public FormLayoutFormBuilder(BindingFactory bindingFactory, FormLayout layout)
072        {
073            this(bindingFactory, layout, new JPanel());
074        }
075    
076        /**
077         * Constructor.
078         *
079         * @param bindingFactory BindingFactory.
080         * @param layout         JGoodies FormLayout
081         * @param panel          JPanel on which the builder will place the components.
082         */
083        public FormLayoutFormBuilder(BindingFactory bindingFactory, FormLayout layout, JPanel panel)
084        {
085            super(bindingFactory);
086            setLayout(layout, panel);
087        }
088    
089        /**
090         * Move to the next line, minding the line gap
091         *
092         * @return This FormBuilder
093         */
094        public FormLayoutFormBuilder nextRow()
095        {
096            return nextRow(FormFactory.DEFAULT_ROWSPEC);
097        }
098    
099        public FormLayoutFormBuilder nextRow(String rowSpec)
100        {
101            return nextRow(RowSpec.decode(rowSpec));
102        }
103    
104        public FormLayoutFormBuilder nextRow(RowSpec rowSpec)
105        {
106            row += DEFAULT_ROW_INCREMENT;
107            correctNumberOfRows(row, rowSpec);
108            return this;
109        }
110    
111        private void correctNumberOfRows(int requiredNrOfRows)
112        {
113            correctNumberOfRows(requiredNrOfRows, FormFactory.DEFAULT_ROWSPEC);
114        }
115    
116        private void correctNumberOfRows(int requiredNrOfRows, RowSpec rowSpec)
117        {
118            int rowCount = layout.getRowCount();
119            // if not initialized, set on first row
120            if (row == -1 && requiredNrOfRows == -1)
121            {
122                requiredNrOfRows = 1;
123                row = 1;
124            }
125    
126            if (requiredNrOfRows > rowCount)
127            {
128                for (int i = rowCount; i < requiredNrOfRows; i = i + DEFAULT_ROW_INCREMENT)
129                {
130                    if (i != 0)
131                        layout.appendRow(FormFactory.LINE_GAP_ROWSPEC);
132                    layout.appendRow(rowSpec);
133                }
134            }
135        }
136    
137        /**
138         * Returns the current row. If the layout is initialized, the row is set to -1. Calling getRow() will
139         * result in setting the row to 1 to allow direct usage of getRow().
140         *
141         * @return the current row
142         */
143        public int getRow()
144        {
145            if (row == -1) // init row
146                row = 1;
147    
148            return row;
149        }
150    
151        /**
152         * Sets the current row
153         *
154         * @param row The current row
155         */
156        public void setRow(int row)
157        {
158            this.row = row;
159            correctNumberOfRows(row);
160        }
161    
162        /**
163         * Create a new panel with the provided layout en default DIALOG_BORDER.
164         *
165         * @param layout JGoodies FormLayout
166         */
167        public void setLayout(FormLayout layout)
168        {
169            setLayout(layout, new JPanel());
170        }
171    
172        /**
173         * Set a panel with the provided layout layout.
174         *
175         * @param layout JGoodies FormLayout
176         * @param panel  JPanel on which the builder will place the components.
177         */
178        public void setLayout(FormLayout layout, JPanel panel)
179        {
180            this.panel = panel;
181            this.layout = layout;
182            panel.setLayout(layout);
183            cc = new CellConstraints();
184            row = -1;
185        }
186    
187        /**
188         * Set the border for the panel.
189         *
190         * @param border The border for the panel
191         */
192        public void setBorder(Border border)
193        {
194            this.panel.setBorder(border);
195        }
196    
197        /**
198         * Add a binder to column 1 and the current row. Equals to builder.addBinding(component, 1,
199         * builder.getRow()).
200         *
201         * @param binding The binding to add
202         * @see #addBinding(Binding, int, int)
203         */
204        public JComponent addBinding(Binding binding)
205        {
206            return addBinding(binding, 1, row);
207        }
208    
209        /**
210         * Add a binder to a column and the current row. Equals to builder.addBinding(component, column,
211         * builder.getRow()).
212         *
213         * @param binding The binding to add
214         * @param column  The column on which the binding must be added
215         * @return The component produced by the binding
216         * @see #addBinding(Binding, int, int)
217         */
218        public JComponent addBinding(Binding binding, int column)
219        {
220            return addBinding(binding, column, row);
221        }
222    
223        /**
224         * Add a binder to a column and a row. Equals to builder.addBinding(component, column,
225         * row).
226         *
227         * @param binding The binding to add
228         * @param column  The column on which the binding must be added
229         * @param column  The row on which the binding must be added
230         * @return The component produced by the binding
231         * @see #addBinding(Binding, int, int, int, int)
232         */
233        public JComponent addBinding(Binding binding, int column, int row)
234        {
235            return this.addBinding(binding, column, row, 1, 1);
236        }
237    
238        /**
239         * Add a binder to a column and a row with width and height spanning.
240         */
241        public JComponent addBinding(Binding binding, int column, int row, int widthSpan, int heightSpan)
242        {
243            ((SwingBindingFactory) getBindingFactory()).interceptBinding(binding);
244            JComponent component = binding.getControl();
245            addComponent(component, column, row, widthSpan, heightSpan);
246            return component;
247        }
248    
249        /**
250         * Add a property to column 1 and the current row. Equals to builder.addBinding(component, 1,
251         * builder.getRow()).
252         *
253         * @see #addProperty(String, int, int)
254         */
255        public JComponent addProperty(String property)
256        {
257            return addProperty(property, 1, row);
258        }
259    
260        /**
261         * Add a property to a column and the current row. Equals to builder.addBinding(component, column,
262         * builder.getRow()).
263         *
264         * @see #addProperty(String, int, int)
265         */
266        public JComponent addProperty(String property, int column)
267        {
268            return addProperty(property, column, row);
269        }
270    
271        public JComponent addProperty(String property, String binderId)
272        {
273            return addProperty(property, binderId, 1, row);
274        }
275    
276        public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap)
277        {
278            return addProperty(property, binderId, contextMap, 1, row);
279        }
280    
281        public JComponent addProperty(String property, String binderId, int column)
282        {
283            return addProperty(property, binderId, column, row);
284        }
285    
286        public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap, int column)
287        {
288            return addProperty(property, binderId, contextMap, column, row);
289        }
290    
291        public JComponent addProperty(String property, int column, int row)
292        {
293            return this.addProperty(property, column, row, 1, 1);
294        }
295    
296        public JComponent addProperty(String property, String binderId, int column, int row)
297        {
298            return this.addProperty(property, binderId, column, row, 1, 1);
299        }
300    
301        public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap, int column, int row)
302        {
303            return this.addProperty(property, binderId, contextMap, column, row, 1, 1);
304        }
305    
306        public JComponent addProperty(String property, int column, int row, int widthSpan, int heightSpan)
307        {
308            JComponent propertyComponent = createDefaultBinding(property).getControl();
309            addComponent(propertyComponent, column, row, widthSpan, heightSpan);
310            return propertyComponent;
311        }
312    
313        public JComponent addProperty(String property, String binderId, int column, int row, int widthSpan,
314                                      int heightSpan)
315        {
316            return addProperty(property, binderId, Collections.EMPTY_MAP, column, row, widthSpan, heightSpan);
317        }
318    
319        public JComponent addProperty(String property, String binderId, Map<?, ?> contextMap, int column, int row, int widthSpan,
320                                      int heightSpan)
321        {
322            JComponent propertyComponent = ((SwingBindingFactory) getBindingFactory()).createBinding(property,
323                    binderId, contextMap).getControl();
324            addComponent(propertyComponent, column, row, widthSpan, heightSpan);
325            return propertyComponent;
326        }
327    
328        public JLabel addLabel(String property)
329        {
330            return addLabel(property, 1, row);
331        }
332    
333        public JLabel addLabel(String property, int column)
334        {
335            return addLabel(property, column, row);
336        }
337    
338        public JLabel addLabel(String property, int column, int row)
339        {
340            return addLabel(property, null, column, row);
341        }
342    
343        public JLabel addLabel(String property, int column, int row, String attributes)
344        {
345            return addLabel(property, null, column, row, 1, 1, attributes);
346        }
347    
348        public JLabel addLabel(String property, JComponent forComponent, int column, int row)
349        {
350            return addLabel(property, forComponent, column, row, 1, 1, this.labelAttributes);
351        }
352    
353        public JLabel addLabel(String property, JComponent forComponent, int column, int row, int widthSpan,
354                               int heightSpan, String attributes)
355        {
356            JLabel labelComponent = createLabelFor(property, forComponent);
357            this.row = row;
358            if (this.row == -1)
359            {
360                this.row = 1;
361            }
362            correctNumberOfRows(this.row);
363            this.panel.add(labelComponent, cc.xywh(column, this.row, widthSpan, heightSpan, attributes));
364            return labelComponent;
365        }
366    
367        public void addComponent(JComponent component)
368        {
369            addComponent(component, 1, row, 1, 1);
370        }
371    
372        public void addComponent(JComponent component, int column)
373        {
374            addComponent(component, column, row, 1, 1);
375        }
376    
377        public void addComponent(JComponent component, int column, int row)
378        {
379            addComponent(component, column, row, 1, 1);
380        }
381    
382        public void addComponent(JComponent component, int column, int row, int widthSpan, int heightSpan)
383        {
384            addComponent(component, column, row, widthSpan, heightSpan, this.componentAttributes);
385        }
386    
387        public void addComponent(JComponent component, int column, int row, int widthSpan, int heightSpan,
388                                 String attributes)
389        {
390            this.row = row;
391            if (this.row == -1)
392            {
393                this.row = 1;
394            }
395            correctNumberOfRows(this.row);
396            if (attributes == null)
397                this.panel.add(component, cc.xywh(column, this.row, widthSpan, heightSpan));
398            else
399                this.panel.add(component, cc.xywh(column, this.row, widthSpan, heightSpan, attributes));
400        }
401    
402        public JComponent[] addPropertyAndLabel(String property)
403        {
404            return addPropertyAndLabel(property, 1, row, this.labelAttributes);
405        }
406    
407        public JComponent[] addPropertyAndLabel(String property, int column)
408        {
409            return addPropertyAndLabel(property, column, row, this.labelAttributes);
410        }
411    
412        public JComponent[] addPropertyAndLabel(String property, String binderId)
413        {
414            return addPropertyAndLabel(property, binderId, 1, row, this.labelAttributes);
415        }
416    
417        public JComponent[] addPropertyAndLabel(String property, int column, String binderId)
418        {
419            return addPropertyAndLabel(property, binderId, column, row, this.labelAttributes);
420        }
421    
422        public JComponent[] addPropertyAndLabel(String property, int column, int row)
423        {
424            return addPropertyAndLabel(property, column, row, this.labelAttributes);
425        }
426    
427        public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row)
428        {
429            return addPropertyAndLabel(property, binderId, column, row, this.labelAttributes);
430        }
431    
432        public JComponent[] addPropertyAndLabel(String property, int column, int row, String attributes)
433        {
434            return addPropertyAndLabel(property, column, row, 1, attributes);
435        }
436    
437        public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row,
438                                                String attributes)
439        {
440            return addPropertyAndLabel(property, binderId, column, row, 1, attributes);
441        }
442    
443        public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan)
444        {
445            return addPropertyAndLabel(property, column, row, widthSpan, this.labelAttributes);
446        }
447    
448        public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan,
449                                                String attributes)
450        {
451            return addPropertyAndLabel(property, column, row, widthSpan, 1, attributes);
452        }
453    
454        public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row,
455                                                int widthSpan, String attributes)
456        {
457            return addPropertyAndLabel(property, binderId, column, row, widthSpan, 1, attributes);
458        }
459    
460        public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan,
461                                                int heightSpan)
462        {
463            return addPropertyAndLabel(property, column, row, widthSpan, heightSpan, this.labelAttributes);
464        }
465    
466        public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row,
467                                                int widthSpan, int heightSpan)
468        {
469            return addPropertyAndLabel(property, binderId, column, row, widthSpan, heightSpan,
470                    this.labelAttributes);
471        }
472    
473        public JComponent[] addPropertyAndLabel(String property, int column, int row, int widthSpan,
474                                                int heightSpan, String attributes)
475        {
476            JComponent component = addProperty(property, column + 2, row, widthSpan, heightSpan);
477            JLabel label = addLabel(property, component, column, row, 1, 1, attributes);
478            return new JComponent[]{label, component};
479        }
480    
481        public JComponent[] addPropertyAndLabel(String property, String binderId, int column, int row,
482                                                int widthSpan, int heightSpan, String attributes)
483        {
484            JComponent component = addProperty(property, binderId, column + 2, row, widthSpan, heightSpan);
485            JLabel label = addLabel(property, component, column, row, 1, 1, attributes);
486            return new JComponent[]{label, component};
487        }
488    
489        public JPanel getPanel()
490        {
491            getBindingFactory().getFormModel().revert();
492            return this.panel;
493        }
494    
495        public void setLabelAttributes(String attributes)
496        {
497            this.labelAttributes = attributes;
498        }
499    
500        public void setComponentAttributes(String attributes)
501        {
502            this.componentAttributes = attributes;
503        }
504    
505        public JComponent[] addTextArea(String propertyName, int column, int row)
506        {
507            return addTextArea(propertyName, column, row, 1, 1);
508        }
509    
510        public JComponent[] addTextArea(String propertyName, int column)
511        {
512            return addTextArea(propertyName, column, row, 1, 1);
513        }
514    
515        public JComponent[] addTextArea(String propertyName)
516        {
517            return addTextArea(propertyName, 1, row, 1, 1);
518        }
519    
520        public JComponent[] addTextArea(String propertyName, int column, int row, int widthSpan, int heightSpan)
521        {
522            return addTextArea(propertyName, column, row, widthSpan, heightSpan,
523                    ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
524                    ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
525        }
526    
527        public JComponent[] addTextArea(String propertyName, int column, int row, int widthSpan, int heightSpan,
528                                        int vsbPolicy, int hsbPolicy)
529        {
530            JComponent textArea = createTextArea(propertyName);
531            createBinding(propertyName, textArea);
532            JScrollPane scrollPane = new JScrollPane(textArea, vsbPolicy, hsbPolicy);
533            addComponent(scrollPane, column, row, widthSpan, heightSpan);
534            return new JComponent[]{textArea, scrollPane};
535        }
536    
537        public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row)
538        {
539            return addTextAreaAndLabel(propertyName, column, row, 1, 1, this.labelAttributes);
540        }
541    
542        public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row, String attributes)
543        {
544            return addTextAreaAndLabel(propertyName, column, row, 1, 1, attributes);
545        }
546    
547        public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row, int widthSpan,
548                                                int heightSpan)
549        {
550            return addTextAreaAndLabel(propertyName, column, row, widthSpan, heightSpan, this.labelAttributes);
551        }
552    
553        public JComponent[] addTextAreaAndLabel(String propertyName, int column, int row, int widthSpan,
554                                                int heightSpan, String attributes)
555        {
556            JLabel label = addLabel(propertyName, column, row, attributes);
557            JComponent[] components = addTextArea(propertyName, column + 2, row, widthSpan, heightSpan);
558            return new JComponent[]{label, components[0], components[1]};
559        }
560    
561        public JComponent addPasswordField(String propertyName, int column)
562        {
563            return addPasswordField(propertyName, column, row, 1, 1);
564        }
565    
566        public JComponent addPasswordField(String propertyName)
567        {
568            return addPasswordField(propertyName, 1, row);
569        }
570    
571        public JComponent addPasswordField(String propertyName, int column, int row)
572        {
573            return addPasswordField(propertyName, column, row, 1, 1);
574        }
575    
576        public JComponent addPasswordField(String propertyName, int column, int row, int widthSpan, int heightSpan)
577        {
578            JComponent passwordField = createBinding(propertyName, createPasswordField(propertyName))
579                    .getControl();
580            addComponent(passwordField, column, row, widthSpan, heightSpan);
581            return passwordField;
582        }
583    
584        public JComponent[] addPasswordFieldAndLabel(String propertyName, int column)
585        {
586            return addPasswordFieldAndLabel(propertyName, column, row, 1, 1);
587        }
588    
589        public JComponent[] addPasswordFieldAndLabel(String propertyName)
590        {
591            return addPasswordFieldAndLabel(propertyName, 1, row);
592        }
593    
594        public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row)
595        {
596            return addPasswordFieldAndLabel(propertyName, column, row, 1, 1, this.labelAttributes);
597        }
598    
599        public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row, String attributes)
600        {
601            return addPasswordFieldAndLabel(propertyName, column, row, 1, 1, attributes);
602        }
603    
604        public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row, int widthSpan,
605                                                     int heightSpan)
606        {
607            return addPasswordFieldAndLabel(propertyName, column, row, widthSpan, heightSpan,
608                    this.labelAttributes);
609        }
610    
611        public JComponent[] addPasswordFieldAndLabel(String propertyName, int column, int row, int widthSpan,
612                                                     int heightSpan, String attributes)
613        {
614            JLabel label = addLabel(propertyName, column, row, attributes);
615            JComponent component = addPasswordField(propertyName, column + 2, row, widthSpan, heightSpan);
616            return new JComponent[]{label, component};
617        }
618    
619        public void addHorizontalSeparator(int column, int row, int widthSpan)
620        {
621            addComponent(new JSeparator(), column, row, widthSpan, 1);
622        }
623    
624        public void addHorizontalSeparator()
625        {
626            addComponent(new JSeparator(), 1, row, 1, 1);
627        }
628    
629        public void addHorizontalSeparator(int widthSpan)
630        {
631            addComponent(new JSeparator(), 1, row, widthSpan, 1);
632        }
633    
634        public void addHorizontalSeparator(int column, int widthSpan)
635        {
636            addComponent(new JSeparator(), column, row, widthSpan, 1);
637        }
638    
639        public void addHorizontalSeparator(String text)
640        {
641            addComponent(getComponentFactory().createLabeledSeparator(text), 1, row, 1, 1);
642        }
643    
644        public void addHorizontalSeparator(String text, int widthSpan)
645        {
646            addComponent(getComponentFactory().createLabeledSeparator(text), 1, row, widthSpan, 1);
647        }
648    
649        public void addHorizontalSeparator(String text, int column, int widthSpan)
650        {
651            addComponent(getComponentFactory().createLabeledSeparator(text), column, row, widthSpan, 1);
652        }    
653    
654        public void addVerticalSeparator(int column, int row, int heightSpan)
655        {
656            addComponent(new JSeparator(SwingConstants.VERTICAL), column, row, 1, heightSpan);
657        }
658    
659        public void addVerticalSeparator()
660        {
661            addComponent(new JSeparator(SwingConstants.VERTICAL), 1, row, 1, 1);
662        }
663    
664        public void addVerticalSeparator(int heightSpan)
665        {
666            addComponent(new JSeparator(SwingConstants.VERTICAL), 1, row, 1, heightSpan);
667        }
668    
669        public void addVerticalSeparator(int column, int heightSpan)
670        {
671            addComponent(new JSeparator(SwingConstants.VERTICAL), column, row, 1, heightSpan);
672        }
673    
674        public JComponent addNestedPropertyReadOnly(String property, String nestedProperty)
675        {
676            return addNestedPropertyReadOnly(property, nestedProperty, 1, row);
677        }
678    
679        public JComponent addNestedPropertyReadOnly(String property, String nestedProperty, int column)
680        {
681            return addNestedPropertyReadOnly(property, nestedProperty, column, row);
682        }
683    
684        public JComponent addNestedPropertyReadOnly(String property, String nestedProperty, int column, int row)
685        {
686            return addNestedPropertyReadOnly(property, nestedProperty, column, row, 1, 1);
687        }
688    
689        public JComponent addNestedPropertyReadOnly(String property, String nestedProperty, int column, int row,
690                                            int widthSpan, int heightSpan)
691        {
692            final JTextField nestedPropertyField = new JTextField();
693            nestedPropertyField.setEditable(false);
694            getFormModel().getValueModel(property).addValueChangeListener(
695                    new NestedPropertyChangeListener(nestedPropertyField, nestedProperty));
696            getFormModelEnabledListener().add(nestedPropertyField);
697            addComponent(nestedPropertyField, column, row, widthSpan, heightSpan);
698            return nestedPropertyField;
699        }
700    
701        public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty)
702        {
703            return addNestedPropertyReadOnlyAndLabel(property, nestedProperty, 1, row);
704        }
705    
706        public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty, int column)
707        {
708            return addNestedPropertyReadOnlyAndLabel(property, nestedProperty, column, row);
709        }
710    
711        public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty, int column, int row)
712        {
713            return addNestedPropertyReadOnlyAndLabel(property, nestedProperty, column, row, 1, 1);
714        }
715    
716        public JComponent[] addNestedPropertyReadOnlyAndLabel(String property, String nestedProperty, int column, int row,
717                                            int widthSpan, int heightSpan)
718        {
719            final JTextField nestedPropertyField = new JTextField();
720            nestedPropertyField.setEditable(false);
721            getFormModel().getValueModel(property).addValueChangeListener(
722                    new NestedPropertyChangeListener(nestedPropertyField, nestedProperty));
723            getFormModelEnabledListener().add(nestedPropertyField);
724            JComponent comp = addNestedPropertyReadOnly(property, nestedProperty, column + 2, row, widthSpan, heightSpan);
725            JLabel label = addLabel(property + "." + nestedProperty, comp, column, row, 1, 1, this.labelAttributes);
726            return new JComponent[] { label, comp };
727        }
728    
729        private FormModelEnabledListener formModelEnabledListener;
730    
731        public FormModelEnabledListener getFormModelEnabledListener()
732        {
733            if (formModelEnabledListener == null)
734            {
735                formModelEnabledListener = new FormModelEnabledListener();
736                getFormModel().addPropertyChangeListener(FormModel.ENABLED_PROPERTY, formModelEnabledListener);
737            }
738            return formModelEnabledListener;
739        }
740    
741        public static class FormModelEnabledListener implements PropertyChangeListener
742        {
743    
744            private List<JComponent> components = new ArrayList<JComponent>();
745    
746            public void propertyChange(PropertyChangeEvent evt)
747            {
748                for (JComponent component : components)
749                {
750                    component.setEnabled((Boolean) evt.getNewValue());
751                }
752            }
753    
754            public void add(JComponent component)
755            {
756                components.add(component);
757            }
758    
759        }
760    }