~ubuntu-branches/ubuntu/trusty/libjgoodies-forms-java/trusty

« back to all changes in this revision

Viewing changes to src/core/com/jgoodies/forms/builder/PanelBuilder.java

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2010-02-11 16:00:46 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100211160046-e96n4h9fqbffl79b
Tags: 1.3.0-1
* New upstream release
* Bump Standards-Version to 3.8.4
* (Build)-Depends on default-jdk-doc (Closes: #567281)
* Switch to source format 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (c) 2002-2008 JGoodies Karsten Lentzsch. All Rights Reserved.
 
2
 * Copyright (c) 2002-2009 JGoodies Karsten Lentzsch. All Rights Reserved.
3
3
 *
4
4
 * Redistribution and use in source and binary forms, with or without
5
5
 * modification, are permitted provided that the following conditions are met:
32
32
 
33
33
import java.awt.Color;
34
34
import java.awt.Component;
 
35
import java.lang.ref.WeakReference;
35
36
 
36
 
import javax.swing.JComponent;
37
 
import javax.swing.JLabel;
38
 
import javax.swing.JPanel;
39
 
import javax.swing.SwingConstants;
 
37
import javax.swing.*;
40
38
import javax.swing.border.Border;
41
39
 
42
40
import com.jgoodies.forms.factories.Borders;
43
41
import com.jgoodies.forms.factories.ComponentFactory;
 
42
import com.jgoodies.forms.factories.ComponentFactory2;
44
43
import com.jgoodies.forms.factories.DefaultComponentFactory;
45
44
import com.jgoodies.forms.layout.CellConstraints;
46
45
import com.jgoodies.forms.layout.FormLayout;
51
50
 * to set a default border and to add labels, titles and titled separators.<p>
52
51
 *
53
52
 * The PanelBuilder is the working horse for layouts when more specialized
54
 
 * builders like the {@link ButtonBarBuilder} or {@link DefaultFormBuilder}
 
53
 * builders like the {@link ButtonBarBuilder2} or {@link DefaultFormBuilder}
55
54
 * are inappropriate.<p>
56
55
 *
57
56
 * The Forms tutorial includes several examples that present and compare
72
71
 * This example creates a panel with 3 columns and 3 rows.
73
72
 * <pre>
74
73
 * FormLayout layout = new FormLayout(
75
 
 *      "right:pref, 6dlu, 50dlu, 4dlu, default",  // columns
76
 
 *      "pref, 3dlu, pref, 3dlu, pref");           // rows
 
74
 *      "pref, $lcgap, 50dlu, $rgap, default",  // columns
 
75
 *      "pref, $lg, pref, $lg, pref");          // rows
77
76
 *
78
77
 * PanelBuilder builder = new PanelBuilder(layout);
79
78
 * CellConstraints cc = new CellConstraints();
80
 
 * builder.addLabel("&Title",      cc.xy  (1, 1));
81
 
 * builder.add(new JTextField(),   cc.xywh(3, 1, 3, 1));
82
 
 * builder.addLabel("&Price",      cc.xy  (1, 3));
83
 
 * builder.add(new JTextField(),   cc.xy  (3, 3));
84
 
 * builder.addLabel("&Author",     cc.xy  (1, 5));
85
 
 * builder.add(new JTextField(),   cc.xy  (3, 5));
86
 
 * builder.add(new JButton("..."), cc.xy  (5, 5));
 
79
 * builder.addLabel("&Title:",        cc.xy  (1, 1));
 
80
 * builder.add(new JTextField(),      cc.xywh(3, 1, 3, 1));
 
81
 * builder.addLabel("&Price:",        cc.xy  (1, 3));
 
82
 * builder.add(new JTextField(),      cc.xy  (3, 3));
 
83
 * builder.addLabel("&Author:",       cc.xy  (1, 5));
 
84
 * builder.add(new JTextField(),      cc.xy  (3, 5));
 
85
 * builder.add(new JButton("\u2026"), cc.xy  (5, 5));
87
86
 * return builder.getPanel();
88
87
 * </pre>
89
88
 *
90
89
 * @author  Karsten Lentzsch
91
 
 * @version $Revision: 1.7 $
 
90
 * @version $Revision: 1.15 $
