1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing.components;
20
21 import java.awt.AWTEventMulticaster;
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.Container;
25 import java.awt.Dimension;
26 import java.awt.GridBagConstraints;
27 import java.awt.GridBagLayout;
28 import java.awt.GridLayout;
29 import java.awt.Insets;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.ActionListener;
32 import java.beans.BeanInfo;
33 import java.beans.Introspector;
34 import java.beans.MethodDescriptor;
35 import java.lang.reflect.Method;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.LinkedList;
42 import java.util.List;
43 import java.util.Map;
44
45 import javax.swing.Box;
46 import javax.swing.JLabel;
47 import javax.swing.JPanel;
48 import javax.swing.JTextField;
49 import javax.swing.SwingConstants;
50
51 /***
52 * InfoPanel uses labels and textfields (or any other component - see below)
53 * to display a list of keys and values in a well-aligned and consistent manner,
54 * conforming to alignment and pixel spacing in the java look and feel
55 * <a href="http://java.sun.com/products/jlf/dg/higg.htm#55417">design guidelines</a>.
56 * <BR><BR>
57 *
58 * Each key is displayed in a label to the left of the component that contains
59 * the corresponding value. Each row is displayed starting at the top of the
60 * component's available area. Each row's height is the maximum preferred
61 * height of its components and the field itself gets as much of the width as
62 * it can, dependent on the length of the longest label. <BR><BR>
63 *
64 * The values in the fields can be editable, and the
65 * current value can be retrieved using the key - for this reason, unique keys
66 * are recommended. <BR><BR>
67 *
68 * As a convenience, push buttons may be placed across the
69 * bottom of the panel in a manner similar to ButtonPanel. <BR><BR>
70 *
71 * The panel forwards any ActionEvents generated by the components and
72 * buttons on it to all registered listeners. <BR><BR>
73 *
74 * Optionally, any component can be used instead of a textfield.
75 * However, <code>get/setValueForKey()</code> and <code>get/setEditable()</code>
76 * may not work for those components. Use <code>getComponentForKey()</code> to
77 * access them instead.
78 *
79 * @author michael@mpowers.net
80 * @author $Author: cgruber $
81 * @version $Revision: 904 $
82 * $Date: 2006-02-18 23:19:05 +0000 (Sat, 18 Feb 2006) $
83 */
84 public class InfoPanel extends JPanel implements ActionListener
85 {
86 /***
87 * Special label for an empty pair - a label and component
88 * that take up space but are hidden from view. This might
89 * be useful for achieving certain layouts.
90 */
91 public static final String HIDDEN = "(hidden)";
92
93 /*** Cache for the introspectComponent method */
94 private static Map _method_cache =
95 Collections.synchronizedMap( new HashMap(30) );
96
97 protected Container listContainer = null;
98 protected int hgap;
99 protected int vgap;
100 protected int margin;
101 protected int columns;
102 protected List fields = null;
103 protected List labels = null;
104 protected List fieldSpacers = null;
105 protected ButtonPanel buttonPanel = null;
106 protected boolean isEditable = true;
107 protected String prefix;
108 protected String postfix;
109 protected int labelAnchor;
110 protected int labelAlign;
111
112
113
114 protected ActionListener actionListener = null;
115
116 /***
117 * Constructs an empty InfoPanel.
118 */
119 public InfoPanel()
120 {
121 hgap = 12;
122 vgap = 6;
123 columns = 1;
124 margin = 0;
125 prefix = "";
126 postfix = ":";
127 fields = new ArrayList();
128 labels = new ArrayList();
129 labelAnchor = GridBagConstraints.NORTHWEST;
130
131 labelAlign = SwingConstants.LEFT;
132
133
134 doInitialLayout();
135 }
136
137 /***
138 * Constructs an InfoPanel with the specified labels
139 * each paired with a blank textfield.
140 * @param labelArray An Array containing the labels in the
141 * order in which they should appear from top to bottom.
142 * A null value produces an empty panel.
143 */
144 public InfoPanel( String[] labelArray )
145 {
146 this();
147 setLabels( labelArray );
148 }
149
150 /***
151 * Creates a set of labels and empty textfields after first
152 * clearing all existing components on the panel.
153 * @param labelArray An Array containing the labels in the order
154 * in which they should appear from top to bottom. A null
155 * value will clear the panel.
156 */
157 public void setLabels( String[] labelArray )
158 {
159 removeAll();
160 if ( labelArray == null ) return;
161 for ( int i = 0; i < labelArray.length; i++ )
162 {
163 addPair( labelArray[i], new JTextField() );
164 }
165 }
166
167 /***
168 * Retrieves the labls for the components on the panel
169 * in the order in which they are displayed from top WIDTH bottom.
170 * These are the keys used to reference values or to reference
171 * the components directly.
172 * @return An Array of Strings containing the labels.
173 */
174 public String[] getLabels()
175 {
176 int length = fields.size();
177 String[] labelArray = new String[ length ];
178 for ( int i = 0; i < length; i++ )
179 {
180 labelArray[i] = ((Component)fields.get(i)).getName();
181 }
182 return labelArray;
183 }
184
185 /***
186 * Retrieves the constant used to anchor the labels in place.
187 * The default value is GridBagConstraints.NORTHWEST.
188 */
189 public int getLabelAnchor()
190 {
191 return labelAnchor;
192 }
193
194 /***
195 * Sets the constant used to anchor the labels in place
196 * and reflows the layout.
197 * @param anAnchorConstant An anchor constant from
198 * GridBagConstraints.
199 */
200 public void setLabelAnchor( int anAnchorConstant )
201 {
202 labelAnchor = anAnchorConstant;
203 updateLabels();
204 }
205
206 /***
207 * Retrieves the constant used to align the labels in place.
208 * The default value is GridBagConstraints.CENTER.
209 */
210 public int getLabelAlignment()
211 {
212 return labelAlign;
213 }
214
215 /***
216 * Sets the constant used to align the labels in place
217 * and reflows the layout.
218 * @param anAlignmentConstant LEFT, CENTER, or RIGHT constants
219 * from SwingUtilities.
220 */
221 public void setLabelAlignment( int anAlignmentConstant )
222 {
223 labelAlign = anAlignmentConstant;
224 updateLabels();
225 }
226
227 /***
228 * Factory method for creating panel spacers.
229 * This implementation returns a JPanel with
230 * opaque set to false. Override to customize.
231 */
232 public JPanel createPanel()
233 {
234 JPanel result = new JPanel();
235 result.setOpaque( false );
236 return result;
237 }
238
239 /***
240 * This method is responsible for the initial layout of the panel.
241 * All labels and textfields will later added to listContainer.
242 * This method is responsible for initializing listContainer.
243 */
244 protected void doInitialLayout()
245 {
246 listContainer = createPanel();
247 listContainer.setLayout( new BetterGridBagLayout() );
248 this.setLayout( new BorderLayout() );
249 this.add( listContainer, BorderLayout.NORTH );
250
251
252
253 }
254
255 /***
256 * Changes the horizontal spacing between the label and the components in the panel.
257 * Note: Assumes listContainer uses a GridBagLayout.
258 * @param newHgap the new spacing, in pixels. May not be negative.
259 */
260 public void setHgap( int newHgap )
261 {
262 if ( newHgap < 0 ) return;
263 this.hgap = newHgap;
264 updateGaps();
265 this.revalidate();
266 this.repaint();
267
268 }
269
270 /***
271 * Gets the current horizontal spacing between components.
272 * @return the current horizontal spacing, in pixels.
273 */
274 public int getHgap()
275 {
276 return this.hgap;
277 }
278
279 /***
280 * Changes the vertical spacing between components in the panel.
281 * Note: Assumes listContainer uses a GridBagLayout.
282 * @param newVgap the new spacing, in pixels. May not be negative.
283 */
284 public void setVgap( int newVgap )
285 {
286 if ( newVgap < 0 ) return;
287 this.vgap = newVgap;
288 updateGaps();
289 this.revalidate();
290 this.repaint();
291
292 }
293
294 /***
295 * Gets the current vertical spacing between components.
296 * @return the current vertical spacing, in pixels.
297 */
298 public int getVgap()
299 {
300 return this.vgap;
301 }
302
303 /***
304 * Sets the minimum width for the labels column.
305 * This left margin will grow if one of the labels
306 * is wider than this value.
307 * Note: assumes GridBagLayout.
308 * @param newMargin the new minimum margin in pixels. May not be negative.
309 */
310 public void setMargin( int newMargin )
311 {
312 if ( newMargin < 0 ) return;
313 this.margin = newMargin;
314
315 if ( listContainer.getLayout() instanceof GridBagLayout )
316 {
317 GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
318 GridBagConstraints constraints = null;
319 Component c = null;
320 int count = listContainer.getComponentCount();
321 for ( int i = 0; i < count; i++ )
322 {
323 c = listContainer.getComponent( i );
324 constraints = gridBag.getConstraints( c );
325 if ( constraints.gridy == 0 && constraints.gridx % 2 == 0 )
326 {
327
328 listContainer.remove( c );
329 listContainer.add( Box.createHorizontalStrut( this.margin ), constraints );
330 }
331 }
332 }
333
334 this.revalidate();
335 this.repaint();
336
337 }
338
339 /***
340 * Gets the current minimum margin for the labels column.
341 * @return the current minimum margin in pixels.
342 */
343 public int getMargin()
344 {
345 return this.margin;
346 }
347
348 /***
349 * Sets the number of columns for the panel.
350 * Label/Component pairs will start from the top left
351 * and fill in to the right before wrapping to the
352 * next row. The default number of columns is one.
353 * Note: assumes GridBagLayout.
354 * @param newColumns the new number of columns. May not be less than one.
355 */
356 public void setColumns( int newColumns )
357 {
358 if ( newColumns < 1 ) return;
359 int oldColumns = this.columns;
360 this.columns = newColumns;
361
362 if ( listContainer.getLayout() instanceof GridBagLayout )
363 {
364 GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
365 int count = listContainer.getComponentCount();
366 Component[] components = listContainer.getComponents();
367 GridBagConstraints[] constraints = new GridBagConstraints[ components.length ];
368 for ( int i = 0; i < components.length; i++ )
369 {
370 constraints[i] = gridBag.getConstraints( components[i] );
371 }
372 listContainer.removeAll();
373 for ( int i = 0; i < components.length; i++ )
374 {
375 if ( constraints[i].gridy != 0 )
376 {
377
378
379
380 int index = ( constraints[i].gridy - 1 ) * oldColumns*2 + constraints[i].gridx;
381 constraints[i].gridy = ( index / (newColumns*2) ) + 1;
382 constraints[i].gridx = index % (newColumns*2) ;
383 listContainer.add( components[i], constraints[i] );
384 }
385 }
386 createSpacers();
387 updateGaps();
388 }
389
390 this.revalidate();
391 this.repaint();
392
393 }
394
395 /***
396 * Sets the vertical weight used for determining how to distribute additional
397 * vertical space in the component.
398 * @param aComponent Key that exists in the layout.
399 * @return weighty The weight of the component, or -1.0 if not found.
400 */
401 public double getVerticalWeightForKey( String key )
402 {
403 Container c = getCompositeComponentForKey( key );
404 if ( c == null ) return -1.0;
405 if ( ! ( listContainer.getLayout() instanceof GridBagLayout ) ) return -1.0;
406 GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
407 GridBagConstraints gbc = layout.getConstraints( c );
408 return gbc.weighty;
409 }
410
411 /***
412 * Sets the vertical weight used for determining how to distribute additional
413 * vertical space in the component. By default, all weights are zero, so each
414 * component gets its preferred height. If any weights are specified, then
415 * additional space is allocated to those components proportionately.
416 * @param aComponent Key that exists in the layout.
417 * @param weighty The new weight.
418 */
419 public void setVerticalWeightForKey( String key, double weighty )
420 {
421 Container c = getCompositeComponentForKey( key );
422 if ( c == null ) return;
423 if ( ! ( listContainer.getLayout() instanceof GridBagLayout ) ) return;
424 GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
425 GridBagConstraints gbc = layout.getConstraints( c );
426 gbc.weighty = weighty;
427 layout.setConstraints( c, gbc );
428
429 updateGaps();
430 this.revalidate();
431 this.repaint();
432 }
433
434 /***
435 * Gets the current number of columns.
436 * @return the current number of columns.
437 */
438 public int getColumns()
439 {
440 return this.columns;
441 }
442
443 /***
444 * Appends a label containing a key and the specified component
445 * to the bottom of the panel. Any registered action listeners
446 * will receive action events from the component - the key corresponding
447 * to the component will be used as the action command.
448 * @param key A string that will be displayed in a label, preferrably unique.
449 * @param component A component that will be placed next to the label.
450 * If null, a blank JPanel will be used.
451 */
452 public void addPair( String key, Component component )
453 {
454 addRow( key, new Component[] { component } );
455 }
456
457 /***
458 * Appends a label containing a key and the specified component
459 * to the bottom of the panel. Any registered action listeners
460 * will receive action events from the component - the key corresponding
461 * to the component will be used as the action command.
462 * @param key A string that will be displayed in a label, preferrably unique.
463 * @param component A component that will be placed next to the label.
464 * If null, a blank JPanel will appear.
465 */
466 public void addRow( String key, Component component )
467 {
468 addRow( key, new Component[] { component } );
469 }
470
471 /***
472 * Appends a label containing a key and the specified components
473 * to the bottom of the panel. Any registered action listeners
474 * will receive action events from the component - the key corresponding
475 * to the component will be used as the action command.
476 * @param key A string that will be displayed in a label, preferrably unique.
477 * @param components An array of components that will be placed next to the label.
478 * Any nulls in the list will be replaced with blank JPanels.
479 */
480 public void addRow(
481 String key, Component[] components )
482 {
483 addCompositeComponent( key, makeCompositeComponent( key, components ) );
484 }
485
486 /***
487 * Appends a label containing a key and the specified components
488 * to the bottom of the panel. Any registered action listeners
489 * will receive action events from the components - the key corresponding
490 * to the component will be used as the action command.
491 * @param key A string that will be displayed in a label, preferrably unique.
492 * @param west A component that will appear to the left of the other components,
493 * as wide as its preferred width and as tall as the tallest of the other components.
494 * A null will be replaced with a blank JPanel.
495 * @param center A component that will appear between the other components,
496 * taking up available space.
497 * A null will be replaced with a blank JPanel.
498 * @param east A component that will appear to the right of the other components,
499 * as wide as its preferred width and as tall as the tallest of the other components.
500 * A null will be replaced with a blank JPanel.
501 */
502 public void addRow(
503 String key, Component west, Component center, Component east )
504 {
505 addCompositeComponent( key,
506 makeCompositeComponent( key,
507 west, center, east ) );
508 }
509
510 /***
511 * Appends a label containing a key and the specified components
512 * to the bottom of the panel. Any registered action listeners
513 * will receive action events from the components - the key corresponding
514 * to the component will be used as the action command.
515 * @param key A string that will be displayed in a label, preferrably unique.
516 * @param west A component that will appear to the left of the other components,
517 * as wide as its preferred width and as tall as the tallest of the other components.
518 * A null will be replaced with a blank JPanel.
519 * @param north A component that will appear above all the other components,
520 * as tall as its preferred height and as wide as the info panel itself.
521 * @param center A component that will appear between the other components,
522 * taking up available space. A null will be replaced with a blank JPanel.
523 * @param south A component that will appear below all the other components,
524 * as tall as its preferred height and as wide as the info panel itself.
525 * @param east A component that will appear to the right of the other components,
526 * as wide as its preferred width and as tall as the tallest of the other components.
527 * A null will be replaced with a blank JPanel.
528 */
529 public void addRow(
530 String key, Component west, Component north,
531 Component center, Component south, Component east )
532 {
533 addCompositeComponent( key,
534 makeCompositeComponent( key,
535 west, north, center, south, east ) );
536 }
537
538 /***
539 * Produces a container that contains the specified components,
540 * using GridLayout. Nulls are ignored.
541 * This implementation returns a JPanel.
542 */
543 protected Container makeCompositeComponent(
544 String key, Component[] components )
545 {
546 JPanel panel = createPanel();
547 if ( components.length != 0 )
548 {
549 panel.setLayout( new GridLayout( 1, components.length, hgap, vgap ) );
550
551 Component c;
552 for ( int i = 0; i < components.length; i++ )
553 {
554 c = components[i];
555 if ( c != null )
556 {
557 introspectComponent( c, key );
558 panel.add( c );
559 }
560 }
561 }
562 return panel;
563 }
564
565 /***
566 * Produces a container that contains the specified components,
567 * using BorderLayout. Nulls are ignored.
568 * This implementation returns a JPanel.
569 */
570 protected Container makeCompositeComponent(
571 String key, Component west, Component center, Component east )
572 {
573 JPanel panel = createPanel();
574 panel.setLayout( new BorderLayout( hgap, vgap ) );
575
576 if ( west != null )
577 {
578 introspectComponent( west, key );
579 panel.add( west, BorderLayout.WEST );
580 }
581
582 if ( center != null )
583 {
584 introspectComponent( center, key );
585 panel.add( center, BorderLayout.CENTER );
586 }
587
588 if ( east != null )
589 {
590 introspectComponent( east, key );
591 panel.add( east, BorderLayout.EAST );
592 }
593
594 return panel;
595 }
596
597 /***
598 * Produces a container that contains the specified components,
599 * using BorderLayout. Nulls are ignored.
600 * This implementation returns a JPanel.
601 */
602 protected Container makeCompositeComponent(
603 String key, Component west, Component north,
604 Component center, Component south, Component east )
605 {
606 JPanel panel = createPanel();
607 panel.setLayout( new BorderLayout( hgap, vgap ) );
608
609 if ( west != null )
610 {
611 introspectComponent( west, key );
612 panel.add( west, BorderLayout.WEST );
613 }
614
615 if ( north != null )
616 {
617 introspectComponent( north, key );
618 panel.add( north, BorderLayout.WEST );
619 }
620
621 if ( center != null )
622 {
623 introspectComponent( center, key );
624 panel.add( center, BorderLayout.CENTER );
625 }
626
627 if ( south != null )
628 {
629 introspectComponent( south, key );
630 panel.add( south, BorderLayout.CENTER );
631 }
632
633 if ( east != null )
634 {
635 introspectComponent( east, key );
636 panel.add( east, BorderLayout.EAST );
637 }
638
639 return panel;
640 }
641
642 /***
643 * Override to return a specific component to be used
644 * as a label. This implementation calls createLabel().
645 */
646 protected Component createLabelForKey( String aKey )
647 {
648 return createLabel();
649 }
650
651 /***
652 * Provided for backwards compatibility, and called by
653 * the default implementation of createLabelForKey.
654 * This implementation returns a JLabel.
655 */
656 protected JLabel createLabel()
657 {
658 return new JLabel();
659 }
660
661 /***
662 * Appends a label containing a key and the specified component
663 * to the bottom of the panel. Any registered action listeners
664 * will receive action events from the component - the key corresponding
665 * to the component will be used as the action command.
666 * @param key A string that will be displayed in a label, preferrably unique.
667 * @param component A component that will be placed next to the label.
668 * If null, a stock JTextField will be used.
669 */
670 protected void addCompositeComponent( String key, Component component )
671 {
672 if ( key == null )
673 {
674 key = "";
675 }
676 Component label = createLabelForKey( key );
677 Component field = component;
678 if ( field == null )
679 {
680 field = new JTextField( 15 );
681 }
682 field.setName( key );
683 label.setName( key );
684 if ( label instanceof JLabel )
685 {
686 ((JLabel)label).setHorizontalAlignment( labelAlign );
687 ((JLabel)label).setLabelFor( field );
688 }
689 if ( "".equals( key ) )
690 {
691 setText( label, "" );
692 }
693 else
694 {
695 setText( label, prefix + key + postfix );
696 }
697 field.setEnabled( this.isEditable );
698
699 GridBagConstraints gbc = new GridBagConstraints();
700
701 if ( listContainer.getComponentCount() == 0 )
702 {
703 createSpacers();
704 }
705
706 gbc.gridx = ( fields.size() % this.columns ) * 2;
707 gbc.gridy = ( fields.size() / this.columns ) + 1;
708 gbc.weightx = 0.0;
709 gbc.weighty = 0.0;
710 gbc.anchor = this.labelAnchor;
711 gbc.fill = GridBagConstraints.HORIZONTAL;
712 listContainer.add( label, gbc );
713
714 gbc.fill = GridBagConstraints.BOTH;
715 gbc.gridx = gbc.gridx + 1;
716
717 gbc.weightx = 1.0;
718 gbc.weighty = 0.0;
719
720 listContainer.add( field, gbc );
721
722 if ( key.equals( HIDDEN ) )
723 {
724 setText( label, " " );
725 field.setVisible( false );
726 }
727
728 fields.add( field );
729 labels.add( label );
730
731
732 updateGaps();
733 this.revalidate();
734 this.repaint();
735 }
736
737 /***
738 * Introspects a component to set the action command and to add the
739 * InfoPanel to its list of ActionListeners.
740 * @param aComponent The Component to be introspected.
741 * @param aKey The action command to be set.
742 */
743 protected void introspectComponent( Component aComponent, String aKey )
744 {
745
746 try {
747 Method [] methods =
748 (Method []) _method_cache.get( aComponent.getClass() );
749 if (methods == null) {
750 Class componentClass = aComponent.getClass();
751 BeanInfo info =
752 Introspector.getBeanInfo( componentClass );
753
754 MethodDescriptor[] descriptors =
755 info.getMethodDescriptors();
756 Method setMethod = null;
757 Method addMethod = null;
758 for ( int i = 0;
759 ((setMethod == null || addMethod == null) &&
760 i < descriptors.length);
761 i++ )
762 {
763 Method m = descriptors[i].getMethod();
764 String name = m.getName ();
765 if ( setMethod == null &&
766 name.equals( "setActionCommand" ) )
767 {
768 setMethod = m;
769 }
770 else if ( addMethod == null &&
771 name.equals( "addActionListener" ) )
772 {
773 addMethod = m;
774 }
775 }
776
777 methods = new Method [] {setMethod, addMethod};
778 _method_cache.put (componentClass, methods);
779 }
780 if (methods [0] != null) {
781 methods [0].invoke( aComponent, new Object[] { aKey } );
782 }
783 if (methods [1] != null) {
784 methods [1].invoke( aComponent, new Object[] { this } );
785 listenedToComponents.add( aComponent );
786 }
787 }
788 catch ( Exception exc )
789 {
790 System.out.println( "InfoPanel.introspectComponent: " + exc );
791 }
792 }
793
794 /***
795 * Called to populate a label component with the specified text.
796 * This implementation attempts to call setText(String) on the component.
797 * Override to customize.
798 */
799 protected void setText( Component c, String text )
800 {
801 try
802 {
803 Method m = c.getClass().getMethod( "setText", new Class[] { String.class } );
804 if ( m != null )
805 {
806 m.invoke( c, new Object[] { text } );
807 }
808 }
809 catch ( Exception exc )
810 {
811
812 }
813 }
814
815 /***
816 * Creates spacer components on the reserved first grid row
817 * for each column of labels and fields.
818 * This allows us to set the margin for those label columns,
819 * and set the preferred width of the field columns.
820 * A list containing the field spacers should be assigned to
821 * the fieldSpacers instance variable.
822 */
823 private void createSpacers()
824 {
825 if ( listContainer.getLayout() instanceof GridBagLayout )
826 {
827
828 GridBagLayout gridBag = (GridBagLayout) listContainer.getLayout();
829 GridBagConstraints constraints = new GridBagConstraints();
830 constraints.gridy = 0;
831 constraints.fill = GridBagConstraints.HORIZONTAL;
832
833 fieldSpacers = new LinkedList();
834 Component fieldSpacer;
835 for ( int i = 0; i < this.columns; i++ )
836 {
837 constraints.gridx = i * 2;
838 listContainer.add( Box.createHorizontalStrut( this.margin ), constraints );
839
840 constraints.gridx = i * 2 + 1;
841 fieldSpacer = Box.createHorizontalStrut( 0 );
842 fieldSpacers.add( fieldSpacer );
843 listContainer.add( fieldSpacer, constraints );
844 }
845 }
846 }
847
848 /***
849 * Updates the insets for all components.
850 */
851 protected void updateGaps()
852 {
853 if ( listContainer.getLayout() instanceof GridBagLayout )
854 {
855 GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
856 Component c = null;
857 GridBagConstraints gbc = null;
858 double totalWeightY = 0.0;
859 int count = listContainer.getComponentCount();
860 int i;
861 for ( i = 0; i < count; i++ )
862 {
863 c = listContainer.getComponent( i );
864 gbc = layout.getConstraints( c );
865 totalWeightY += gbc.weighty;
866 if ( (gbc.gridx + 1) % ( this.columns * 2 ) == 0 )
867 {
868 gbc.insets = new Insets( 0, 0, this.vgap, 0 );
869 }
870 else
871 {
872 if ( gbc.gridx % 2 == 0 )
873 {
874 gbc.insets = new Insets( 0, 0, this.vgap, 11 );
875 }
876 else
877 {
878 if ( gbc.gridy != 0 )
879 {
880 if ( c instanceof JPanel ) ((JPanel)c).setPreferredSize( null );
881 gbc.insets = new Insets( 0, 0, this.vgap, this.hgap );
882 }
883 }
884 }
885 layout.setConstraints( c, gbc );
886 }
887
888
889
890 this.remove( listContainer );
891 if ( totalWeightY == 0.0 )
892 {
893 this.add( listContainer, BorderLayout.NORTH );
894 }
895 else
896 {
897 this.add( listContainer, BorderLayout.CENTER );
898 }
899 }
900 }
901
902 /***
903 * Updates the label alignment.
904 */
905 protected void updateLabels()
906 {
907 if ( listContainer.getLayout() instanceof GridBagLayout )
908 {
909 GridBagLayout layout = (GridBagLayout) listContainer.getLayout();
910 Component c = null;
911 GridBagConstraints gbc = null;
912 Iterator it = labels.iterator();
913 while ( it.hasNext() )
914 {
915 c = (Component) it.next();
916 if ( c instanceof JLabel )
917 {
918 ((JLabel)c).setHorizontalAlignment( labelAlign );
919 }
920 gbc = layout.getConstraints( c );
921 gbc.anchor = this.labelAnchor;
922 layout.setConstraints( c, gbc );
923 }
924 }
925 }
926
927 /***
928 * Convenience method that uses a stock JTextField.
929 * @param key A string that will be displayed in a label, preferrably unique.
930 * @param value A string that will be displayed in a textfield.
931 */
932 public void addPair( String key, String value )
933 {
934 addPair( key, value, null );
935 }
936
937 /***
938 * Convenience method that uses the specified JTextField or subclass
939 * and sets it to the specified value.
940 * @param key A string that will be displayed in a label, preferrably unique.
941 * @param value A string that will be displayed in a textfield.
942 * @param textField A JTextField or subclass that will be used to display the value.
943 * If null, a stock JTextField will be used.
944 */
945 public void addPair( String key, String value, JTextField textField )
946 {
947 if ( value == null )
948 {
949 value = "";
950 }
951 JTextField field = textField;
952 if ( field == null )
953 {
954 field = new JTextField( 15 );
955 }
956 else
957 {
958 field = textField;
959 }
960 field.setText( value );
961
962 addPair( key, (Component) field );
963 }
964
965 /***
966 * Removes all components from the list. Buttons, if any,
967 * will remain unchanged - use setButtons( null ) to remove
968 * them. NOTE: does not call super.removeAll().
969 */
970 public void removeAll()
971 {
972 Object component;
973 Method method;
974 Class[] paramClasses = new Class[] { ActionListener.class };
975 Object[] paramObjects = new Object[] { this };
976
977 Iterator iterator = listenedToComponents.iterator();
978 while ( iterator.hasNext() )
979 {
980 component = iterator.next();
981 try
982 {
983 method = component.getClass().getMethod( "removeActionListener", paramClasses );
984 if ( method != null )
985 {
986 method.invoke( component, paramObjects );
987 }
988 }
989 catch ( Exception exception )
990 {
991
992 }
993 }
994
995 listenedToComponents.clear();
996
997 listContainer.removeAll();
998 fields.clear();
999 labels.clear();
1000 this.revalidate();
1001 this.repaint();
1002
1003
1004
1005 }
1006
1007 /***
1008 * Adds one or buttons to the bottom of the panel with the specified labels
1009 * from left to right. Any action listeners will receive action events
1010 * from clicks on these buttons - the supplied label will be used as the action command.
1011 * @param buttons A string array containing the strings to be used for the button labels
1012 * and action commands. A null value will remove the button panel.
1013 * @see ButtonPanel
1014 */
1015 public void setButtons( String[] buttons )
1016 {
1017 if ( buttonPanel == null )
1018 {
1019 buttonPanel = new ButtonPanel();
1020 buttonPanel.setInsets( new Insets( 6, 0, 0, 0 ) );
1021
1022
1023 buttonPanel.addActionListener( this );
1024 this.add( buttonPanel, BorderLayout.SOUTH );
1025 }
1026 if ( buttons == null )
1027 {
1028 this.remove( buttonPanel );
1029 buttonPanel = null;
1030 }
1031 else
1032 {
1033 buttonPanel.setLabels( buttons );
1034 }
1035
1036 this.revalidate();
1037 this.repaint();
1038 }
1039 protected Collection listenedToComponents = new LinkedList();
1040
1041 /***
1042 * Retrieves the names of the buttons that are displayed, if any.
1043 * @return A string array containing the strings used for the button labels
1044 * and action commands, or null if no buttons have been created.
1045 * @see ButtonPanel
1046 */
1047 public String[] getButtons()
1048 {
1049 if ( buttonPanel == null )
1050 {
1051 return null;
1052 }
1053
1054 return buttonPanel.getLabels();
1055 }
1056
1057 /***
1058 * Retrieves the actual button panel, if any.
1059 * @return A button panel, or null if none has been created.
1060 * @see ButtonPanel
1061 */
1062 public ButtonPanel getButtonPanel()
1063 {
1064 return buttonPanel;
1065 }
1066
1067
1068 /***
1069 * Sets whether the values displayed in the panel should be editable. Defaults to true.
1070 * @param isEditable Whether the values should be editable.
1071 */
1072 public void setEditable( boolean isEditable )
1073 {
1074 this.isEditable = isEditable;
1075 Iterator enumeration = fields.iterator();
1076 while ( enumeration.hasNext() )
1077 {
1078 ( (Component) enumeration.next() ).setEnabled( isEditable );
1079 }
1080 }
1081
1082 /***
1083 * Gets whether the values displayed in the panel are editable.
1084 * @return Whether the values should be editable.
1085 */
1086 public boolean isEditable()
1087 {
1088 return this.isEditable;
1089 }
1090
1091 /***
1092 * Sets the field associated with the key to the specified value.
1093 * Note: If the component does not respond to setText() or setString()
1094 * or setValue() the value will not be set. JTextFields and the like will work.
1095 * @param key A string representing the key associated with the field. Nulls are converted to an empty string.
1096 * @param value A object to be displayed in the specified field. Nulls are converted to an empty string.
1097 */
1098 public void setValueForKey( String key, Object value )
1099 {
1100 setValueForKey( key, value, 0 );
1101 }
1102
1103 /***
1104 * Sets the field associated with the key to the specified value.
1105 * Note: If the component does not respond to setText() or setString()
1106 * or setValue() the value will not be set. JTextFields and the like will work.
1107 * @param key A string representing the key associated with the field. Nulls are converted to an empty string.
1108 * @param value A object to be displayed in the specified field. Nulls are converted to an empty string.
1109 */
1110 public void setValueForKey( String key, Object value, int index )
1111 {
1112 if ( key == null )
1113 {
1114 key = "";
1115 }
1116
1117 Container field = null;
1118 for ( int i = 0; i < fields.size(); i++ )
1119 {
1120 field = (Container) fields.get(i);
1121 if ( key.equals( field.getName() ) )
1122 {
1123 setValueForIndex( index, i, value );
1124 return;
1125 }
1126 }
1127
1128 }
1129
1130 /***
1131 * Sets the first field at the specified row index to the specified value.
1132 * Note: If the component does not respond to setText() or setString()
1133 * or setValue() the value will not be set. JTextFields and the like will work.
1134 * @param row The row index of the component.
1135 * @param value A object to be displayed in the specified field.
1136 * Nulls are converted to an empty string.
1137 */
1138 public void setValueForIndex( int row, Object value )
1139 {
1140 setValueForIndex( row, 0, value );
1141 }
1142
1143 /***
1144 * Sets the field at the specified row index and column index to the specified value.
1145 * Note: If the component does not respond to setText() or setString()
1146 * or setValue() the value will not be set. JTextFields and the like will work.
1147 * @param row The row index of the component.
1148 * @param index The column index of the component.
1149 * @param value A object to be displayed in the specified field.
1150 * Nulls are converted to an empty string.
1151 */
1152 public void setValueForIndex( int row, int col, Object value )
1153 {
1154 Container field = (Container) fields.get( row );
1155 Component c = field.getComponent( col );
1156 setValueForComponent( c, value );
1157 }
1158
1159
1160
1161 /***
1162 * Sets the value in the field at the specified index.
1163 * Note: If the component does not respond to setText() or setString()
1164 * or setValue() this method will return null. JTextFields and the like will work.
1165 * @param A valid index.
1166 * @param value A object to be displayed in the specified field.
1167 */
1168 protected void setValueForComponent( Component aComponent, Object value )
1169 {
1170
1171 try {
1172 BeanInfo info = Introspector.getBeanInfo( aComponent.getClass() );
1173 MethodDescriptor[] methods = info.getMethodDescriptors();
1174 for ( int i = 0; i < methods.length; i++ )
1175 {
1176 Method m = methods[i].getMethod();
1177 Class[] paramTypes = m.getParameterTypes();
1178 if ( paramTypes.length == 1 )
1179 {
1180 if ( m.getName().equals( "setText" ) )
1181 {
1182 if ( paramTypes[0].getName().equals( String.class.getName() ) )
1183 {
1184 m.invoke( aComponent, new Object[] { value } );
1185 }
1186 }
1187 if ( m.getName().equals( "setString" ) )
1188 {
1189 if ( paramTypes[0].getName().equals( String.class.getName() ) )
1190 {
1191 m.invoke( aComponent, new Object[] { value } );
1192 }
1193 }
1194 if ( m.getName().equals( "setValue" ) )
1195 {
1196 if ( paramTypes[0].getName().equals( Object.class.getName() ) )
1197 {
1198 m.invoke( aComponent, new Object[] { value } );
1199 }
1200 }
1201 }
1202 }
1203 }
1204 catch ( Exception exc )
1205 {
1206
1207 System.out.println( "InfoPanel.setValueForComponent: " + exc );
1208 }
1209 }
1210
1211 /***
1212 * Gets the value in the field at the specified index.
1213 * Note: If the component does not respond to getText() or getString()
1214 * or getSelectedItem() this method will return null. JTextFields and the like will work.
1215 * @param A valid index.
1216 * @return An object representing the value in the field at the specified index,
1217 * or null if the component does not have a text property or if the index is out of bounds.
1218 */
1219 public Object getValueForIndex( int anIndex )
1220 {
1221 return getValueForIndex( anIndex, 0 );
1222 }
1223
1224 /***
1225 * Gets the value in the field at the specified row and column.
1226 * Note: If the component does not respond to getText() or getString()
1227 * or getSelectedItem() this method will return null. JTextFields and the like will work.
1228 * @param A valid index.
1229 * @return An object representing the value in the field at the specified index,
1230 * or null if the component does not have a text property or if the index is out of bounds.
1231 */
1232 public Object getValueForIndex( int row, int col )
1233 {
1234 if ( ( row >= fields.size() ) || ( row < 0 ) )
1235 {
1236 return null;
1237 }
1238
1239 Container field = (Container) fields.get( row );
1240 Component c = field.getComponent( col );
1241 return getValueForComponent( c );
1242 }
1243
1244 /***
1245 * Gets the value in the field associated with the key.
1246 * Note: If the component does not respond to getText() or getString()
1247 * or getSelectedItem() this method will return null. JTextFields and the like will work.
1248 * @param key An string representing the key associated with the field. Nulls are converted to an empty string.
1249 * @return An object representing the value in the field associated with the key,
1250 * or null if the key does not exist or if the component does not have a text property.
1251 */
1252 public Object getValueForKey( String key )
1253 {
1254 return getValueForKey( key, 0 );
1255 }
1256
1257 /***
1258 * Gets the value in the field associated with the key.
1259 * Note: If the component does not respond to getText() or getString()
1260 * or getSelectedItem() this method will return null. JTextFields and the like will work.
1261 * @param key An string representing the key associated with the field. Nulls are converted to an empty string.
1262 * @return An object representing the value in the field associated with the key,
1263 * or null if the key does not exist or if the component does not have a text property.
1264 */
1265 public Object getValueForKey( String key, int index )
1266 {
1267 if ( key == null )
1268 {
1269 key = "";
1270 }
1271
1272 Container field = null;
1273 Iterator enumeration = fields.iterator();
1274 while ( enumeration.hasNext() )
1275 {
1276 field = (Container) enumeration.next();
1277 if ( key.equals( field.getName() ) )
1278 {
1279 Component c = field.getComponent( index );
1280 if ( c != null )
1281 {
1282 return getValueForComponent( c );
1283 }
1284 }
1285 }
1286
1287 return null;
1288 }
1289
1290 /***
1291 * Gets the value in the specified component.
1292 * Note: If the component does not respond to getText() or getString()
1293 * or getSelectedItem() this method will return null. JTextFields and the like will work.
1294 * @param aComponent The specified component.
1295 * @return An object representing the value in the component.
1296 * or null if the component does not have a text property.
1297 */
1298 protected Object getValueForComponent( Component aComponent )
1299 {
1300
1301 try
1302 {
1303 BeanInfo info = Introspector.getBeanInfo( aComponent.getClass() );
1304 MethodDescriptor[] methods = info.getMethodDescriptors();
1305 for ( int i = 0; i < methods.length; i++ )
1306 {
1307 Method m = methods[i].getMethod();
1308 Class[] paramTypes = m.getParameterTypes();
1309 if ( m.getName().equals( "getText" ) )
1310 {
1311 if ( paramTypes.length == 0 )
1312 {
1313 return m.invoke( aComponent, new Object[] {} );
1314 }
1315 }
1316 if ( m.getName().equals( "getString" ) )
1317 {
1318 if ( paramTypes.length == 0 )
1319 {
1320 return m.invoke( aComponent, new Object[] {} );
1321 }
1322 }
1323 if ( m.getName().equals( "getSelectedItem" ) )
1324 {
1325 if ( paramTypes.length == 0 )
1326 {
1327 return m.invoke( aComponent, new Object[] {} );
1328 }
1329 }
1330
1331 }
1332 }
1333 catch ( Exception exc )
1334 {
1335 System.out.println( "InfoPanel.getValueFromComponent: " + exc );
1336 }
1337
1338
1339 return null;
1340 }
1341
1342 /***
1343 * Gets the component associated with the key as a JTextField, for backwards compatibility.
1344 * @param key A string representing the key associated with the component. Nulls are converted to an empty string.
1345 * @return A JTextField that contains the value associated with the key,
1346 * or null if the key does not exist or if the component is not a JTextField.
1347 */
1348 public JTextField getFieldForKey( String key )
1349 {
1350 Component c = getComponentForKey( key );
1351 if ( c instanceof JTextField )
1352 {
1353 return (JTextField) c;
1354 }
1355 return null;
1356 }
1357
1358 /***
1359 * Gets the component associated with the key. If more than one component is associated
1360 * with the key, returns the first such component.
1361 * @param key A string representing the key associated with the component.
1362 * Nulls are converted to an empty string.
1363 * @return A component that contains the value associated with the key,
1364 * or null if the key does not exist.
1365 */
1366 public Component getComponentForKey( String key )
1367 {
1368 return getComponentForKey( key, 0 );
1369 }
1370
1371 /***
1372 * Gets the component associated with the key and index.
1373 * @param key A string representing the key associated with the component.
1374 * Nulls are converted to an empty string.
1375 * @return A component that contains the value associated with the key,
1376 * or null if the key does not exist.
1377 */
1378 public Component getComponentForKey( String key, int index )
1379 {
1380 Container c = getCompositeComponentForKey( key );
1381 if ( c == null ) return null;
1382 return c.getComponent( index );
1383 }
1384
1385 /***
1386 * Gets the component at the specified row. If more than one component exists
1387 * on that row, returns the first such component.
1388 * @return A component or null if the row does not exist.
1389 */
1390 public Object getComponentForIndex( int row )
1391 {
1392 return getComponentForIndex( row, 0 );
1393 }
1394
1395 /***
1396 * Gets the component at the specified row and column.
1397 * @return A component or null if the index is out of bounds.
1398 */
1399 public Object getComponentForIndex( int row, int col )
1400 {
1401 if ( ( row > fields.size() ) || ( row < 0 ) )
1402 {
1403 return null;
1404 }
1405
1406 Container field = (Container) fields.get( row );
1407 return field.getComponent( col );
1408 }
1409
1410 /***
1411 * Gets the container associated with the key.
1412 * @param key A string representing the key associated with the component.
1413 * Nulls are converted to an empty string.
1414 * @return A component that contains the value associated with the key,
1415 * or null if the key does not exist.
1416 */
1417 protected Container getCompositeComponentForKey( String key )
1418 {
1419 if ( key == null )
1420 {
1421 key = "";
1422 }
1423
1424 JPanel field = null;
1425 Iterator enumeration = fields.iterator();
1426 while ( enumeration.hasNext() )
1427 {
1428 field = (JPanel) enumeration.next();
1429 if ( key.equals( field.getName() ) )
1430 {
1431 return field;
1432 }
1433 }
1434
1435
1436 return null;
1437 }
1438
1439 /***
1440 * Provided for backwards compatibility: calls getLabelComponentForKey.
1441 * @param key A string representing the key associated with the compoent.
1442 * Nulls are converted to an empty string.
1443 * @return Component label object associated with the key, or null if the key does not exist
1444 * or if the label component is not an instance of JLabel.
1445 */
1446 public JLabel getLabelForKey( String key )
1447 {
1448 Component result = getLabelComponentForKey( key );
1449 if ( result instanceof JLabel ) return (JLabel) result;
1450 return null;
1451 }
1452
1453 /***
1454 * Get the label component associated with the key.
1455 * @param key A string representing the key associated with the compoent.
1456 * Nulls are converted to an empty string.
1457 * @return Component label object associated with the key, or null if the key does not exist.
1458 */
1459 public Component getLabelComponentForKey( String key )
1460 {
1461 if ( key == null )
1462 {
1463 key = "";
1464 }
1465
1466 Component label = null;
1467 Iterator enumeration = labels.iterator();
1468 while ( enumeration.hasNext() )
1469 {
1470 label = (Component) enumeration.next();
1471 if ( key.equals( label.getName() ) )
1472 {
1473 return label;
1474 }
1475 }
1476
1477
1478 return null;
1479 }
1480
1481 /***
1482 * Replaces the first component associated with the key. Any value in the existing
1483 * component will be copied to the new component.
1484 * @param key A string representing the key to be associated with the component.
1485 * Nulls are converted to an empty string.
1486 * @param c A component to be placed next to the label corresponding to the key.
1487 * Nulls are converted to a JTextField.
1488 */
1489 public void setComponentForKey( String key, Component c )
1490 {
1491 setComponentForKey( key, c, 0 );
1492 }
1493
1494 /***
1495 * Replaces the component associated with the key. Any value in the existing
1496 * component will be copied to the new component.
1497 * @param key A string representing the key to be associated with the component.
1498 * Nulls are converted to an empty string.
1499 * @param c A component to be placed next to the label corresponding to the key.
1500 * Nulls are converted to a JTextField.
1501 */
1502 public void setComponentForKey( String key, Component c, int index )
1503 {
1504 if ( c == null )
1505 {
1506 c = new JTextField( 15 );
1507 }
1508 if ( key == null )
1509 {
1510 key = "";
1511 }
1512
1513 Container container = this.getCompositeComponentForKey( key );
1514 Component field = container.getComponent( index );
1515 Object value = this.getValueForKey( key, index );
1516 if ( field != null )
1517 {
1518 container.remove( index );
1519 container.add( c, index );
1520 c.setEnabled( this.isEditable );
1521 introspectComponent( c, key );
1522 setValueForComponent( c, value );
1523 }
1524 }
1525
1526 /***
1527 * Replaces the first component in the specified row. Any value in the existing
1528 * component will be copied to the new component.
1529 * @param row A valid index.
1530 * @param c A component to be placed next to the label corresponding to the key.
1531 */
1532 public void setComponentForIndex( int row, Component c )
1533 {
1534 setComponentForIndex( row, 0, c );
1535 }
1536
1537 /***
1538 * Replaces the component associated with the key. Any value in the existing
1539 * component will be copied to the new component.
1540 * @param row A valid index.
1541 * @param c A component to be placed next to the label corresponding to the key.
1542 */
1543 public void setComponentForIndex( int row, int col, Component c )
1544 {
1545 setComponentForKey( getLabels()[row], c, col );
1546 }
1547
1548 /***
1549 * Sets the string that appears before each label's text on the panel.
1550 * @param aString A String to be used as the label prefix.
1551 */
1552 public void setLabelPrefix( String aString )
1553 {
1554 prefix = aString;
1555 setLabels( getLabels() );
1556 }
1557
1558 /***
1559 * Gets the string that appears before each label's text on the panel.
1560 * Defaults to "", an empty string.
1561 * @return A String that is currently used as the label prefix.
1562 */
1563 public String getLabelPrefix()
1564 {
1565 return prefix;
1566 }
1567
1568 /***
1569 * Sets the string that appears after each label's text on the panel.
1570 * Defaults to ": ", a colon followed by a space.
1571 * @param aString A String to be used as the label postfix.
1572 */
1573 public void setLabelPostfix( String aString )
1574 {
1575 postfix = aString;
1576 setLabels( getLabels() );
1577 }
1578
1579 /***
1580 * Gets the string that appears after each label's text on the panel.
1581 * @return A String that is currently used as the label postfix.
1582 */
1583 public String getLabelPostfix()
1584 {
1585 return postfix;
1586 }
1587
1588 /***
1589 * Adds an action listener to the list that will be
1590 * notified by events occurring in the panel.
1591 * @param l An action listener to be notified.
1592 */
1593 public void addActionListener(ActionListener l)
1594 {
1595 actionListener = AWTEventMulticaster.add(actionListener, l);
1596 }
1597 /***
1598 * Removes an action listener from the list that will be
1599 * notified by events occurring in the panel.
1600 * @param l An action listener to be removed.
1601 */
1602 public void removeActionListener(ActionListener l)
1603 {
1604 actionListener = AWTEventMulticaster.remove(actionListener, l);
1605 }
1606 /***
1607 * Notifies all registered action listeners of a pending Action Event.
1608 * @param e An action event to be broadcast.
1609 */
1610 protected void broadcastEvent(ActionEvent e)
1611 {
1612 if (actionListener != null)
1613 {
1614 actionListener.actionPerformed(e);
1615 }
1616 }
1617
1618
1619
1620 /***
1621 * Called by buttons on panel and by other components that
1622 * might be set to broadcast events to this listener.
1623 * Simply forwards the action event unchanged.
1624 * @param e An action event to be received.
1625 */
1626 public void actionPerformed(ActionEvent e)
1627 {
1628
1629
1630 broadcastEvent(e);
1631
1632 }
1633
1634 /***
1635 * GridBagLayout allocates weightx only after considering
1636 * the preferred width of the components in a column.
1637 * We'd prefer that preferred width wasn't considered,
1638 * so that the layout worked more like a html-table.
1639 * GridBagLayout is poorly factored for subclassing,
1640 * so this code is going to get a little bit ugly.
1641 * Really, what good is a protected method that returns
1642 * a private class? Would have liked to just override
1643 * getLayoutInfo and be done with it.
1644 */
1645 private class BetterGridBagLayout extends GridBagLayout
1646 {
1647 public Dimension preferredLayoutSize(Container parent)
1648 {
1649 preprocess();
1650 return super.preferredLayoutSize( parent );
1651 }
1652
1653 public Dimension minimumLayoutSize(Container parent)
1654 {
1655 preprocess();
1656 return super.minimumLayoutSize( parent );
1657 }
1658
1659
1660 public void layoutContainer(Container parent)
1661 {
1662 preprocess();
1663 super.layoutContainer( parent );
1664 }
1665
1666 protected void preprocess()
1667 {
1668 if ( fieldSpacers == null ) return;
1669 Iterator i;
1670
1671
1672 Component c;
1673 int maxWidth = 0;
1674 i = fields.iterator();
1675 while ( i.hasNext() )
1676 {
1677 c = (Component) i.next();
1678 maxWidth = Math.max( maxWidth,
1679 Math.max( c.getPreferredSize().width, c.getMinimumSize().width ) );
1680 }
1681
1682
1683 Dimension min = new Dimension( 0, 0 );
1684 Dimension pref = new Dimension( maxWidth, 0 );
1685 i = fieldSpacers.iterator();
1686 while ( i.hasNext() )
1687 {
1688 ((Box.Filler)i.next()).changeShape( min, pref, pref );
1689 }
1690 }
1691 }
1692 }
1693