2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 2010-2015 Martin
19 package com
.googlecode
.lanterna
.gui2
;
21 import com
.googlecode
.lanterna
.TerminalPosition
;
22 import com
.googlecode
.lanterna
.TerminalSize
;
27 * This emulates the behaviour of the GridLayout in SWT (as opposed to the one in AWT/Swing). I originally ported the
28 * SWT class itself but due to licensing concerns (the eclipse license is not compatible with LGPL) I was advised not to
29 * do that. This is a partial implementation and some of the semantics have changed, but in general it works the same
30 * way so the SWT documentation will generally match.
32 * You use the {@code GridLayout} by specifying a number of columns you want your grid to have and then when you add
33 * components, you assign {@code LayoutData} to these components using the different static methods in this class
34 * ({@code createLayoutData(..)}). You can set components to span both rows and columns, as well as defining how to
35 * distribute the available space.
37 public class GridLayout
implements LayoutManager
{
39 * The enum is used to specify where in a grid cell a component should be placed, in the case that the preferred
40 * size of the component is smaller than the space in the cell. This class will generally use two alignments, one
41 * for horizontal and one for vertical.
43 public enum Alignment
{
45 * Place the component at the start of the cell (horizontally or vertically) and leave whatever space is left
46 * after the preferred size empty.
50 * Place the component at the middle of the cell (horizontally or vertically) and leave the space before and
55 * Place the component at the end of the cell (horizontally or vertically) and leave whatever space is left
56 * before the preferred size empty.
60 * Force the component to be the same size as the table cell
66 static class GridLayoutData
implements LayoutData
{
67 final Alignment horizontalAlignment
;
68 final Alignment verticalAlignment
;
69 final boolean grabExtraHorizontalSpace
;
70 final boolean grabExtraVerticalSpace
;
71 final int horizontalSpan
;
72 final int verticalSpan
;
74 private GridLayoutData(
75 Alignment horizontalAlignment
,
76 Alignment verticalAlignment
,
77 boolean grabExtraHorizontalSpace
,
78 boolean grabExtraVerticalSpace
,
82 if(horizontalSpan
< 1 || verticalSpan
< 1) {
83 throw new IllegalArgumentException("Horizontal/Vertical span must be 1 or greater");
86 this.horizontalAlignment
= horizontalAlignment
;
87 this.verticalAlignment
= verticalAlignment
;
88 this.grabExtraHorizontalSpace
= grabExtraHorizontalSpace
;
89 this.grabExtraVerticalSpace
= grabExtraVerticalSpace
;
90 this.horizontalSpan
= horizontalSpan
;
91 this.verticalSpan
= verticalSpan
;
95 private static GridLayoutData DEFAULT
= new GridLayoutData(
104 * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
105 * component in case the cell space is larger than the preferred size of the component
106 * @param horizontalAlignment Horizontal alignment strategy
107 * @param verticalAlignment Vertical alignment strategy
108 * @return The layout data object containing the specified alignments
110 public static LayoutData
createLayoutData(Alignment horizontalAlignment
, Alignment verticalAlignment
) {
111 return createLayoutData(horizontalAlignment
, verticalAlignment
, false, false);
115 * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
116 * component in case the cell space is larger than the preferred size of the component. This method also has fields
117 * for indicating that the component would like to take more space if available to the container. For example, if
118 * the container is assigned is assigned an area of 50x15, but all the child components in the grid together only
119 * asks for 40x10, the remaining 10 columns and 5 rows will be empty. If just a single component asks for extra
120 * space horizontally and/or vertically, the grid will expand out to fill the entire area and the text space will be
121 * assigned to the component that asked for it.
123 * @param horizontalAlignment Horizontal alignment strategy
124 * @param verticalAlignment Vertical alignment strategy
125 * @param grabExtraHorizontalSpace If set to {@code true}, this component will ask to be assigned extra horizontal
126 * space if there is any to assign
127 * @param grabExtraVerticalSpace If set to {@code true}, this component will ask to be assigned extra vertical
128 * space if there is any to assign
129 * @return The layout data object containing the specified alignments and size requirements
131 public static LayoutData
createLayoutData(
132 Alignment horizontalAlignment
,
133 Alignment verticalAlignment
,
134 boolean grabExtraHorizontalSpace
,
135 boolean grabExtraVerticalSpace
) {
137 return createLayoutData(horizontalAlignment
, verticalAlignment
, grabExtraHorizontalSpace
, grabExtraVerticalSpace
, 1, 1);
141 * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
142 * component in case the cell space is larger than the preferred size of the component. This method also has fields
143 * for indicating that the component would like to take more space if available to the container. For example, if
144 * the container is assigned is assigned an area of 50x15, but all the child components in the grid together only
145 * asks for 40x10, the remaining 10 columns and 5 rows will be empty. If just a single component asks for extra
146 * space horizontally and/or vertically, the grid will expand out to fill the entire area and the text space will be
147 * assigned to the component that asked for it. It also puts in data on how many rows and/or columns the component
150 * @param horizontalAlignment Horizontal alignment strategy
151 * @param verticalAlignment Vertical alignment strategy
152 * @param grabExtraHorizontalSpace If set to {@code true}, this component will ask to be assigned extra horizontal
153 * space if there is any to assign
154 * @param grabExtraVerticalSpace If set to {@code true}, this component will ask to be assigned extra vertical
155 * space if there is any to assign
156 * @param horizontalSpan How many "cells" this component wants to span horizontally
157 * @param verticalSpan How many "cells" this component wants to span vertically
158 * @return The layout data object containing the specified alignments, size requirements and cell spanning
160 public static LayoutData
createLayoutData(
161 Alignment horizontalAlignment
,
162 Alignment verticalAlignment
,
163 boolean grabExtraHorizontalSpace
,
164 boolean grabExtraVerticalSpace
,
168 return new GridLayoutData(
171 grabExtraHorizontalSpace
,
172 grabExtraVerticalSpace
,
178 * This is a shortcut method that will create a grid layout data object that will expand its cell as much as is can
179 * horizontally and make the component occupy the whole area horizontally and center it vertically
180 * @param horizontalSpan How many cells to span horizontally
181 * @return Layout data object with the specified span and horizontally expanding as much as it can
183 public static LayoutData
createHorizontallyFilledLayoutData(int horizontalSpan
) {
184 return createLayoutData(
194 * This is a shortcut method that will create a grid layout data object that will expand its cell as much as is can
195 * vertically and make the component occupy the whole area vertically and center it horizontally
196 * @param horizontalSpan How many cells to span vertically
197 * @return Layout data object with the specified span and vertically expanding as much as it can
199 public static LayoutData
createHorizontallyEndAlignedLayoutData(int horizontalSpan
) {
200 return createLayoutData(
209 private final int numberOfColumns
;
210 private int horizontalSpacing
;
211 private int verticalSpacing
;
212 private int topMarginSize
;
213 private int bottomMarginSize
;
214 private int leftMarginSize
;
215 private int rightMarginSize
;
217 private boolean changed
;
220 * Creates a new {@code GridLayout} with the specified number of columns. Initially, this layout will have a
221 * horizontal spacing of 1 and vertical spacing of 0, with a left and right margin of 1.
222 * @param numberOfColumns Number of columns in this grid
224 public GridLayout(int numberOfColumns
) {
225 this.numberOfColumns
= numberOfColumns
;
226 this.horizontalSpacing
= 1;
227 this.verticalSpacing
= 0;
228 this.topMarginSize
= 0;
229 this.bottomMarginSize
= 0;
230 this.leftMarginSize
= 1;
231 this.rightMarginSize
= 1;
236 * Returns the horizontal spacing, i.e. the number of empty columns between each cell
237 * @return Horizontal spacing
239 public int getHorizontalSpacing() {
240 return horizontalSpacing
;
244 * Sets the horizontal spacing, i.e. the number of empty columns between each cell
245 * @param horizontalSpacing New horizontal spacing
248 public GridLayout
setHorizontalSpacing(int horizontalSpacing
) {
249 if(horizontalSpacing
< 0) {
250 throw new IllegalArgumentException("Horizontal spacing cannot be less than 0");
252 this.horizontalSpacing
= horizontalSpacing
;
258 * Returns the vertical spacing, i.e. the number of empty columns between each row
259 * @return Vertical spacing
261 public int getVerticalSpacing() {
262 return verticalSpacing
;
266 * Sets the vertical spacing, i.e. the number of empty columns between each row
267 * @param verticalSpacing New vertical spacing
270 public GridLayout
setVerticalSpacing(int verticalSpacing
) {
271 if(verticalSpacing
< 0) {
272 throw new IllegalArgumentException("Vertical spacing cannot be less than 0");
274 this.verticalSpacing
= verticalSpacing
;
280 * Returns the top margin, i.e. number of empty rows above the first row in the grid
281 * @return Top margin, in number of rows
283 public int getTopMarginSize() {
284 return topMarginSize
;
288 * Sets the top margin, i.e. number of empty rows above the first row in the grid
289 * @param topMarginSize Top margin, in number of rows
292 public GridLayout
setTopMarginSize(int topMarginSize
) {
293 if(topMarginSize
< 0) {
294 throw new IllegalArgumentException("Top margin size cannot be less than 0");
296 this.topMarginSize
= topMarginSize
;
302 * Returns the bottom margin, i.e. number of empty rows below the last row in the grid
303 * @return Bottom margin, in number of rows
305 public int getBottomMarginSize() {
306 return bottomMarginSize
;
310 * Sets the bottom margin, i.e. number of empty rows below the last row in the grid
311 * @param bottomMarginSize Bottom margin, in number of rows
314 public GridLayout
setBottomMarginSize(int bottomMarginSize
) {
315 if(bottomMarginSize
< 0) {
316 throw new IllegalArgumentException("Bottom margin size cannot be less than 0");
318 this.bottomMarginSize
= bottomMarginSize
;
324 * Returns the left margin, i.e. number of empty columns left of the first column in the grid
325 * @return Left margin, in number of columns
327 public int getLeftMarginSize() {
328 return leftMarginSize
;
332 * Sets the left margin, i.e. number of empty columns left of the first column in the grid
333 * @param leftMarginSize Left margin, in number of columns
336 public GridLayout
setLeftMarginSize(int leftMarginSize
) {
337 if(leftMarginSize
< 0) {
338 throw new IllegalArgumentException("Left margin size cannot be less than 0");
340 this.leftMarginSize
= leftMarginSize
;
346 * Returns the right margin, i.e. number of empty columns right of the last column in the grid
347 * @return Right margin, in number of columns
349 public int getRightMarginSize() {
350 return rightMarginSize
;
354 * Sets the right margin, i.e. number of empty columns right of the last column in the grid
355 * @param rightMarginSize Right margin, in number of columns
358 public GridLayout
setRightMarginSize(int rightMarginSize
) {
359 if(rightMarginSize
< 0) {
360 throw new IllegalArgumentException("Right margin size cannot be less than 0");
362 this.rightMarginSize
= rightMarginSize
;
368 public boolean hasChanged() {
373 public TerminalSize
getPreferredSize(List
<Component
> components
) {
374 TerminalSize preferredSize
= TerminalSize
.ZERO
;
375 if(components
.isEmpty()) {
376 return preferredSize
.withRelative(
377 leftMarginSize
+ rightMarginSize
,
378 topMarginSize
+ bottomMarginSize
);
381 Component
[][] table
= buildTable(components
);
382 table
= eliminateUnusedRowsAndColumns(table
);
384 //Figure out each column first, this can be done independently of the row heights
385 int preferredWidth
= 0;
386 int preferredHeight
= 0;
387 for(int width
: getPreferredColumnWidths(table
)) {
388 preferredWidth
+= width
;
390 for(int height
: getPreferredRowHeights(table
)) {
391 preferredHeight
+= height
;
393 preferredSize
= preferredSize
.withRelative(preferredWidth
, preferredHeight
);
394 preferredSize
= preferredSize
.withRelativeColumns(leftMarginSize
+ rightMarginSize
+ (table
[0].length
- 1) * horizontalSpacing
);
395 preferredSize
= preferredSize
.withRelativeRows(topMarginSize
+ bottomMarginSize
+ (table
.length
- 1) * verticalSpacing
);
396 return preferredSize
;
400 public void doLayout(TerminalSize area
, List
<Component
> components
) {
401 //Sanity check, if the area is way too small, just return
402 Component
[][] table
= buildTable(components
);
403 table
= eliminateUnusedRowsAndColumns(table
);
405 if(area
.equals(TerminalSize
.ZERO
) ||
407 area
.getColumns() <= leftMarginSize
+ rightMarginSize
+ ((table
[0].length
- 1) * horizontalSpacing
) ||
408 area
.getRows() <= bottomMarginSize
+ topMarginSize
+ ((table
.length
- 1) * verticalSpacing
)) {
412 //Adjust area to the margins
413 area
= area
.withRelative(-leftMarginSize
- rightMarginSize
, -topMarginSize
- bottomMarginSize
);
415 Map
<Component
, TerminalSize
> sizeMap
= new IdentityHashMap
<Component
, TerminalSize
>();
416 Map
<Component
, TerminalPosition
> positionMap
= new IdentityHashMap
<Component
, TerminalPosition
>();
418 //Figure out each column first, this can be done independently of the row heights
419 int[] columnWidths
= getPreferredColumnWidths(table
);
421 //Take notes of which columns we can expand if the usable area is larger than what the components want
422 Set
<Integer
> expandableColumns
= getExpandableColumns(table
);
424 //Next, start shrinking to make sure it fits the size of the area we are trying to lay out on.
425 //Notice we subtract the horizontalSpacing to take the space between components into account
426 TerminalSize areaWithoutHorizontalSpacing
= area
.withRelativeColumns(-horizontalSpacing
* (table
[0].length
- 1));
427 int totalWidth
= shrinkWidthToFitArea(areaWithoutHorizontalSpacing
, columnWidths
);
429 //Finally, if there is extra space, make the expandable columns larger
430 while(areaWithoutHorizontalSpacing
.getColumns() > totalWidth
&& !expandableColumns
.isEmpty()) {
431 totalWidth
= grabExtraHorizontalSpace(areaWithoutHorizontalSpacing
, columnWidths
, expandableColumns
, totalWidth
);
434 //Now repeat for rows
435 int[] rowHeights
= getPreferredRowHeights(table
);
436 Set
<Integer
> expandableRows
= getExpandableRows(table
);
437 TerminalSize areaWithoutVerticalSpacing
= area
.withRelativeRows(-verticalSpacing
* (table
.length
- 1));
438 int totalHeight
= shrinkHeightToFitArea(areaWithoutVerticalSpacing
, rowHeights
);
439 while(areaWithoutVerticalSpacing
.getRows() > totalHeight
&& !expandableRows
.isEmpty()) {
440 totalHeight
= grabExtraVerticalSpace(areaWithoutVerticalSpacing
, rowHeights
, expandableRows
, totalHeight
);
443 //Ok, all constraints are in place, we can start placing out components. To simplify, do it horizontally first
444 //and vertically after
445 TerminalPosition tableCellTopLeft
= TerminalPosition
.TOP_LEFT_CORNER
;
446 for(int y
= 0; y
< table
.length
; y
++) {
447 tableCellTopLeft
= tableCellTopLeft
.withColumn(0);
448 for(int x
= 0; x
< table
[y
].length
; x
++) {
449 Component component
= table
[y
][x
];
450 if(component
!= null && !positionMap
.containsKey(component
)) {
451 GridLayoutData layoutData
= getLayoutData(component
);
452 TerminalSize size
= component
.getPreferredSize();
453 TerminalPosition position
= tableCellTopLeft
;
455 int availableHorizontalSpace
= 0;
456 int availableVerticalSpace
= 0;
457 for (int i
= 0; i
< layoutData
.horizontalSpan
; i
++) {
458 availableHorizontalSpace
+= columnWidths
[x
+ i
] + (i
> 0 ? horizontalSpacing
: 0);
460 for (int i
= 0; i
< layoutData
.verticalSpan
; i
++) {
461 availableVerticalSpace
+= rowHeights
[y
+ i
] + (i
> 0 ? verticalSpacing
: 0);
464 //Make sure to obey the size restrictions
465 size
= size
.withColumns(Math
.min(size
.getColumns(), availableHorizontalSpace
));
466 size
= size
.withRows(Math
.min(size
.getRows(), availableVerticalSpace
));
468 switch (layoutData
.horizontalAlignment
) {
470 position
= position
.withRelativeColumn((availableHorizontalSpace
- size
.getColumns()) / 2);
473 position
= position
.withRelativeColumn(availableHorizontalSpace
- size
.getColumns());
476 size
= size
.withColumns(availableHorizontalSpace
);
481 switch (layoutData
.verticalAlignment
) {
483 position
= position
.withRelativeRow((availableVerticalSpace
- size
.getRows()) / 2);
486 position
= position
.withRelativeRow(availableVerticalSpace
- size
.getRows());
489 size
= size
.withRows(availableVerticalSpace
);
495 sizeMap
.put(component
, size
);
496 positionMap
.put(component
, position
);
498 tableCellTopLeft
= tableCellTopLeft
.withRelativeColumn(columnWidths
[x
] + horizontalSpacing
);
500 tableCellTopLeft
= tableCellTopLeft
.withRelativeRow(rowHeights
[y
] + verticalSpacing
);
503 //Apply the margins here
504 for(Component component
: components
) {
505 component
.setPosition(positionMap
.get(component
).withRelative(leftMarginSize
, topMarginSize
));
506 component
.setSize(sizeMap
.get(component
));
508 this.changed
= false;
511 private int[] getPreferredColumnWidths(Component
[][] table
) {
512 //actualNumberOfColumns may be different from this.numberOfColumns since some columns may have been eliminated
513 int actualNumberOfColumns
= table
[0].length
;
514 int columnWidths
[] = new int[actualNumberOfColumns
];
516 //Start by letting all span = 1 columns take what they need
517 for(Component
[] row
: table
) {
518 for(int i
= 0; i
< actualNumberOfColumns
; i
++) {
519 Component component
= row
[i
];
520 if(component
== null) {
523 GridLayoutData layoutData
= getLayoutData(component
);
524 if (layoutData
.horizontalSpan
== 1) {
525 columnWidths
[i
] = Math
.max(columnWidths
[i
], component
.getPreferredSize().getColumns());
530 //Next, do span > 1 and enlarge if necessary
531 for(Component
[] row
: table
) {
532 for(int i
= 0; i
< actualNumberOfColumns
; ) {
533 Component component
= row
[i
];
534 if(component
== null) {
538 GridLayoutData layoutData
= getLayoutData(component
);
539 if(layoutData
.horizontalSpan
> 1) {
541 for(int j
= i
; j
< i
+ layoutData
.horizontalSpan
; j
++) {
542 accumWidth
+= columnWidths
[j
];
545 int preferredWidth
= component
.getPreferredSize().getColumns();
546 if(preferredWidth
> accumWidth
) {
547 int columnOffset
= 0;
549 columnWidths
[i
+ columnOffset
++]++;
551 if(columnOffset
== layoutData
.horizontalSpan
) {
555 while(preferredWidth
> accumWidth
);
558 i
+= layoutData
.horizontalSpan
;
564 private int[] getPreferredRowHeights(Component
[][] table
) {
565 int numberOfRows
= table
.length
;
566 int rowHeights
[] = new int[numberOfRows
];
568 //Start by letting all span = 1 rows take what they need
570 for(Component
[] row
: table
) {
571 for(int i
= 0; i
< row
.length
; i
++) {
572 Component component
= row
[i
];
573 if(component
== null) {
576 GridLayoutData layoutData
= getLayoutData(component
);
577 if(layoutData
.verticalSpan
== 1) {
578 rowHeights
[rowIndex
] = Math
.max(rowHeights
[rowIndex
], component
.getPreferredSize().getRows());
584 //Next, do span > 1 and enlarge if necessary
585 for(int x
= 0; x
< numberOfColumns
; x
++) {
586 for(int y
= 0; y
< numberOfRows
&& y
< table
.length
; ) {
587 if(x
>= table
[y
].length
) {
591 Component component
= table
[y
][x
];
592 if(component
== null) {
596 GridLayoutData layoutData
= getLayoutData(component
);
597 if(layoutData
.verticalSpan
> 1) {
598 int accumulatedHeight
= 0;
599 for(int i
= y
; i
< y
+ layoutData
.verticalSpan
; i
++) {
600 accumulatedHeight
+= rowHeights
[i
];
603 int preferredHeight
= component
.getPreferredSize().getRows();
604 if(preferredHeight
> accumulatedHeight
) {
607 rowHeights
[y
+ rowOffset
++]++;
609 if(rowOffset
== layoutData
.verticalSpan
) {
613 while(preferredHeight
> accumulatedHeight
);
616 y
+= layoutData
.verticalSpan
;
622 private Set
<Integer
> getExpandableColumns(Component
[][] table
) {
623 Set
<Integer
> expandableColumns
= new TreeSet
<Integer
>();
624 for(Component
[] row
: table
) {
625 for (int i
= 0; i
< row
.length
; i
++) {
629 GridLayoutData layoutData
= getLayoutData(row
[i
]);
630 if(layoutData
.grabExtraHorizontalSpace
) {
631 expandableColumns
.add(i
);
635 return expandableColumns
;
638 private Set
<Integer
> getExpandableRows(Component
[][] table
) {
639 Set
<Integer
> expandableRows
= new TreeSet
<Integer
>();
640 for(int rowIndex
= 0; rowIndex
< table
.length
; rowIndex
++) {
641 Component
[] row
= table
[rowIndex
];
642 for (int columnIndex
= 0; columnIndex
< row
.length
; columnIndex
++) {
643 if(row
[columnIndex
] == null) {
646 GridLayoutData layoutData
= getLayoutData(row
[columnIndex
]);
647 if(layoutData
.grabExtraVerticalSpace
) {
648 expandableRows
.add(rowIndex
);
652 return expandableRows
;
655 private int shrinkWidthToFitArea(TerminalSize area
, int[] columnWidths
) {
657 for(int width
: columnWidths
) {
660 if(totalWidth
> area
.getColumns()) {
661 int columnOffset
= 0;
663 if(columnWidths
[columnOffset
] > 0) {
664 columnWidths
[columnOffset
]--;
667 if(++columnOffset
== numberOfColumns
) {
671 while(totalWidth
> area
.getColumns());
676 private int shrinkHeightToFitArea(TerminalSize area
, int[] rowHeights
) {
678 for(int height
: rowHeights
) {
679 totalHeight
+= height
;
681 if(totalHeight
> area
.getRows()) {
684 if(rowHeights
[rowOffset
] > 0) {
685 rowHeights
[rowOffset
]--;
688 if(++rowOffset
== rowHeights
.length
) {
692 while(totalHeight
> area
.getRows());
697 private int grabExtraHorizontalSpace(TerminalSize area
, int[] columnWidths
, Set
<Integer
> expandableColumns
, int totalWidth
) {
698 for(int columnIndex
: expandableColumns
) {
699 columnWidths
[columnIndex
]++;
701 if(area
.getColumns() == totalWidth
) {
708 private int grabExtraVerticalSpace(TerminalSize area
, int[] rowHeights
, Set
<Integer
> expandableRows
, int totalHeight
) {
709 for(int rowIndex
: expandableRows
) {
710 rowHeights
[rowIndex
]++;
712 if(area
.getColumns() == totalHeight
) {
719 private Component
[][] buildTable(List
<Component
> components
) {
720 List
<Component
[]> rows
= new ArrayList
<Component
[]>();
721 List
<int[]> hspans
= new ArrayList
<int[]>();
722 List
<int[]> vspans
= new ArrayList
<int[]>();
726 Queue
<Component
> toBePlaced
= new LinkedList
<Component
>(components
);
727 while(!toBePlaced
.isEmpty() || rowCount
< rowsExtent
) {
729 Component
[] row
= new Component
[numberOfColumns
];
730 int[] hspan
= new int[numberOfColumns
];
731 int[] vspan
= new int[numberOfColumns
];
733 for(int i
= 0; i
< numberOfColumns
; i
++) {
734 if(i
> 0 && hspan
[i
- 1] > 1) {
736 hspan
[i
] = hspan
[i
- 1] - 1;
737 vspan
[i
] = vspan
[i
- 1];
739 else if(rowCount
> 0 && vspans
.get(rowCount
- 1)[i
] > 1) {
740 row
[i
] = rows
.get(rowCount
- 1)[i
];
741 hspan
[i
] = hspans
.get(rowCount
- 1)[i
];
742 vspan
[i
] = vspans
.get(rowCount
- 1)[i
] - 1;
744 else if(!toBePlaced
.isEmpty()) {
745 Component component
= toBePlaced
.poll();
746 GridLayoutData gridLayoutData
= getLayoutData(component
);
749 hspan
[i
] = gridLayoutData
.horizontalSpan
;
750 vspan
[i
] = gridLayoutData
.verticalSpan
;
751 rowsExtent
= Math
.max(rowsExtent
, rowCount
+ gridLayoutData
.verticalSpan
);
765 return rows
.toArray(new Component
[rows
.size()][]);
768 private Component
[][] eliminateUnusedRowsAndColumns(Component
[][] table
) {
769 if(table
.length
== 0) {
772 //Could make this into a Set, but I doubt there will be any real gain in performance as these are probably going
774 List
<Integer
> rowsToRemove
= new ArrayList
<Integer
>();
775 List
<Integer
> columnsToRemove
= new ArrayList
<Integer
>();
777 final int tableRows
= table
.length
;
778 final int tableColumns
= table
[0].length
;
780 //Scan for unnecessary columns
782 for(int column
= tableColumns
- 1; column
> 0; column
--) {
783 for(int row
= 0; row
< tableRows
; row
++) {
784 if(table
[row
][column
] != table
[row
][column
- 1]) {
788 columnsToRemove
.add(column
);
791 //Scan for unnecessary rows
793 for(int row
= tableRows
- 1; row
> 0; row
--) {
794 for(int column
= 0; column
< tableColumns
; column
++) {
795 if(table
[row
][column
] != table
[row
- 1][column
]) {
799 rowsToRemove
.add(row
);
802 //If there's nothing to remove, just return the same
803 if(rowsToRemove
.isEmpty() && columnsToRemove
.isEmpty()) {
807 //Build a new table with rows & columns eliminated
808 Component
[][] newTable
= new Component
[tableRows
- rowsToRemove
.size()][];
809 int insertedRowCounter
= 0;
810 for(int row
= 0; row
< tableRows
; row
++) {
811 Component
[] newColumn
= new Component
[tableColumns
- columnsToRemove
.size()];
812 int insertedColumnCounter
= 0;
813 for(int column
= 0; column
< tableColumns
; column
++) {
814 if(columnsToRemove
.contains(column
)) {
817 newColumn
[insertedColumnCounter
++] = table
[row
][column
];
819 newTable
[insertedRowCounter
++] = newColumn
;
824 private GridLayoutData
getLayoutData(Component component
) {
825 LayoutData layoutData
= component
.getLayoutData();
826 if(layoutData
== null || !(layoutData
instanceof GridLayoutData
)) {
830 return (GridLayoutData
)layoutData
;