92
91
 *
93
92
 * @see com.jgoodies.forms.factories.ComponentFactory
94
93
 * @see     I15dPanelBuilder
96
95
 */
97
96
public class PanelBuilder extends AbstractFormBuilder {
98
97
 
 
98
    // Constants **************************************************************
 
99
 
 
100
    /**
 
101
     * A JComponent client property that is used to determine the label
 
102
     * labeling a component. Copied from the JLabel class.
 
103
     */
 
104
    private static final String LABELED_BY_PROPERTY = "labeledBy";
 
105
 
 
106
 
 
107
    // Static Fields **********************************************************
 
108
 
 
109
    /**
 
110
     * The global default for the enablement of the setLabelFor feature.
 
111
     * Turned off by default.
 
112
     *
 
113
     * @see #setLabelForFeatureEnabledDefault(boolean)
 
114
     * @see #setLabelForFeatureEnabled(boolean)
 
115
     */
 
116
    private static boolean labelForFeatureEnabledDefault = false;
 
117
 
 
118
 
 
119
    // Instance Fields ********************************************************
 
120
 
99
121
    /**
100
122
     * Refers to a factory that is used to create labels,
101
123
     * titles and paragraph separators.
103
125
    private ComponentFactory componentFactory;
104
126
 
105
127
 
106
 
    // Instance Creation ****************************************************
 
128
    /**
 
129
     * The instance value for the setLabelFor feature.
 
130
     * Is initialized using the global default.
 
131
     *
 
132
     * @see #setLabelForFeatureEnabled(boolean)
 
133
     * @see #setLabelForFeatureEnabledDefault(boolean)
 
134
     */
 
135
    private boolean labelForFeatureEnabled;
 
136
 
 
137
 
 
138
    /**
 
139
     * Refers to the most recently added label.
 
140
     * Used to invoke {@link JLabel#setLabelFor(java.awt.Component)}
 
141
     * for the next component added to the panel that is applicable for
 
142
     * this feature (for example focusable). After the association
 
143
     * has been set, the reference will be cleared.
 
144
     *
 
145
     * @see #add(Component, CellConstraints)
 
146
     */
 
147
    private WeakReference mostRecentlyAddedLabelReference = null;
 
148
 
 
149
 
 
150
    // Instance Creation ******************************************************
107
151
 
108
152
    /**
109
153
     * Constructs a <code>PanelBuilder</code> for the given
125
169
     */
126
170
    public PanelBuilder(FormLayout layout, JPanel panel){
127
171
        super(layout, panel);
128
 
    }
129
 
 
130
 
 
131
 
    // Accessors ************************************************************
 
172
        labelForFeatureEnabled = labelForFeatureEnabledDefault;
 
173
    }
 
174
 
 
175
 
 
176
    // Global Defaults ********************************************************
 
177
 
 
178
    /**
 
179
     * Returns the global default for the enablement of the setLabelFor feature.
 
180
     * This can be overridden per PanelBuilder using
 
181
     * {@link #setLabelForFeatureEnabled(boolean)}.
 
182
     * The feature is globally disabled by default.
 
183
     *
 
184
     * @return true for globally enabled, false for globally disabled
 
185
     */
 
186
    public static boolean getLabelForFeatureEnabledDefault() {
 
187
        return labelForFeatureEnabledDefault;
 
188
    }
 
189
 
 
190
 
 
191
    /**
 
192
     * Sets the default value for the setLabelFor feature enablement.
 
193
     * This can be overridden per PanelBuilder using
 
194
     * {@link #setLabelForFeatureEnabled(boolean)}.
 
195
     * The default value is used to set the initial PanelBuilder
 
196
     * setting for this feature.
 
197
     * The feature is globally disabled by default.
 
198
     *
 
199
     * @param b true for globally enabled, false for globally disabled
 
200
     */
 
201
    public static void setLabelForFeatureEnabledDefault(boolean b) {
 
202
        labelForFeatureEnabledDefault = b;
 
203
    }
 
204
 
 
205
 
 
206
    // Configuration **********************************************************
 
