1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package com.gargoylesoftware.base.gui;
39
40 import com.gargoylesoftware.base.trace.Trace;
41 import com.gargoylesoftware.base.trace.TraceChannel;
42 import com.gargoylesoftware.base.util.DetailedIllegalArgumentException;
43 import com.gargoylesoftware.base.util.DetailedNullPointerException;
44 import java.awt.Component;
45 import java.awt.Container;
46 import java.awt.Dimension;
47 import java.awt.Graphics;
48 import java.awt.Insets;
49 import java.awt.LayoutManager2;
50 import java.io.Serializable;
51 import java.util.ArrayList;
52 import java.util.HashSet;
53 import java.util.Iterator;
54 import java.util.List;
55 import java.util.Set;
56 import javax.swing.SwingConstants;
57
58 /***
59 * The TableLayout lays out items based on a table of rows and columns.<p>
60 *
61 * If you are doing simple layout, you can specify constraints as strings of the format
62 * "row,column". If you want the component to stretch over multiple rows/columns
63 * then you can specify the constraint as "row+rowspan,column+columnspan" as shown in
64 * the sample below.
65 * <pre>
66 * final TableLayout layout = new TableLayout();
67 * final JPanel panel = new JPanel(layout);
68 *
69 * panel.add( new JLabel("squirrel"), "1,1" );
70 * panel.add( new JLabel("raccoon"), "1,2" );
71 * panel.add( new JLabel("bluejay"), "2,1" );
72 * panel.add( new JLabel("goldfish"), "2,2" );
73 * panel.add( new JLabel("marshhawk"), "3,1+3" );
74 * </pre>
75 *
76 * If you want more flexibility over the layout then this, use a {@link TableLayoutConstraints}
77 * object instead of a string. Here is a more complicated sample that uses
78 * {@link TableLayoutConstraints} to customize the layout a bit more. Note the use of
79 * {@link TableLayoutDebuggingPanel} - this will draw lines on layout boundaries to help
80 * debug layout problems.
81 * <pre>
82 * final TableLayout layout = new TableLayout();
83 * final JPanel panel = new TableLayoutDebuggingPanel(layout);
84 *
85 * TableLayoutConstraints constraints;
86 *
87 * layout.setRowExpandable(1, true);
88 *
89 * constraints = new TableLayoutConstraints(1,1);
90 * constraints.setVerticalStretch(true);
91 * panel.add( new JButton("squirrel"), constraints );
92 *
93 * constraints = new TableLayoutConstraints(1,2);
94 * constraints.setVerticalAlignment(TableLayout.TOP);
95 * panel.add( new JButton("raccoon"), constraints );
96 *
97 * panel.add( new JButton("bluejay"), "2,1" );
98 * panel.add( new JButton("goldfish"), "2,2" );
99 * panel.add( new JButton("marshhawk"), "3,1+3" );
100 * </pre>
101 *
102 * <b>Debugging tip: </b>Most layout problems become obvious if you use a
103 * {@link TableLayoutDebuggingPanel} to see where the layout boundaries are. In those
104 * rare cases where this doesn't give you enough information, try calling
105 * {@link #setTraceChannel(TraceChannel)} with a non-null TraceChannel such as Trace.out
106 * or Trace.err. This will dump quite a bit of diagnostic information.
107 * <pre>
108 * layout.setTraceChannel(Trace.out)
109 * </pre>
110 *
111 * @version $Revision: 1.6 $
112 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
113 */
114 public class TableLayout
115 implements
116 LayoutManager2,
117 SwingConstants,
118 Serializable {
119
120
121 private static final long serialVersionUID = 396191633848670929L;
122
123 private final Set rowHeaderPermanentInfos_ = new HashSet();
124 private final Set columnHeaderPermanentInfos_ = new HashSet();
125
126 private Container parent_;
127 private final List constraints_ = new ArrayList();
128
129
130 private TraceChannel traceChannel_ = null;
131
132
133 private Header tempColumnHeaders_[] = null;
134 private Header tempRowHeaders_[] = null;
135
136
137
138 private int columnCount_ = 0;
139 private int rowCount_ = 0;
140
141
142 private boolean tempSizesAreValid_ = false;
143
144
145
146
147 private int verticalAlignment_ = CENTER;
148 private int horizontalAlignment_ = CENTER;
149
150 private Dimension minimumSize_ = null;
151 private Dimension maximumSize_ = null;
152 private Dimension preferredSize_ = null;
153 private Dimension actualSize_ = null;
154
155 private boolean ignoreInvisibleComponents_ = true;
156
157
158 /***
159 * Create a new TableLayout.
160 */
161 public TableLayout() {
162 }
163
164
165 /***
166 * Convenience method to create a string from a Dimension object.
167 *
168 * @param dimension Description of Parameter
169 * @return Description of the Returned Value
170 */
171 private static String toString( final Dimension dimension ) {
172 return "(" + dimension.width + "," + dimension.height + ")";
173 }
174
175
176 /***
177 * Set the vertical alignment. Legal values are:
178 * <ul>
179 * <li> <tt>TableLayout.TOP</tt>
180 * <li> <tt>TableLayout.CENTER</tt>
181 * <li> <tt>TableLayout.BOTTOM</tt>
182 * </ul>
183 *
184 *
185 * @param alignment The new vertical alignment.
186 */
187 public void setVerticalAlignment( final int alignment ) {
188 Trace.println( traceChannel_,
189 "setVerticalAlignment(" + alignment + ")" );
190
191 switch ( alignment ) {
192 case TableLayout.TOP:
193 case TableLayout.BOTTOM:
194 case TableLayout.CENTER:
195 verticalAlignment_ = alignment;
196 break;
197 default:
198 throw new DetailedIllegalArgumentException( "alignment", new Integer(alignment) );
199 }
200 }
201
202
203 /***
204 * Set the vertical alignment. Legal values are:
205 * <ul>
206 * <li> <tt>TableLayout.LEFT</tt>
207 * <li> <tt>TableLayout.CENTER</tt>
208 * <li> <tt>TableLayout.RIGHT</tt>
209 * </ul>
210 *
211 *
212 * @param alignment The new horizontal alignment.
213 */
214 public void setHorizontalAlignment( final int alignment ) {
215 Trace.println( traceChannel_, "setHorizontalAlignment()" );
216 switch ( alignment ) {
217 case TableLayout.LEFT:
218 case TableLayout.RIGHT:
219 case TableLayout.CENTER:
220 horizontalAlignment_ = alignment;
221 break;
222 default:
223 throw new DetailedIllegalArgumentException( "alignment", new Integer(alignment) );
224 }
225 }
226
227
228 /***
229 * Set the minimum row height for a specific row.
230 *
231 * @param index The row that we are setting the height for.
232 * @param size The new minimum height.
233 */
234 public void setMinimumRowHeight( final int index, final int size ) {
235 final HeaderPermanentInfo info
236 = getPermanentInfo( rowHeaderPermanentInfos_, index, true );
237 info.setMin( size );
238 }
239
240
241 /***
242 * Set the minimum column width for a specific column.
243 *
244 * @param index The column that we are setting the width for.
245 * @param size The new width.
246 */
247 public void setMinimumColumnWidth( final int index, final int size ) {
248 final HeaderPermanentInfo info
249 = getPermanentInfo( columnHeaderPermanentInfos_, index, true );
250 info.setMin( size );
251 }
252
253
254 /***
255 * Set whether this row can be expanded beyond its preferred size.
256 *
257 * @param index The row index.
258 * @param isExpandable true if the row is to be expandable.
259 */
260 public void setRowExpandable( final int index,
261 final boolean isExpandable ) {
262 final HeaderPermanentInfo info
263 = getPermanentInfo( rowHeaderPermanentInfos_, index, true );
264 info.setExpandable( isExpandable );
265 }
266
267
268 /***
269 * Set whether this column can be expanded beyond its preferred size.
270 *
271 * @param index The column index.
272 * @param isExpandable true if the column is to be expandable.
273 */
274 public void setColumnExpandable( final int index,
275 final boolean isExpandable ) {
276 final HeaderPermanentInfo info
277 = getPermanentInfo( columnHeaderPermanentInfos_, index, true );
278 info.setExpandable( isExpandable );
279 }
280
281
282 /***
283 * Set the trace channel used for printing diagnostic information. If the
284 * channel is null then no tracing will be done.
285 *
286 * @param channel The new trace channel.
287 */
288 public void setTraceChannel( final TraceChannel channel ) {
289 traceChannel_ = channel;
290 }
291
292
293 /***
294 * Set whether or not we should ignore an components that are not visible.
295 *
296 * @param ignore True if we should ignore them.
297 */
298 public void setIgnoreInvisibleComponents( final boolean ignore ) {
299 ignoreInvisibleComponents_ = ignore;
300 }
301
302
303 /***
304 * I don't really understand what this method is supposed to return so I
305 * always return 0F. If you can explain this one to me then send me an
306 * email at mbowler@GargoyleSoftware.com and I'll fix this method up
307 * appropriately.
308 *
309 * @param target The container that this layout is managing.
310 * @return Zero.
311 */
312 public float getLayoutAlignmentX( final Container target ) {
313 return 0F;
314 }
315
316
317 /***
318 * I don't really understand what this method is supposed to return so I
319 * always return 0F. If you can explain this one to me then send me an
320 * email at mbowler@GargoyleSoftware.com and I'll fix this method up
321 * appropriately.
322 *
323 * @param target The container that this layout is managing.
324 * @return Zero.
325 */
326 public float getLayoutAlignmentY( final Container target ) {
327 return 0F;
328 }
329
330
331 /***
332 * Return the vertical alignment.
333 *
334 * @return The vertical alignment.
335 * @see #setVerticalAlignment(int)
336 */
337 public int getVerticalAlignment() {
338 return verticalAlignment_;
339 }
340
341
342 /***
343 * Return the horizontal alignment.
344 *
345 * @return The horizontal alignment.
346 */
347 public int getHorizontalAlignment() {
348 return horizontalAlignment_;
349 }
350
351
352 /***
353 * Return true if this row can be expanded beyond its preferred size. The
354 * default is false.
355 *
356 * @param index The row index
357 * @return true if the specified row is expandable.
358 */
359 public boolean isRowExpandable( final int index ) {
360 final HeaderPermanentInfo info
361 = getPermanentInfo( rowHeaderPermanentInfos_, index, false );
362 final boolean isExpandable;
363 if( info == null ) {
364 isExpandable = false;
365 }
366 else {
367 isExpandable = info.isExpandable();
368 }
369 return isExpandable;
370 }
371
372
373 /***
374 * Return true if this column can be expanded beyond its preferred size.
375 * The default is false.
376 *
377 * @param index The column.
378 * @return true if the column is expandable.
379 */
380 public boolean isColumnExpandable( final int index ) {
381 final HeaderPermanentInfo info
382 = getPermanentInfo( columnHeaderPermanentInfos_, index, false );
383 final boolean isExpandable;
384 if( info == null ) {
385 isExpandable = false;
386 }
387 else {
388 isExpandable = info.isExpandable();
389 }
390 return isExpandable;
391 }
392
393
394 /***
395 * Return the trace channel.
396 *
397 * @return The trace channel or null if one wasn't set.
398 */
399 public TraceChannel getTraceChannel() {
400 return traceChannel_;
401 }
402
403
404 /***
405 * Get whether or not we should ignore an components that are not visible.
406 *
407 * @return True if we should ignore them.
408 */
409 public boolean getIgnoreInvisibleComponents() {
410 return ignoreInvisibleComponents_;
411 }
412
413
414 /***
415 * Add the specified component to the layout with the specified
416 * constraints. This method should never be called as Container.addImpl()
417 * should call addLayoutComponent(Component,Object) instead. Not
418 * implemented.
419 *
420 * @param name The constraints string.
421 * @param comp the component that is being
422 * added.
423 * @throws UnsupportedOperationException If called.
424 */
425 public void addLayoutComponent( final String name, final Component comp )
426 throws UnsupportedOperationException {
427 throw new UnsupportedOperationException( "Use addLayoutComponent(Component,Object)" );
428 }
429
430
431 /***
432 * Add the specified component to the layout with the specified
433 * constraints. Throw an IllegalArgumentException if the same component is
434 * specified twice. The constraints object can be either an instance of
435 * TableLayoutConstraints or a String. If it is a string then an instance
436 * of TableLayoutConstraints will be created with the method
437 * TableLayoutConstraints.makeConstraints(String).
438 *
439 * @param comp The component that is being added.
440 * @param constraints The constraints object.
441 * @see TableLayoutConstraints#makeConstraints(String)
442 */
443 public void addLayoutComponent( final Component comp, final Object constraints ) {
444 assertNotNull("comp", comp);
445 assertNotNull("constraints", constraints);
446
447 TableLayoutConstraints tableLayoutConstraints = null;
448 if( constraints instanceof TableLayoutConstraints ) {
449 tableLayoutConstraints = (TableLayoutConstraints)constraints;
450 }
451 else if( constraints instanceof String ) {
452 tableLayoutConstraints
453 = TableLayoutConstraints.makeConstraints( (String)constraints );
454 }
455 else {
456 throw new DetailedIllegalArgumentException( "constraints", constraints, "Must be an instance "
457 + "of TableLayoutConstraints or String: "
458 + constraints.getClass().getName() );
459 }
460
461 final Iterator iterator = constraints_.iterator();
462 while( iterator.hasNext() ) {
463 if( ( (Entry)iterator.next() ).getComponent() == comp ) {
464 throw new DetailedIllegalArgumentException( "comp", comp, "Already in layout" );
465 }
466 }
467
468 tableLayoutConstraints.setImmutable();
469 final Entry entry = new Entry( comp, tableLayoutConstraints );
470 constraints_.add( entry );
471 invalidateLayout();
472 }
473
474
475 /***
476 * Remove the specified component from the layout.
477 *
478 * @param comp The component to remove.
479 */
480 public void removeLayoutComponent( final Component comp ) {
481 Trace.println( traceChannel_, "removeLayoutComponent(" + comp + ")" );
482 assertNotNull("comp", comp);
483
484 Entry entry;
485 final Iterator iterator = constraints_.iterator();
486 while( iterator.hasNext() ) {
487 entry = (Entry)iterator.next();
488 if( entry.getComponent() == comp ) {
489 iterator.remove();
490 invalidateLayout();
491 return;
492 }
493 }
494
495 throw new DetailedIllegalArgumentException( "comp", comp, "Not found" );
496 }
497
498
499 /***
500 * Get the minimum size of this layout.
501 *
502 * @param parent The container that this layout is managing.
503 * @return The minimum size required for this layout.
504 */
505 public Dimension minimumLayoutSize( final Container parent ) {
506 setParent( parent );
507
508 if( minimumSize_ == null ) {
509 calculateMinMaxPreferredSizes();
510 }
511 return minimumSize_;
512 }
513
514
515 /***
516 * Return the preferred layout size.
517 *
518 * @param parent The container that this layout is managing.
519 * @return The preferred layout size.
520 */
521 public Dimension preferredLayoutSize( final Container parent ) {
522 setParent( parent );
523
524 if( preferredSize_ == null ) {
525 calculateMinMaxPreferredSizes();
526 }
527 return preferredSize_;
528 }
529
530
531 /***
532 * Layout all the components in this container.
533 *
534 * @param parent The container that this layout is managing.
535 */
536 public void layoutContainer( final Container parent ) {
537 setParent( parent );
538
539 final Dimension parentSize = parent.getSize();
540 Entry entry;
541 int x = 0;
542 int y = 0;
543 int height = 0;
544 int width = 0;
545 int i;
546 int rowIndex;
547 int columnIndex;
548
549
550 if( parent.getComponentCount() == 0 ) {
551 return;
552 }
553
554 calculateSizes();
555 calculatePositions( parent, parentSize );
556
557 final Iterator iterator = constraints_.iterator();
558 while( iterator.hasNext() ) {
559 entry = (Entry)iterator.next();
560
561 rowIndex = entry.getConstraints().getRow();
562 columnIndex = entry.getConstraints().getColumn();
563
564 y = tempRowHeaders_[rowIndex].getStart();
565 x = tempColumnHeaders_[columnIndex].getStart();
566
567 height = 0;
568 width = 0;
569 for( i = 0; i < entry.getConstraints().getRowSpan(); i++ ) {
570 height += tempRowHeaders_[rowIndex + i].getActual();
571 }
572 for( i = 0; i < entry.getConstraints().getColumnSpan(); i++ ) {
573 width += tempColumnHeaders_[columnIndex + i].getActual();
574 }
575
576 positionComponent( entry, x, y, width, height );
577 }
578 }
579
580
581 /***
582 * Return the maximum layout size.
583 *
584 * @param target The container that this layout is managing.
585 * @return The maximum layout size.
586 */
587 public Dimension maximumLayoutSize( final Container target ) {
588 setParent( target );
589 if( maximumSize_ == null ) {
590 calculateMinMaxPreferredSizes();
591 }
592 return maximumSize_;
593 }
594
595
596 /***
597 * Invalidate the layout and throw away and temporary calculations.
598 *
599 * @param target The container that this layout is managing.
600 */
601 public void invalidateLayout( final Container target ) {
602 setParent( target );
603 invalidateLayout();
604 }
605
606
607 /***
608 * A debugging method that draws lines on the parent component to show
609 * where the table cell boundaries are. The lines will be drawn in the
610 * current colour.
611 *
612 * @param graphics The graphics object.
613 * @see TableLayoutDebuggingPanel
614 */
615 public void drawOutlines( final Graphics graphics ) {
616 int i;
617 int j;
618 Header column;
619 Header row;
620
621 for( i = 0; i < columnCount_; i++ ) {
622 column = tempColumnHeaders_[i];
623 for( j = 0; j < rowCount_; j++ ) {
624 row = tempRowHeaders_[j];
625 graphics.drawRect(
626 column.getStart(),
627 row.getStart(),
628 column.getActual(),
629 row.getActual() );
630 }
631 }
632 }
633
634
635 /***
636 * Set the parent container for this layout.
637 *
638 * @param newParent The new parent value
639 */
640 private void setParent( final Container newParent ) {
641 assertNotNull("newParent", newParent);
642
643 if( ( parent_ != null ) && ( newParent != parent_ ) ) {
644 throw new DetailedIllegalArgumentException( "newParent", newParent, "Attempt to reassign parent" );
645 }
646
647 parent_ = newParent;
648 }
649
650
651 /***
652 * Return an array containing all the headers that are expandable.
653 *
654 * @param first Description of Parameter
655 * @param last Description of Parameter
656 * @param headers Description of Parameter
657 * @return The expandableHeaders value
658 */
659 private Header[] getExpandableHeaders( final int first,
660 final int last,
661 final Header[] headers ) {
662
663 final List list = new ArrayList( headers.length );
664 int i;
665
666 for( i = first; i <= last; i++ ) {
667 if( headers[i].isExpandable() ) {
668 list.add( headers[i] );
669 }
670 }
671
672 final Header expandableHeaders[] = new Header[list.size()];
673 list.toArray( expandableHeaders );
674 return expandableHeaders;
675 }
676
677
678 /***
679 * Return the minimum row height. The default is 0.
680 *
681 * @param index Description of Parameter
682 * @return The minimumRowHeight value
683 */
684 private int getMinimumRowHeight( final int index ) {
685 final HeaderPermanentInfo info
686 = getPermanentInfo( rowHeaderPermanentInfos_, index, false );
687 final int result;
688 if( info == null ) {
689 result = 0;
690 }
691 else {
692 result = info.getMin();
693 }
694 return result;
695 }
696
697
698 /***
699 * Return the minimum column width. The default is 0.
700 *
701 * @param index The column index.
702 * @return The minimum column width for the specified column.
703 */
704 private int getMinimumColumnWidth( final int index ) {
705 final HeaderPermanentInfo info
706 = getPermanentInfo( columnHeaderPermanentInfos_, index, false );
707 final int result;
708 if( info == null ) {
709 result = 0;
710 }
711 else {
712 result = info.getMin();
713 }
714 return result;
715 }
716
717
718 /***
719 * TODO: Provide comments
720 *
721 * @param infoList Description of Parameter
722 * @param index Description of Parameter
723 * @param createIfNeeded Description of Parameter
724 * @return The permanentInfo value
725 */
726 private HeaderPermanentInfo getPermanentInfo( final Set infoList,
727 final int index,
728 final boolean createIfNeeded ) {
729 final Iterator iterator = infoList.iterator();
730 HeaderPermanentInfo info;
731
732 while( iterator.hasNext() ) {
733 info = (HeaderPermanentInfo)iterator.next();
734 if( info.getIndex() == index ) {
735 return info;
736 }
737 }
738
739
740 if( createIfNeeded ) {
741 if( traceChannel_ != null ) {
742 final StringBuffer buffer = new StringBuffer();
743 buffer.append( "getPermanentInfo() creating new info for " );
744 if( infoList == rowHeaderPermanentInfos_ ) {
745 buffer.append( "row " );
746 }
747 else {
748 buffer.append( "column " );
749 }
750 buffer.append( index );
751 Trace.println( buffer.toString() );
752 }
753 info = new HeaderPermanentInfo( index );
754 infoList.add( info );
755 }
756 else {
757 info = null;
758 }
759
760 return info;
761 }
762
763
764 /***
765 * Return the TableLayoutConstraints object that corresponds to the
766 * specified component or null if this component could not be found.
767 *
768 * @param component Description of Parameter
769 * @return The constraints value
770 */
771 private TableLayoutConstraints getConstraints( final Component component ) {
772 Entry entry;
773
774 final Iterator iterator = constraints_.iterator();
775 while( iterator.hasNext() ) {
776 entry = (Entry)iterator.next();
777
778 if( entry.getComponent() == component ) {
779 return entry.getConstraints();
780 }
781 }
782
783 return null;
784 }
785
786
787 /***
788 * Return the minimum size of the specified component. If the component is
789 * not visible and we are ignoring invisible components then return a size
790 * of 0,0
791 *
792 * @param component The component that we will be querying
793 * @return The size
794 */
795 private final Dimension getComponentMinimumSize( final Component component ) {
796 final Dimension size;
797 if( component.isVisible() == false && ignoreInvisibleComponents_ == true ) {
798 size = new Dimension( 0, 0 );
799 }
800 else {
801 size = component.getMinimumSize();
802 }
803 return size;
804 }
805
806
807 /***
808 * Return the minimum size of the specified component. If the component is
809 * not visible and we are ignoring invisible components then return a size
810 * of 0,0
811 *
812 * @param component The component that we will be querying
813 * @return The size
814 */
815 private final Dimension getComponentMaximumSize( final Component component ) {
816 final Dimension size;
817 if( component.isVisible() == false && ignoreInvisibleComponents_ == true ) {
818 size = new Dimension( 0, 0 );
819 }
820 else {
821 size = component.getMaximumSize();
822 }
823 return size;
824 }
825
826
827 /***
828 * Return the minimum size of the specified component. If the component is
829 * not visible and we are ignoring invisible components then return a size
830 * of 0,0
831 *
832 * @param component The component that we will be querying
833 * @return The size
834 */
835 private final Dimension getComponentPreferredSize( final Component component ) {
836 final Dimension size;
837 if( component.isVisible() == false && ignoreInvisibleComponents_ == true ) {
838 size = new Dimension( 0, 0 );
839 }
840 else {
841 size = component.getPreferredSize();
842 }
843 return size;
844 }
845
846
847 /***
848 * Calculate the various sizes.
849 */
850 private void calculateMinMaxPreferredSizes() {
851 int i;
852 int xMin = 0;
853 int yMin = 0;
854 int xMax = 0;
855 int yMax = 0;
856 int xPreferred = 0;
857 int yPreferred = 0;
858
859 calculateRowAndColumnCount();
860 calculateSizes();
861
862 if( false ) {
863 Trace.println(
864
865 "calculateMinMaxPreferredSize() tempRowHeaders_=["
866 + tempRowHeaders_
867 + "] rowCount_=["
868 + rowCount_
869 + "] columnCount=["
870 + columnCount_
871 + "] tempSizesAreValid_=["
872 + tempSizesAreValid_
873 + "]" );
874 }
875
876 for( i = 0; i < rowCount_; i++ ) {
877 yMin += tempRowHeaders_[i].getMin();
878 yMax += tempRowHeaders_[i].getMax();
879 yPreferred += tempRowHeaders_[i].getPreferred();
880 }
881
882 for( i = 0; i < columnCount_; i++ ) {
883 xMin += tempColumnHeaders_[i].getMin();
884 xMax += tempColumnHeaders_[i].getMax();
885 xPreferred += tempColumnHeaders_[i].getPreferred();
886 }
887
888
889 final Insets insets = parent_.getInsets();
890 final int horizontalInset = insets.left + insets.right;
891 final int verticalInset = insets.top + insets.bottom;
892
893 xMin += horizontalInset;
894 yMin += verticalInset;
895 xPreferred += horizontalInset;
896 yPreferred += verticalInset;
897 xMax += horizontalInset;
898 yMax += verticalInset;
899
900 xPreferred = Math.max( xPreferred, xMin );
901 xMax = Math.max( xMax, xPreferred );
902 yPreferred = Math.max( yPreferred, yMin );
903 yMax = Math.max( yMax, yPreferred );
904
905
906 if( areAnyExpandable( rowHeaderPermanentInfos_ ) ) {
907 yMax = Integer.MAX_VALUE;
908 }
909 if( areAnyExpandable( columnHeaderPermanentInfos_ ) ) {
910 xMax = Integer.MAX_VALUE;
911 }
912
913
914
915 minimumSize_ = new Dimension( xMin, yMin );
916 maximumSize_ = new Dimension( xMax, yMax );
917 preferredSize_ = new Dimension( xPreferred, yPreferred );
918
919 if( constraints_.size() == 0 ) {
920
921 maximumSize_ = new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
922 }
923 }
924
925
926 /***
927 * Return true if any of the infos are expandable.
928 *
929 * @param permanentInfos The infos.
930 * @return Description of the Returned Value
931 */
932 private boolean areAnyExpandable( final Set permanentInfos ) {
933 HeaderPermanentInfo info;
934
935 final Iterator iterator = permanentInfos.iterator();
936 while( iterator.hasNext() ) {
937 info = (HeaderPermanentInfo)iterator.next();
938 if( info.isExpandable() ) {
939 return true;
940 }
941 }
942
943 return false;
944 }
945
946
947 /***
948 * Invalidate the layout.
949 */
950 private void invalidateLayout() {
951 tempColumnHeaders_ = null;
952 tempRowHeaders_ = null;
953 tempSizesAreValid_ = false;
954 minimumSize_ = null;
955 maximumSize_ = null;
956 preferredSize_ = null;
957 actualSize_ = null;
958 columnCount_ = 0;
959 rowCount_ = 0;
960 }
961
962
963 /***
964 * Calculate all the various sizing information required for this layout.
965 * Called by layoutContainer(), minimumLayoutSize(), maximumLayoutSize()
966 */
967 private void calculateSizes() {
968 if( false ) {
969 Trace.println( "TableLayout.calculateSizes() "
970 + "tempSizesAreValid_=["
971 + tempSizesAreValid_
972 + "] tempRowHeaders_=["
973 + tempRowHeaders_
974 + "] tempColumnHeaders_=["
975 + tempColumnHeaders_
976 + "]" );
977 }
978 if( tempSizesAreValid_ && tempRowHeaders_ == null ) {
979 Trace.whereAmI();
980 }
981 Dimension minSize;
982 Dimension maxSize;
983 Dimension preferredSize;
984 Entry entry;
985 TableLayoutConstraints constraints;
986 Iterator iterator;
987
988
989 if( rowCount_ == 0 || columnCount_ == 0 ) {
990 return;
991 }
992
993 if( tempSizesAreValid_ ) {
994 return;
995 }
996 tempSizesAreValid_ = true;
997
998 initTempSizes();
999
1000
1001
1002
1003
1004 iterator = constraints_.iterator();
1005 while( iterator.hasNext() ) {
1006 entry = (Entry)iterator.next();
1007 constraints = entry.getConstraints();
1008
1009 if( constraints.getObeyMinimumSize() ) {
1010 minSize = getComponentMinimumSize( entry.getComponent() );
1011 }
1012 else {
1013 minSize = new Dimension( 0, 0 );
1014 }
1015
1016 if( constraints.getObeyMaximumSize() ) {
1017 maxSize = getComponentMaximumSize( entry.getComponent() );
1018 }
1019 else {
1020 maxSize = new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
1021 }
1022 preferredSize = getComponentPreferredSize( entry.getComponent() );
1023
1024
1025 if( constraints.getRowSpan() == 1 ) {
1026 Trace.println( traceChannel_, "tempRowHeaders_.length=["
1027 + tempRowHeaders_.length + "] constraints.getRow()=["
1028 + constraints.getRow() + "]" );
1029 tempRowHeaders_[constraints.getRow()].setHasComponents( true );
1030
1031 if( minSize.height > tempRowHeaders_[constraints.getRow()].getMin() ) {
1032 tempRowHeaders_[constraints.getRow()].setMin( minSize.height );
1033 }
1034 if( maxSize.height > tempRowHeaders_[constraints.getRow()].getMax() ) {
1035 tempRowHeaders_[constraints.getRow()].setMax( maxSize.height );
1036 }
1037 if( preferredSize.height > tempRowHeaders_[constraints.getRow()].getPreferred() ) {
1038 tempRowHeaders_[constraints.getRow()].setPreferred( preferredSize.height );
1039 }
1040
1041
1042
1043
1044
1045 if( tempRowHeaders_[constraints.getRow()].getMin()
1046 > tempRowHeaders_[constraints.getRow()].getMax() ) {
1047 tempRowHeaders_[constraints.getRow()].setMax( tempRowHeaders_[constraints.getRow()].getMin() );
1048 }
1049
1050
1051 if( tempRowHeaders_[constraints.getRow()].getMin()
1052 > tempRowHeaders_[constraints.getRow()].getPreferred() ) {
1053 tempRowHeaders_[constraints.getRow()].setPreferred(
1054 tempRowHeaders_[constraints.getRow()].getMin() );
1055 }
1056 }
1057
1058
1059 if( constraints.getColumnSpan() == 1 ) {
1060 if( minSize.width > tempColumnHeaders_[constraints.getColumn()].getMin() ) {
1061 tempColumnHeaders_[constraints.getColumn()].setMin( minSize.width );
1062 }
1063 if( maxSize.width > tempColumnHeaders_[constraints.getColumn()].getMax() ) {
1064 tempColumnHeaders_[constraints.getColumn()].setMax( maxSize.width );
1065 }
1066 if( preferredSize.width > tempColumnHeaders_[constraints.getColumn()].getPreferred() ) {
1067 tempColumnHeaders_[constraints.getColumn()].setPreferred( preferredSize.width );
1068 }
1069
1070 tempColumnHeaders_[constraints.getColumn()].setHasComponents( true );
1071
1072
1073
1074 if( tempColumnHeaders_[constraints.getColumn()].getMin()
1075 > tempColumnHeaders_[constraints.getColumn()].getMax() ) {
1076 tempColumnHeaders_[constraints.getColumn()].setMax(
1077 tempColumnHeaders_[constraints.getColumn()].getMin() );
1078 }
1079
1080
1081 if( tempColumnHeaders_[constraints.getColumn()].getMin()
1082 > tempColumnHeaders_[constraints.getColumn()].getPreferred() ) {
1083 tempColumnHeaders_[constraints.getColumn()].setPreferred(
1084 tempColumnHeaders_[constraints.getColumn()].getMin() );
1085 }
1086 }
1087 }
1088
1089
1090
1091
1092 iterator = constraints_.iterator();
1093 while( iterator.hasNext() ) {
1094 entry = (Entry)iterator.next();
1095 constraints = entry.getConstraints();
1096
1097 if( constraints.getObeyMinimumSize() ) {
1098 minSize = getComponentMinimumSize( entry.getComponent() );
1099 }
1100 else {
1101 minSize = new Dimension( 0, 0 );
1102 }
1103
1104 if( constraints.getObeyMaximumSize() == true ) {
1105 maxSize = getComponentMaximumSize( entry.getComponent() );
1106 }
1107 else {
1108 maxSize = new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
1109 }
1110 preferredSize = getComponentPreferredSize( entry.getComponent() );
1111
1112
1113 if( constraints.getRowSpan() != 1 ) {
1114 adjustSizesForSpanning( constraints.getRow(), constraints.getRowSpan(),
1115 tempRowHeaders_,
1116 minSize.height, preferredSize.height, maxSize.height );
1117 }
1118
1119
1120 if( constraints.getColumnSpan() != 1 ) {
1121 adjustSizesForSpanning( constraints.getColumn(), constraints.getColumnSpan(),
1122 tempColumnHeaders_,
1123 minSize.width, preferredSize.width, maxSize.width );
1124 }
1125 }
1126
1127
1128
1129 adjustHeaderSizes( tempRowHeaders_ );
1130 adjustHeaderSizes( tempColumnHeaders_ );
1131 }
1132
1133
1134 /***
1135 * Adjust the various sizes to account for components than span multiple
1136 * columns/rows.
1137 *
1138 * @param start The starting index of the component.
1139 * @param span The number of columns/rows that the component
1140 * spans.
1141 * @param sizes The headers that we are adjusting.
1142 * @param minSize The minimum size of the component.
1143 * @param preferredSize The preferred size of the component.
1144 * @param maxSize The maximum size of the component.
1145 */
1146 private void adjustSizesForSpanning( final int start,
1147 final int span,
1148 final Header sizes[],
1149 final int minSize,
1150 final int preferredSize,
1151 final int maxSize ) {
1152 int combinedSize;
1153 int remainder;
1154 int i;
1155
1156 final Header expandableHeaders[] = getExpandableHeaders( start, start + span, sizes );
1157
1158
1159
1160
1161
1162
1163
1164 combinedSize = 0;
1165 for( i = 0; i < span; i++ ) {
1166 combinedSize += sizes[start + i].getMin();
1167 }
1168 if( minSize > combinedSize ) {
1169 if( expandableHeaders.length == 0 ) {
1170 final int delta = ( minSize - combinedSize ) / span;
1171 for( i = 0; i < span; i++ ) {
1172 sizes[start + i].setMin( sizes[start + i].getMin() + delta );
1173 combinedSize += delta;
1174 }
1175
1176 remainder = minSize - combinedSize;
1177 for( i = 0; i < remainder; i++ ) {
1178 sizes[start + i].setMin( sizes[start + i].getMin() + 1 );
1179 }
1180 }
1181 else {
1182 final int delta = ( minSize - combinedSize ) / expandableHeaders.length;
1183 for( i = 0; i < expandableHeaders.length; i++ ) {
1184 expandableHeaders[i].setMin( expandableHeaders[i].getMin() + delta );
1185 combinedSize += delta;
1186 }
1187
1188 remainder = minSize - combinedSize;
1189 for( i = 0; i < remainder; i++ ) {
1190 expandableHeaders[i].setMin( expandableHeaders[i].getMin() + 1 );
1191 }
1192 }
1193 }
1194
1195
1196
1197
1198 combinedSize = 0;
1199 for( i = 0; i < span; i++ ) {
1200 combinedSize += sizes[start + i].getPreferred();
1201 }
1202 if( preferredSize > combinedSize ) {
1203 if( expandableHeaders.length == 0 ) {
1204
1205 final int delta = ( preferredSize - combinedSize ) / span;
1206 for( i = 0; i < span; i++ ) {
1207 sizes[start + i].setPreferred( sizes[start + i].getPreferred() + delta );
1208 combinedSize += delta;
1209 }
1210
1211 remainder = preferredSize - combinedSize;
1212 for( i = 0; i < remainder; i++ ) {
1213 sizes[start + i].setPreferred( sizes[start + i].getPreferred() + 1 );
1214 }
1215 }
1216 else {
1217
1218 final int delta = ( preferredSize - combinedSize ) / expandableHeaders.length;
1219 for( i = 0; i < expandableHeaders.length; i++ ) {
1220 expandableHeaders[i].setPreferred( expandableHeaders[i].getPreferred() + delta );
1221 combinedSize += delta;
1222 }
1223
1224 remainder = preferredSize - combinedSize;
1225 for( i = 0; i < remainder; i++ ) {
1226 expandableHeaders[i].setPreferred( expandableHeaders[i].getPreferred() + 1 );
1227 }
1228 }
1229 }
1230 }
1231
1232
1233 /***
1234 * Calculate all the positions of the various rows/columns
1235 *
1236 * @param parent Description of Parameter
1237 * @param parentSize Description of Parameter
1238 */
1239 private void calculatePositions( final Container parent,
1240 final Dimension parentSize ) {
1241 int i;
1242 int rowStart;
1243 int columnStart;
1244
1245 final Insets insets = parent.getInsets();
1246
1247
1248
1249
1250 calculateActualSizes( parentSize );
1251
1252
1253
1254
1255 switch ( verticalAlignment_ ) {
1256 case TOP:
1257 rowStart = insets.top;
1258 break;
1259 case BOTTOM:
1260 rowStart = parentSize.height - actualSize_.height - insets.bottom;
1261 break;
1262 case CENTER:
1263 rowStart = ( parentSize.height - actualSize_.height
1264 - insets.top - insets.bottom ) / 2 + insets.top;
1265 break;
1266 default:
1267 throw new IllegalStateException( "Unknown verticalAlignment: "
1268 + verticalAlignment_ );
1269 }
1270
1271 switch ( horizontalAlignment_ ) {
1272 case LEFT:
1273 columnStart = insets.left;
1274 break;
1275 case RIGHT:
1276 columnStart = parentSize.width - actualSize_.width - insets.right;
1277 break;
1278 case CENTER:
1279 columnStart = ( parentSize.width - actualSize_.width
1280 - insets.left - insets.right ) / 2 + insets.left;
1281 break;
1282 default:
1283 throw new IllegalStateException( "Unknown horizontalAlignment: "
1284 + horizontalAlignment_ );
1285 }
1286
1287 tempRowHeaders_[0].setStart( rowStart );
1288 for( i = 1; i < rowCount_; i++ ) {
1289 tempRowHeaders_[i].setStart( tempRowHeaders_[i - 1].getStart() + tempRowHeaders_[i - 1].getActual() );
1290 }
1291
1292 tempColumnHeaders_[0].setStart( columnStart );
1293 for( i = 1; i < columnCount_; i++ ) {
1294 tempColumnHeaders_[i].setStart( tempColumnHeaders_[i - 1].getStart()
1295 + tempColumnHeaders_[i - 1].getActual() );
1296 }
1297
1298
1299 if( traceChannel_ != null ) {
1300 Trace.println( traceChannel_,
1301 "TableLayout.calculatePositions() START parentSize="
1302 + parentSize );
1303 for( i = 0; i < rowCount_; i++ ) {
1304 Trace.println( traceChannel_,
1305 " tempRowSizes[" + i + "]="
1306 + tempRowHeaders_[i] );
1307 }
1308 for( i = 0; i < columnCount_; i++ ) {
1309 Trace.println( traceChannel_,
1310 " tempColumnSizes[" + i + "]="
1311 + tempColumnHeaders_[i] );
1312 }
1313 final Component children[] = parent_.getComponents();
1314 StringBuffer buffer;
1315 TableLayoutConstraints constraints;
1316 for( i = 0; i < children.length; i++ ) {
1317 constraints = getConstraints( children[i] );
1318 buffer = new StringBuffer();
1319 buffer.append( " children[" + i + "] min=" );
1320 buffer.append( toString( children[i].getMinimumSize() ) );
1321 buffer.append( " preferred=" );
1322 buffer.append( toString( children[i].getPreferredSize() ) );
1323 buffer.append( " visible=[" );
1324 buffer.append( children[i].isVisible() );
1325 buffer.append( " type=[" );
1326 buffer.append( children[i].getClass().getName() );
1327 buffer.append( "] name=[" );
1328 buffer.append( children[i].getName() );
1329 buffer.append( "] row=[" );
1330 buffer.append( constraints.getRow() );
1331 buffer.append( "+" );
1332 buffer.append( constraints.getRowSpan() );
1333 buffer.append( "] column=[" );
1334 buffer.append( constraints.getColumn() );
1335 buffer.append( "+" );
1336 buffer.append( constraints.getColumnSpan() );
1337 buffer.append( "]" );
1338 Trace.println( traceChannel_, buffer.toString() );
1339 }
1340 Trace.println( traceChannel_,
1341 "TableLayout.calculatePositions() END" );
1342 }
1343 }
1344
1345
1346 /***
1347 * Position one component given the bounding coordinates
1348 *
1349 * @param entry Description of Parameter
1350 * @param x Description of Parameter
1351 * @param y Description of Parameter
1352 * @param width Description of Parameter
1353 * @param height Description of Parameter
1354 */
1355 private void positionComponent( final Entry entry,
1356 final int x, final int y,
1357 final int width, final int height ) {
1358
1359 final TableLayoutConstraints constraints = entry.getConstraints();
1360 final Dimension maxSize;
1361
1362 final Dimension minSize = getComponentPreferredSize( entry.getComponent() );
1363 int newWidth;
1364 int newHeight;
1365 final int newX;
1366 final int newY;
1367
1368 if( constraints.getObeyMaximumSize() == true ) {
1369 maxSize = getComponentMaximumSize( entry.getComponent() );
1370 }
1371 else {
1372 maxSize = new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
1373 }
1374
1375 if( constraints.getVerticalStretch() ) {
1376 newHeight = Math.min( maxSize.height, height );
1377 }
1378 else {
1379 newHeight = minSize.height;
1380 }
1381
1382 if( constraints.getHorizontalStretch() ) {
1383 newWidth = Math.min( maxSize.width, width );
1384 }
1385 else {
1386 newWidth = minSize.width;
1387 }
1388
1389 if( newHeight > height ) {
1390 newHeight = height;
1391 }
1392 if( newWidth > width ) {
1393 newWidth = width;
1394 }
1395
1396 switch ( constraints.getVerticalAlignment() ) {
1397 case TOP:
1398 newY = y;
1399 break;
1400 case BOTTOM:
1401 newY = y + ( height - newHeight );
1402 break;
1403 case CENTER:
1404 newY = y + ( height - newHeight ) / 2;
1405 break;
1406 default:
1407 throw new IllegalStateException(
1408 "Illegal value for verticalAlignment: "
1409 + constraints.getVerticalAlignment() );
1410 }
1411
1412 switch ( constraints.getHorizontalAlignment() ) {
1413 case LEFT:
1414 newX = x;
1415 break;
1416 case RIGHT:
1417 newX = x + ( width - newWidth );
1418 break;
1419 case CENTER:
1420 newX = x + ( width - newWidth ) / 2;
1421 break;
1422 default:
1423 throw new IllegalStateException(
1424 "Illegal value for horizontalAlignment: "
1425 + constraints.getVerticalAlignment() );
1426 }
1427
1428 entry.getComponent().setBounds( newX, newY, newWidth, newHeight );
1429 }
1430
1431
1432 /***
1433 * The list of constraints has been modified. Update the row and column
1434 * counts according to the new constraints.
1435 */
1436 private void calculateRowAndColumnCount() {
1437 Entry entry;
1438 int lastRow = -1;
1439 int lastColumn = -1;
1440 int row;
1441 int column;
1442
1443 Iterator iterator;
1444 TableLayoutConstraints constraints;
1445
1446 iterator = constraints_.iterator();
1447 while( iterator.hasNext() ) {
1448 entry = (Entry)iterator.next();
1449 constraints = entry.getConstraints();
1450
1451 row = constraints.getRow()
1452 + constraints.getRowSpan();
1453 if( row > lastRow ) {
1454 lastRow = row;
1455 }
1456
1457 column = constraints.getColumn()
1458 + constraints.getColumnSpan();
1459 if( column > lastColumn ) {
1460 lastColumn = column;
1461 }
1462 }
1463
1464 HeaderPermanentInfo info;
1465
1466
1467
1468
1469 iterator = rowHeaderPermanentInfos_.iterator();
1470 while( iterator.hasNext() ) {
1471 info = (HeaderPermanentInfo)iterator.next();
1472 if( info.getIndex() > lastRow ) {
1473 lastRow = info.getIndex();
1474 }
1475 }
1476
1477 iterator = columnHeaderPermanentInfos_.iterator();
1478 while( iterator.hasNext() ) {
1479 info = (HeaderPermanentInfo)iterator.next();
1480 if( info.getIndex() > lastColumn ) {
1481 Trace.println( traceChannel_,
1482 "Increasing column count to " + info.getIndex() );
1483 lastColumn = info.getIndex();
1484 }
1485 }
1486
1487 rowCount_ = lastRow + 1;
1488 columnCount_ = lastColumn + 1;
1489
1490
1491
1492
1493
1494
1495 if( rowCount_ == 0 || columnCount_ == 0 ) {
1496 rowCount_ = 0;
1497 columnCount_ = 0;
1498 }
1499
1500 if( traceChannel_ != null ) {
1501 Trace.println( traceChannel_,
1502 "calculateRowAndColumnCount() rowCount="
1503 + rowCount_
1504 + " columnCount_="
1505 + columnCount_ );
1506 }
1507 }
1508
1509
1510 /***
1511 * Calculate the actual sizes to be used based on the actual dimension of
1512 * the parent container.
1513 *
1514 * @param parentSize Description of Parameter
1515 */
1516 private void calculateActualSizes( final Dimension parentSize ) {
1517 final Dimension preferredSize = preferredLayoutSize( parent_ );
1518 final Insets insets = parent_.getInsets();
1519
1520 final int x = calculateActualSizes( tempColumnHeaders_,
1521 preferredSize.width,
1522 parentSize.width - insets.left - insets.right );
1523 final int y = calculateActualSizes( tempRowHeaders_,
1524 preferredSize.height,
1525 parentSize.height - insets.top - insets.bottom );
1526 actualSize_ = new Dimension( x, y );
1527 }
1528
1529
1530 /***
1531 * Fix up all the headers such that minimum <= preferred <= maximum
1532 *
1533 * @param sizes Description of Parameter
1534 */
1535 private void adjustHeaderSizes( final Header sizes[] ) {
1536 int i;
1537 Header header;
1538
1539 for( i = 0; i < sizes.length; i++ ) {
1540 header = sizes[i];
1541 if( header.getMin() > header.getPreferred() ) {
1542 header.setPreferred( header.getMin() );
1543 }
1544 if( header.getMin() > header.getMax() ) {
1545 header.setMax( header.getMin() );
1546 }
1547 if( header.getPreferred() > header.getMax() ) {
1548 header.setPreferred( header.getMax() );
1549 }
1550 }
1551 }
1552
1553
1554 /***
1555 * Calculate the actual sizes for the specified row or column headers.
1556 * Return the actual length.
1557 *
1558 * @param sizes Description of Parameter
1559 * @param preferredLength Description of Parameter
1560 * @param clipLength Description of Parameter
1561 * @return Description of the Returned Value
1562 */
1563 private int calculateActualSizes( final Header sizes[],
1564 final int preferredLength,
1565 final int clipLength ) {
1566
1567 int i;
1568
1569 if( clipLength < 1 ) {
1570
1571
1572 for( i = 0; i < sizes.length; i++ ) {
1573 sizes[i].setActual( 0 );
1574 }
1575 return 0;
1576 }
1577
1578
1579
1580 if( preferredLength <= clipLength ) {
1581 for( i = 0; i < sizes.length; i++ ) {
1582 sizes[i].setActual( sizes[i].getPreferred() );
1583 }
1584
1585 if( preferredLength < clipLength ) {
1586 expandToFit( sizes, clipLength );
1587 }
1588 }
1589 else {
1590 shrinkToFit( sizes, clipLength );
1591 }
1592
1593 int actualLength = 0;
1594 for( i = 0; i < sizes.length; i++ ) {
1595 actualLength += sizes[i].getActual();
1596 }
1597
1598 return actualLength;
1599 }
1600
1601
1602 /***
1603 * Expand the specified sizes to fit within the specified clipLength.
1604 *
1605 * @param sizes Description of Parameter
1606 * @param clipLength Description of Parameter
1607 */
1608 private void expandToFit( final Header sizes[], final int clipLength ) {
1609
1610 int i;
1611 int numberExpandable = 0;
1612 int currentLength = 0;
1613
1614 for( i = 0; i < sizes.length; i++ ) {
1615 if( sizes[i].isExpandable() == true ) {
1616 numberExpandable++;
1617 }
1618 currentLength += sizes[i].getActual();
1619 }
1620
1621
1622 if( numberExpandable == 0 ) {
1623 return;
1624 }
1625 int addAmount = ( clipLength - currentLength ) / numberExpandable;
1626 for( i = 0; i < sizes.length; i++ ) {
1627 if( sizes[i].isExpandable() == true ) {
1628 sizes[i].setActual( sizes[i].getActual() + addAmount );
1629 }
1630 }
1631
1632
1633 int remaining = ( clipLength - currentLength )
1634 - ( addAmount * numberExpandable );
1635 if( remaining != 0 ) {
1636 for( i = 0; i < sizes.length; i++ ) {
1637 if( sizes[i].isExpandable() == true ) {
1638 sizes[i].setActual( sizes[i].getActual() + 1 );
1639 remaining--;
1640 if( remaining == 0 ) {
1641 break;
1642 }
1643 }
1644 }
1645 }
1646
1647 }
1648
1649
1650 /***
1651 * Shrink the specified sizes to fit within the specified clipLength. Do
1652 * not shrink beyond the minimum size.
1653 *
1654 * @param sizes Description of Parameter
1655 * @param clipLength Description of Parameter
1656 */
1657 private void shrinkToFit( final Header sizes[], final int clipLength ) {
1658 if( clipLength < 0 ) {
1659 throw new DetailedIllegalArgumentException(
1660 "clipLength", new Integer(clipLength), "may not be negative" );
1661 }
1662
1663 int i;
1664 int remaining = clipLength;
1665
1666
1667 for( i = 0; i < sizes.length; i++ ) {
1668 sizes[i].setActual( sizes[i].getMin() );
1669 remaining -= sizes[i].getActual();
1670 }
1671
1672 if( remaining < 0 ) {
1673
1674
1675 return;
1676 }
1677
1678
1679
1680 int delta;
1681 int addAmount = 1;
1682 int numberChanged = sizes.length;
1683 while( numberChanged != 0 ) {
1684
1685
1686 addAmount = remaining / numberChanged;
1687 if( addAmount == 0 ) {
1688 addAmount = 1;
1689 }
1690
1691 numberChanged = 0;
1692 for( i = 0; i < sizes.length; i++ ) {
1693 delta = sizes[i].getPreferred() - sizes[i].getActual();
1694
1695 if( delta > 0 ) {
1696 delta = Math.min( delta, addAmount );
1697 sizes[i].setActual( sizes[i].getActual() + delta );
1698 numberChanged++;
1699 remaining -= delta;
1700 if( remaining == 0 ) {
1701 return;
1702 }
1703 }
1704 }
1705 }
1706 }
1707
1708
1709 /***
1710 * Initialize the temporary arrays (tempRowHeaders_ and tempColumnHeaders_)
1711 * for use in a calculation.
1712 */
1713 private void initTempSizes() {
1714 int i;
1715
1716
1717
1718
1719
1720
1721 if( ( tempRowHeaders_ == null ) || ( tempRowHeaders_.length != rowCount_ ) ) {
1722 tempRowHeaders_ = new Header[rowCount_];
1723 for( i = 0; i < rowCount_; i++ ) {
1724 tempRowHeaders_[i] = new Header();
1725 }
1726 }
1727 if( ( tempColumnHeaders_ == null ) || ( tempColumnHeaders_.length != columnCount_ ) ) {
1728 tempColumnHeaders_ = new Header[columnCount_];
1729 for( i = 0; i < columnCount_; i++ ) {
1730 tempColumnHeaders_[i] = new Header();
1731 }
1732 }
1733
1734
1735
1736
1737 for( i = 0; i < tempRowHeaders_.length; i++ ) {
1738 tempRowHeaders_[i].setMin( getMinimumRowHeight( i ) );
1739 tempRowHeaders_[i].setPreferred( getMinimumRowHeight( i ) );
1740 tempRowHeaders_[i].setMax( Integer.MAX_VALUE );
1741 tempRowHeaders_[i].setHasComponents( false );
1742 tempRowHeaders_[i].setExpandable( isRowExpandable( i ) );
1743 }
1744 for( i = 0; i < tempColumnHeaders_.length; i++ ) {
1745 tempColumnHeaders_[i].setMin( getMinimumColumnWidth( i ) );
1746 tempColumnHeaders_[i].setPreferred( getMinimumColumnWidth( i ) );
1747 tempColumnHeaders_[i].setMax( Integer.MAX_VALUE );
1748 tempColumnHeaders_[i].setHasComponents( false );
1749 tempColumnHeaders_[i].setExpandable( isColumnExpandable( i ) );
1750 }
1751 }
1752
1753
1754 /***
1755 * A convenience class to attach the constraints to a component.
1756 */
1757 private class Entry implements Serializable {
1758 private static final long serialVersionUID = 396191633848670929L;
1759
1760 /***
1761 * Description of the Field
1762 */
1763 private final Component component_;
1764 /***
1765 * Description of the Field
1766 */
1767 private final TableLayoutConstraints constraints_;
1768
1769
1770 /***
1771 * Constructor for the Entry object
1772 *
1773 * @param comp Description of Parameter
1774 * @param constraints Description of Parameter
1775 */
1776 public Entry( final Component comp,
1777 final TableLayoutConstraints constraints ) {
1778 component_ = comp;
1779 constraints_ = constraints;
1780 }
1781
1782
1783
1784 /***
1785 * Gets the component attribute of the Entry object
1786 *
1787 * @return The component value
1788 */
1789 public final Component getComponent() {
1790 return component_;
1791 }
1792
1793
1794 /***
1795 * Gets the constraints attribute of the Entry object
1796 *
1797 * @return The constraints value
1798 */
1799 public final TableLayoutConstraints getConstraints() {
1800 return constraints_;
1801 }
1802
1803
1804 /***
1805 * Description of the Method
1806 *
1807 * @return Description of the Returned Value
1808 */
1809 public String toString() {
1810 return this.getClass().getName()
1811 + " component_=" + getComponent()
1812 + " constraints_=" + getConstraints();
1813 }
1814 }
1815
1816
1817 /***
1818 * A convenience class to hold information specific to a row or column.
1819 */
1820 private class Header implements Serializable {
1821 private static final long serialVersionUID = 396191633848670929L;
1822
1823 private int min_;
1824 private int max_;
1825 private int preferred_;
1826 private int actual_;
1827
1828 private int start_;
1829 private boolean hasComponents_;
1830 private boolean isExpandable_;
1831
1832
1833
1834 /***
1835 * Sets the actual attribute of the Header object
1836 *
1837 * @param actual The new actual value
1838 */
1839 public final void setActual( int actual ) {
1840 actual_ = actual;
1841 }
1842
1843
1844 /***
1845 * Sets the preferred attribute of the Header object
1846 *
1847 * @param preferred The new preferred value
1848 */
1849 public final void setPreferred( int preferred ) {
1850 preferred_ = preferred;
1851 }
1852
1853
1854 /***
1855 * Sets the min attribute of the Header object
1856 *
1857 * @param min The new min value
1858 */
1859 public final void setMin( int min ) {
1860 min_ = min;
1861 }
1862
1863
1864 /***
1865 * Sets the max attribute of the Header object
1866 *
1867 * @param max The new max value
1868 */
1869 public final void setMax( int max ) {
1870 max_ = max;
1871 }
1872
1873
1874 /***
1875 * Sets the start attribute of the Header object
1876 *
1877 * @param start The new start value
1878 */
1879 public final void setStart( int start ) {
1880 start_ = start;
1881 }
1882
1883
1884 /***
1885 * Sets the hasComponents attribute of the Header object
1886 *
1887 * @param hasComponents The new hasComponents value
1888 */
1889 public final void setHasComponents( boolean hasComponents ) {
1890 hasComponents_ = hasComponents;
1891 }
1892
1893
1894 /***
1895 * Sets the expandable attribute of the Header object
1896 *
1897 * @param expandable The new expandable value
1898 */
1899 public final void setExpandable( boolean expandable ) {
1900 isExpandable_ = expandable;
1901 }
1902
1903
1904 /***
1905 * Gets the actual attribute of the Header object
1906 *
1907 * @return The actual value
1908 */
1909 public final int getActual() {
1910 return actual_;
1911 }
1912
1913
1914 /***
1915 * Gets the preferred attribute of the Header object
1916 *
1917 * @return The preferred value
1918 */
1919 public final int getPreferred() {
1920 return preferred_;
1921 }
1922
1923
1924 /***
1925 * Gets the min attribute of the Header object
1926 *
1927 * @return The min value
1928 */
1929 public final int getMin() {
1930 return min_;
1931 }
1932
1933
1934 /***
1935 * Gets the max attribute of the Header object
1936 *
1937 * @return The max value
1938 */
1939 public final int getMax() {
1940 return max_;
1941 }
1942
1943
1944 /***
1945 * Gets the start attribute of the Header object
1946 *
1947 * @return The start value
1948 */
1949 public final int getStart() {
1950 return start_;
1951 }
1952
1953
1954 /***
1955 * Gets the hasComponents attribute of the Header object
1956 *
1957 * @return The hasComponents value
1958 */
1959 public final boolean getHasComponents() {
1960 return hasComponents_;
1961 }
1962
1963
1964 /***
1965 * Gets the expandable attribute of the Header object
1966 *
1967 * @return The expandable value
1968 */
1969 public final boolean isExpandable() {
1970 return isExpandable_;
1971 }
1972
1973
1974 /***
1975 * Description of the Method
1976 *
1977 * @return Description of the Returned Value
1978 */
1979 public String toString() {
1980 final StringBuffer buffer = new StringBuffer();
1981 buffer.append( "TableLayout.Header[" );
1982 buffer.append( " min=[" );
1983 buffer.append( getMin() );
1984 buffer.append( "] max=[" );
1985 final int max = getMax();
1986 if( max == Integer.MAX_VALUE ) {
1987 buffer.append( "max_int" );
1988 }
1989 else {
1990 buffer.append( max );
1991 }
1992 buffer.append( "] preferred=[" );
1993 buffer.append( getPreferred() );
1994 buffer.append( "] start=[" );
1995 buffer.append( getStart() );
1996 buffer.append( "] actual=[" );
1997 buffer.append( getActual() );
1998 buffer.append( "] hasComponents=[" );
1999 buffer.append( getHasComponents() );
2000 buffer.append( "] isExpandable=[" );
2001 buffer.append( isExpandable() );
2002 buffer.append( "]]" );
2003
2004 return buffer.toString();
2005 }
2006 }
2007
2008
2009 /***
2010 * Contains the information that the user has specified for the specific
2011 * row or column.
2012 */
2013
2014 private class HeaderPermanentInfo implements Serializable {
2015 private static final long serialVersionUID = 396191633848670929L;
2016 private final int index_;
2017
2018 private int min_;
2019 private boolean isExpandable_;
2020
2021
2022 /***
2023 * Constructor for the HeaderPermanentInfo object
2024 *
2025 * @param newIndex Description of Parameter
2026 */
2027 public HeaderPermanentInfo( final int newIndex ) {
2028 if( newIndex < 0 ) {
2029 throw new DetailedIllegalArgumentException( "newIndex", newIndex, "May not be negative" );
2030 }
2031 index_ = newIndex;
2032 min_ = 0;
2033 isExpandable_ = false;
2034 }
2035
2036
2037 /***
2038 * Sets the min attribute of the HeaderPermanentInfo object
2039 *
2040 * @param min The new min value
2041 */
2042 public final void setMin( final int min ) {
2043 min_ = min;
2044 }
2045
2046
2047 /***
2048 * Sets the expandable attribute of the HeaderPermanentInfo object
2049 *
2050 * @param expandable The new expandable value
2051 */
2052 public final void setExpandable( final boolean expandable ) {
2053 isExpandable_ = expandable;
2054 }
2055
2056
2057 /***
2058 * Gets the min attribute of the HeaderPermanentInfo object
2059 *
2060 * @return The min value
2061 */
2062 public final int getMin() {
2063 return min_;
2064 }
2065
2066
2067 /***
2068 * Gets the index attribute of the HeaderPermanentInfo object
2069 *
2070 * @return The index value
2071 */
2072 public final int getIndex() {
2073 return index_;
2074 }
2075
2076
2077 /***
2078 * Gets the expandable attribute of the HeaderPermanentInfo object
2079 *
2080 * @return The expandable value
2081 */
2082 public final boolean isExpandable() {
2083 return isExpandable_;
2084 }
2085 }
2086
2087
2088 /***
2089 * Verify that the specified value is not null. If it is then throw an exception
2090 *
2091 * @param fieldName The name of the field to check
2092 * @param fieldValue The value of the field to check
2093 * @exception DetailedNullPointerException If fieldValue is null
2094 */
2095 protected final void assertNotNull( final String fieldName, final Object fieldValue )
2096 throws DetailedNullPointerException {
2097
2098 if( fieldValue == null ) {
2099 throw new DetailedNullPointerException(fieldName);
2100 }
2101 }
2102 }
2103
2104