207
 
 
208
    /**
 
209
     * Returns whether the setLabelFor feature is enabled for this PanelBuilder.
 
210
     * The value is initialized from the global default value for this feature
 
211
     * {@link #getLabelForFeatureEnabledDefault()}. It is globally disabled
 
212
     * by default.
 
213
     *
 
214
     * @return true for enabled, false for disabled
 
215
     */
 
216
    public boolean isLabelForFeatureEnabled() {
 
217
        return labelForFeatureEnabled;
 
218
    }
 
219
 
 
220
 
 
221
    /**
 
222
     * Enables or disables the setLabelFor feature for this PanelBuilder.
 
223
     * The value is initialized from the global default value
 
224
     * {@link #getLabelForFeatureEnabledDefault()}. It is globally disabled
 
225
     * by default.
 
226
     *
 
227
     * @param b true for enabled, false for disabled
 
228
     */
 
229
    public void setLabelForFeatureEnabled(boolean b) {
 
230
        labelForFeatureEnabled = b;
 
231
    }
 
232
 
 
233
 
 
234
    // Accessors **************************************************************
132
235
 
133
236
    /**
134
237
     * Returns the panel used to build the form.
198
301
     * Adds a textual label to the form using the default constraints.<p>
199
302
     *
200
303
     * <pre>
201
 
     * addLabel("Name");       // No Mnemonic
202
 
     * addLabel("N&ame");      // Mnemonic is 'a'
203
 
     * addLabel("Save &as");   // Mnemonic is the second 'a'
204
 
     * addLabel("Look&&Feel"); // No mnemonic, text is "look&feel"
 
304
     * addLabel("Name:");       // No Mnemonic
 
305
     * addLabel("N&ame:");      // Mnemonic is 'a'
 
306
     * addLabel("Save &as:");   // Mnemonic is the second 'a'
 
307
     * addLabel("Look&&Feel:"); // No mnemonic, text is "look&feel"
205
308
     * </pre>
206
309
     *
207
310
     * @param textWithMnemonic   the label's text -
219
322
     * Adds a textual label to the form using the specified constraints.<p>
220
323
     *
221
324
     * <pre>
222
 
     * addLabel("Name",       cc.xy(1, 1)); // No Mnemonic
223
 
     * addLabel("N&ame",      cc.xy(1, 1)); // Mnemonic is 'a'
224
 
     * addLabel("Save &as",   cc.xy(1, 1)); // Mnemonic is the second 'a'
225
 
     * addLabel("Look&&Feel", cc.xy(1, 1)); // No mnemonic, text is "look&feel"
 
325
     * addLabel("Name:",       cc.xy(1, 1)); // No Mnemonic
 
326
     * addLabel("N&ame:",      cc.xy(1, 1)); // Mnemonic is 'a'
 
327
     * addLabel("Save &as:",   cc.xy(1, 1)); // Mnemonic is the second 'a'
 
328
     * addLabel("Look&&Feel:", cc.xy(1, 1)); // No mnemonic, text is "look&feel"
226
329
     * </pre>
227
330
     *
228
331
     * @param textWithMnemonic  the label's text -
243
346
     * Adds a textual label to the form using the specified constraints.<p>
244
347
     *
245
348
     * <pre>
246
 
     * addLabel("Name",       "1, 1"); // No Mnemonic
247
 
     * addLabel("N&ame",      "1, 1"); // Mnemonic is 'a'
248
 
     * addLabel("Save &as",   "1, 1"); // Mnemonic is the second 'a'
249
 
     * addLabel("Look&&Feel", "1, 1"); // No mnemonic, text is "look&feel"
 
349
     * addLabel("Name:",       "1, 1"); // No Mnemonic
 
350
     * addLabel("N&ame:",      "1, 1"); // Mnemonic is 'a'
 
351
     * addLabel("Save &as:",   "1, 1"); // Mnemonic is the second 'a'
 
352
     * addLabel("Look&&Feel:", "1, 1"); // No mnemonic, text is "look&feel"
250
353
     * </pre>
251
354
     *
252
355
     * @param textWithMnemonic    the label's text -
261
364
    }
262
365
 
263
366
 
264
 
    // Adding Label with related Component ************************************
265
 
 
266
 
    /**
267
 
     * Adds a label and component to the panel using the given cell constraints.
268
 
     * Sets the given label as <i>the</i> component label using
269
 
     * {@link JLabel#setLabelFor(java.awt.Component)}.<p>
270
 
     *
271
 
     * <strong>Note:</strong> The {@link CellConstraints} objects for the label
272
 
     * and the component must be different. Cell constraints are implicitly
273
 
     * cloned by the <code>FormLayout</code> when added to the container.
274
 
     * However, in this case you may be tempted to reuse a
275
 
     * <code>CellConstraints</code> object in the same way as with many other
276
 
     * builder methods that require a single <code>CellConstraints</code>
277
 
     * parameter.
278
 
     * The pitfall is that the methods <code>CellConstraints.xy*(...)</code>
279
 
     * just set the coordinates but do <em>not</em> create a new instance.
280
 
     * And so the second invocation of <code>xy*(...)</code> overrides
281
 
     * the settings performed in the first invocation before the object
282
 
     * is cloned by the <code>FormLayout</code>.<p>
283
 
     *
284
 
     * <strong>Wrong:</strong><pre>
285
 
     * CellConstraints cc = new CellConstraints();
286
 
     * builder.add(
287
 
     *     nameLabel,
288
 
     *     cc.xy(1, 7),         // will be modified by the code below
289
 
     *     nameField,
290
 
     *     cc.xy(3, 7)          // sets the single instance to (3, 7)
291
 
     * );
292
 
     * </pre>
293
 
     * <strong>Correct:</strong><pre>
294
 
     * // Using a single CellConstraints instance and cloning
295
 
     * CellConstraints cc = new CellConstraints();
296
 
     * builder.add(
297
 
     *     nameLabel,
298
 
     *     (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification
299
 
     *     nameField,
300
 
     *     cc.xy(3, 7)                            // sets this instance to (3, 7)
301
 
     * );
302
 
     *
303
 
     * // Using two CellConstraints instances
304
 
     * CellConstraints cc1 = new CellConstraints();
305
 
     * CellConstraints cc2 = new CellConstraints();
306
 
     * builder.add(
307
 
     *     nameLabel,
308
 
     *     cc1.xy(1, 7),       // sets instance 1 to (1, 7)
309
 
     *     nameField,
310
 
     *     cc2.xy(3, 7)        // sets instance 2 to (3, 7)
311
 
     * );
312
 
     * </pre>
313
 
     *
314
 
     * @param label                 the label to add
315
 
     * @param labelConstraints      the label's cell constraints
316
 
     * @param component             the component to add
317
 
     * @param componentConstraints  the component's cell constraints
318
 
     * @return the added label
319
 
     * @throws IllegalArgumentException if the same cell constraints instance
320
 
     *     is used for the label and the component
321
 
     *
322
 
     * @see JLabel#setLabelFor(java.awt.Component)
323
 
     * @see DefaultFormBuilder
324
 
     */
325
 
    public final JLabel add(JLabel label,        CellConstraints labelConstraints,
326
 
                            Component component, CellConstraints componentConstraints) {
327
 
        if (labelConstraints == componentConstraints)
328
 
            throw new IllegalArgumentException(
329
 
                    "You must provide two CellConstraints instances, " +
330
 
                    "one for the label and one for the component.\n" +
331
 
                        "Consider using #clone(). See the JavaDocs for details.");
332
 
 
333
 
        add(label,     labelConstraints);
334
 
        add(component, componentConstraints);
335
 
        label.setLabelFor(component);
336
 
        return label;
337
 
    }
338
 
 
339
 
 
340
367
    /**
341
368
     * Adds a label and component to the panel using the given cell constraints.
342
369
     * Sets the given label as <i>the</i> component label using
414
441
    }
415
442
 
416
443
 
 
444
    // Adding Labels for Read-Only Components ---------------------------------
 
445
 
 
446
    /**
 
447
     * Adds a textual label intended for labeling read-only components
 
448
     * to the form using the default constraints.<p>
 
449
     *
 
450
     * <pre>
 
451
     * addROLabel("Name:");       // No Mnemonic
 
452
     * addROLabel("N&ame:");      // Mnemonic is 'a'
 
453
     * addROLabel("Save &as:");   // Mnemonic is the second 'a'
 
454
     * addROLabel("Look&&Feel:"); // No mnemonic, text is "look&feel"
 
455
     * </pre>
 
456
     *
 
457
     * @param textWithMnemonic   the label's text -
 
458
     *     may contain an ampersand (<tt>&amp;</tt>) to mark a mnemonic
 
459
     * @return the new label
 
460
     *
 
461
     * @see ComponentFactory2
 
462
     *
 
463
     * @since 1.3
 
464
     */
 
465
    public final JLabel addROLabel(String textWithMnemonic) {
 
466
        return addROLabel(textWithMnemonic, cellConstraints());
 
467
    }
 
468
 
 
469
 
 
470
    /**
 
471
     * Adds a textual label intended for labeling read-only components
 
472
     * to the form using the specified constraints.<p>
 
473
     *
 
474
     * <pre>
 
475
     * addROLabel("Name:",       cc.xy(1, 1)); // No Mnemonic
 
476
     * addROLabel("N&ame:",      cc.xy(1, 1)); // Mnemonic is 'a'
 
477
     * addROLabel("Save &as:",   cc.xy(1, 1)); // Mnemonic is the second 'a'
 
478
     * addROLabel("Look&&Feel:", cc.xy(1, 1)); // No mnemonic, text is "look&feel"
 
479
     * </pre>
 
480
     *
 
481
     * @param textWithMnemonic  the label's text -
 
482
     *     may contain an ampersand (<tt>&amp;</tt>) to mark a mnemonic
 
483
     * @param constraints       the label's cell constraints
 
484
     * @return the new label
 
485
     *
 
486
     * @see ComponentFactory2
 
487
     *
 
488
     * @since 1.3
 
489
     */
 
490
    public final JLabel addROLabel(String textWithMnemonic, CellConstraints constraints) {
 
491
        ComponentFactory factory = getComponentFactory();
 
492
        ComponentFactory2 factory2;
 
493
        if (factory instanceof ComponentFactory2) {
 
494
            factory2 = (ComponentFactory2) factory;
 
495
        } else {
 
496
            factory2 = DefaultComponentFactory.getInstance();
 
497
        }
 
498
        JLabel label = factory2.createReadOnlyLabel(textWithMnemonic);
 
499
        add(label, constraints);
 
500
        return label;
 
501
    }
 
502
 
 
503
 
 
504
    /**
 
505
     * Adds a textual label intended for labeling read-only components
 
506
     * to the form using the specified constraints.<p>
 
507
     *
 
508
     * <pre>
 
509
     * addROLabel("Name:",       "1, 1"); // No Mnemonic
 
510
     * addROLabel("N&ame:",      "1, 1"); // Mnemonic is 'a'
 
511
     * addROLabel("Save &as:",   "1, 1"); // Mnemonic is the second 'a'
 
512
     * addROLabel("Look&&Feel:", "1, 1"); // No mnemonic, text is "look&feel"
 
513
     * </pre>
 
514
     *
 
515
     * @param textWithMnemonic    the label's text -
 
516
     *     may contain an ampersand (<tt>&amp;</tt>) to mark a mnemonic
 
517
     * @param encodedConstraints  a string representation for the constraints
 
518
     * @return the new label
 
519
     *
 
520
     * @see ComponentFactory2
 
521
     *
 
522
     * @since 1.3
 
523
     */
 
524
    public final JLabel addROLabel(String textWithMnemonic, String encodedConstraints) {
 
525
        return addROLabel(textWithMnemonic, new CellConstraints(encodedConstraints));
 
526
    }
 
527
 
 
528
 
 
529
    /**
 
530
     * Adds a label and component to the panel using the given cell constraints.
 
531
     * Sets the given label as <i>the</i> component label using
 
532
     * {@link JLabel#setLabelFor(java.awt.Component)}.<p>
 
533
     *
 
534
     * <strong>Note:</strong> The {@link CellConstraints} objects for the label
 
535
     * and the component must be different. Cell constraints are implicitly
 
536
     * cloned by the FormLayout when added to the container.
 
537
     * However, in this case you may be tempted to reuse a
 
538
     * <code>CellConstraints</code> object in the same way as with many other
 
539
     * builder methods that require a single <code>CellConstraints</code>
 
540
     * parameter.
 
541
     * The pitfall is that the methods <code>CellConstraints.xy*(...)</code>
 
542
     * just set the coordinates but do <em>not</em> create a new instance.
 
543
     * And so the second invocation of <code>xy*(...)</code> overrides
 
544
     * the settings performed in the first invocation before the object
 
545
     * is cloned by the <code>FormLayout</code>.<p>
 
546
     *
 
547
     * <strong>Wrong:</strong><pre>
 
548
     * builder.addROLabel(
 
549
     *     "&Name:",            // Mnemonic is 'N'
 
550
     *     cc.xy(1, 7),         // will be modified by the code below
 
551
     *     nameField,
 
552
     *     cc.xy(3, 7)          // sets the single instance to (3, 7)
 
553
     * );
 
554
     * </pre>
 
555
     * <strong>Correct:</strong><pre>
 
556
     * // Using a single CellConstraints instance and cloning
 
557
     * CellConstraints cc = new CellConstraints();
 
558
     * builder.addROLabel(
 
559
     *     "&Name:",
 
560
     *     (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification
 
561
     *     nameField,
 
562
     *     cc.xy(3, 7)                            // sets this instance to (3, 7)
 
563
     * );
 
564
     *
 
565
     * // Using two CellConstraints instances
 
566
     * CellConstraints cc1 = new CellConstraints();
 
567
     * CellConstraints cc2 = new CellConstraints();
 
568
     * builder.addROLabel(
 
569
     *     "&Name:",           // Mnemonic is 'N'
 
570
     *     cc1.xy(1, 7),       // sets instance 1 to (1, 7)
 
571
     *     nameField,
 
572
     *     cc2.xy(3, 7)        // sets instance 2 to (3, 7)
 
573
     * );
 
574
     * </pre>
 
575
     *
 
576
     * @param textWithMnemonic      the label's text -
 
577
     *     may contain an ampersand (<tt>&amp;</tt>) to mark a mnemonic
 
578
     * @param labelConstraints      the label's cell constraints
 
579
     * @param component             the component to add
 
580
     * @param componentConstraints  the component's cell constraints
 
581
     * @return the added label
 
582
     * @throws IllegalArgumentException if the same cell constraints instance
 
583
     *     is used for the label and the component
 
584
     *
 
585
     * @see JLabel#setLabelFor(java.awt.Component)
 
586
     * @see ComponentFactory2
 
587
     * @see DefaultFormBuilder
 
588
     *
 
589
     * @since 1.3
 
590
     */
 
591
    public final JLabel addROLabel(
 
592
        String textWithMnemonic, CellConstraints labelConstraints,
 
593
        Component component,     CellConstraints componentConstraints) {
 
594
 
 
595
        if (labelConstraints == componentConstraints)
 
596
            throw new IllegalArgumentException(
 
597
                    "You must provide two CellConstraints instances, " +
 
598
                    "one for the label and one for the component.\n" +
 
599
                    "Consider using #clone(). See the JavaDocs for details.");
 
600
 
 
601
        JLabel label = addROLabel(textWithMnemonic, labelConstraints);
 
602
        add(component, componentConstraints);
 
603
        label.setLabelFor(component);
 
604
        return label;
 
605
    }
 
606
 
 
607
 
 
608
 
417
609
    // Adding Titles ----------------------------------------------------------
418
610
 
419
611
    /**
570
762
    }
571
763
 
572
764
 
 
765
    /**
 
766
     * Adds a label and component to the panel using the given cell constraints.
 
767
     * Sets the given label as <i>the</i> component label using
 
768
     * {@link JLabel#setLabelFor(java.awt.Component)}.<p>
 
769
     *
 
770
     * <strong>Note:</strong> The {@link CellConstraints} objects for the label
 
771
     * and the component must be different. Cell constraints are implicitly
 
772
     * cloned by the <code>FormLayout</code> when added to the container.
 
773
     * However, in this case you may be tempted to reuse a
 
774
     * <code>CellConstraints</code> object in the same way as with many other
 
775
     * builder methods that require a single <code>CellConstraints</code>
 
776
     * parameter.
 
777
     * The pitfall is that the methods <code>CellConstraints.xy*(...)</code>
 
778
     * just set the coordinates but do <em>not</em> create a new instance.
 
779
     * And so the second invocation of <code>xy*(...)</code> overrides
 
780
     * the settings performed in the first invocation before the object
 
781
     * is cloned by the <code>FormLayout</code>.<p>
 
782
     *
 
783
     * <strong>Wrong:</strong><pre>
 
784
     * CellConstraints cc = new CellConstraints();
 
785
     * builder.add(
 
786
     *     nameLabel,
 
787
     *     cc.xy(1, 7),         // will be modified by the code below
 
788
     *     nameField,
 
789
     *     cc.xy(3, 7)          // sets the single instance to (3, 7)
 
790
     * );
 
791
     * </pre>
 
792
     * <strong>Correct:</strong><pre>
 
793
     * // Using a single CellConstraints instance and cloning
 
794
     * CellConstraints cc = new CellConstraints();
 
795
     * builder.add(
 
796
     *     nameLabel,
 
797
     *     (CellConstraints) cc.xy(1, 7).clone(), // cloned before the next modification
 
798
     *     nameField,
 
799
     *     cc.xy(3, 7)                            // sets this instance to (3, 7)
 
800
     * );
 
801
     *
 
802
     * // Using two CellConstraints instances
 
803
     * CellConstraints cc1 = new CellConstraints();
 
804
     * CellConstraints cc2 = new CellConstraints();
 
805
     * builder.add(
 
806
     *     nameLabel,
 
807
     *     cc1.xy(1, 7),       // sets instance 1 to (1, 7)
 
808
     *     nameField,
 
809
     *     cc2.xy(3, 7)        // sets instance 2 to (3, 7)
 
810
     * );
 
811
     * </pre>
 
812
     *
 
813
     * @param label                 the label to add
 
814
     * @param labelConstraints      the label's cell constraints
 
815
     * @param component             the component to add
 
816
     * @param componentConstraints  the component's cell constraints
 
817
     * @return the added label
 
818
     * @throws IllegalArgumentException if the same cell constraints instance
 
819
     *     is used for the label and the component
 
820
     *
 
821
     * @see JLabel#setLabelFor(java.awt.Component)
 
822
     * @see DefaultFormBuilder
 
823
     */
 
824
    public final JLabel add(JLabel label,        CellConstraints labelConstraints,
 
825
                            Component component, CellConstraints componentConstraints) {
 
826
        if (labelConstraints == componentConstraints)
 
827
            throw new IllegalArgumentException(
 
828
                    "You must provide two CellConstraints instances, " +
 
829
                    "one for the label and one for the component.\n" +
 
830
                    "Consider using #clone(). See the JavaDocs for details.");
 
831
 
 
832
        add(label,     labelConstraints);
 
833
        add(component, componentConstraints);
 
834
        label.setLabelFor(component);
 
835
        return label;
 
836
    }
 
837
 
 
838
 
573
839
    // Accessing the ComponentFactory *****************************************
574
840
 
575
841
    /**
601
867
    }
602
868
 
603
869
 
 
870
    // Overriding Superclass Behavior *****************************************
 
871
 
 
872
    /**
 
873
     * Adds a component to the panel using the given cell constraints.
 
874
     * In addition to the superclass behavior, this implementation
 
875
     * tracks the most recently added label, and associates it with
 
876
     * the next added component that is applicable for being set as component
 
877
     * for the label.
 
878
     *
 
879
     * @param component        the component to add
 
880
     * @param cellConstraints  the component's cell constraints
 
881
     * @return the added component
 
882
     *
 
883
     * @see #isLabelForFeatureEnabled()
 
884
     * @see #isLabelForApplicable(JLabel, Component)
 
885
     */
 
886
    public Component add(Component component, CellConstraints cellConstraints) {
 
887
        Component result = super.add(component, cellConstraints);
 
888
        if (!isLabelForFeatureEnabled()) {
 
889
            return result;
 
890
        }
 
891
        JLabel mostRecentlyAddedLabel = getMostRecentlyAddedLabel();
 
892
        if (   (mostRecentlyAddedLabel != null)
 
893
            && isLabelForApplicable(mostRecentlyAddedLabel, component)) {
 
894
            setLabelFor(mostRecentlyAddedLabel, component);
 
895
            clearMostRecentlyAddedLabel();
 
896
        }
 
897
        if (component instanceof JLabel) {
 
898
            setMostRecentlyAddedLabel((JLabel) component);
 
899
        }
 
900
        return result;
 
901
    }
 
902
 
 
903
 
 
904
    // Default Behavior *******************************************************
 
905
 
 
906
    /**
 
907
     * Checks and answers whether the given component shall be set
 
908
     * as component for a previously added label using
 
909
     * {@link JLabel#setLabelFor(Component)}.
 
910
     *
 
911
     * This default implementation checks whether the component is focusable,
 
912
     * and - if a JComponent - whether it is already labeled by a JLabel.
 
913
     * Subclasses may override.
 
914
     *
 
915
     * @param label        the candidate for labeling {@code component}
 
916
     * @param component    the component that could be labeled by {@code label}
 
917
     * @return true if focusable, false otherwise
 
918
     */
 
919
    protected boolean isLabelForApplicable(JLabel label, Component component) {
 
920
        // 1) Is the label labeling a component?
 
921
        if (label.getLabelFor() != null) {
 
922
            return false;
 
923
        }
 
924
 
 
925
        // 2) Is the component focusable?
 
926
        if (!component.isFocusable()) {
 
927
            return false;
 
928
        }
 
929
 
 
930
        // 3) Is the component labeled by another label?
 
931
        if (!(component instanceof JComponent)) {
 
932
            return true;
 
933
        }
 
934
        JComponent c = (JComponent) component;
 
935
        return c.getClientProperty(LABELED_BY_PROPERTY) == null;
 
936
    }
 
937
 
 
938
 
 
939
    /**
 
940
     * Sets {@code label} as labeling label for {@code component} or an
 
941
     * appropriate child. In case of a JScrollPane as given component,
 
942
     * this default implementation labels the view of the scroll pane's
 
943
     * viewport.
 
944
     *
 
945
     * @param label      the labeling label
 
946
     * @param component  the component to be labeled, or the parent of
 
947
     *   the labeled component
 
948
     */
 
949
    protected void setLabelFor(JLabel label, Component component) {
 
950
        Component labeledComponent;
 
951
        if (component instanceof JScrollPane) {
 
952
            JScrollPane scrollPane = (JScrollPane) component;
 
953
            labeledComponent = scrollPane.getViewport().getView();
 
954
        } else {
 
955
            labeledComponent = component;
 
956
        }
 
957
        label.setLabelFor(labeledComponent);
 
958
    }
 
959
 
 
960
 
 
961
    // Helper Code ************************************************************
 
962
 
 
963
    /**
 
964
     * Returns the most recently added JLabel that has a mnemonic set
 
965
     * - if any, <code>null</code>, if none has been set, or if it has
 
966
     * been cleared after setting an association before, or if it has been
 
967
     * cleared by the garbage collector.
 
968
     *
 
969
     * @return the most recently added JLabel that has a mnemonic set
 
970
     *     and has not been associated with a component applicable for this
 
971
     *     feature. <code>null</code> otherwise.
 
972
     */
 
973
    private JLabel getMostRecentlyAddedLabel() {
 
974
        if (mostRecentlyAddedLabelReference == null) {
 
975
            return null;
 
976
        }
 
977
        JLabel label = (JLabel) mostRecentlyAddedLabelReference.get();
 
978
        if (label == null) {
 
979
            return null;
 
980
        }
 
981
        return label;
 
982
    }
 
983
 
 
984
 
 
985
    /**
 
986
     * Sets the given label as most recently added label using a weak reference.
 
987
     *
 
988
     * @param label  the label to be set
 
989
     */
 
990
    private void setMostRecentlyAddedLabel(JLabel label) {
 
991
        mostRecentlyAddedLabelReference = new WeakReference(label);
 
992
    }
 
993
 
 
994
 
 
995
    /**
 
996
     * Clears the reference to the most recently added mnemonic label.
 
997
     */
 
998
    private void clearMostRecentlyAddedLabel() {
 
999
        mostRecentlyAddedLabelReference = null;
 
1000
    }
 
1001
 
 
1002
 
604
1003
}