int[] relativeWeights = new int[columnCount];
boolean[] defaultWidthColumns = new boolean[columnCount];
int totalRelativeWeight = 0;
BoxModel boxModel = WidgetHelper.getBoxModel(this);
// First, we calculate the base widths of the columns, giving relative
// columns their preferred width
for (int i = 0; i < columnCount; i++) {
Grid.Column column = columns.get(i);
boolean isRelative = column.isRelative();
LayoutData layoutData = WidgetHelper.getLayoutData(column);
String widthHint = layoutData.getPreferredWidth();
defaultWidthColumns[i] = (widthHint == null);
if (isRelative) {
relativeWeights[i] = column.getWeight();
totalRelativeWeight += relativeWeights[i];
}
int columnWidth;
if (widthHint == null || isRelative) {
columnWidth = getPreferredColumnWidth(i);
} else {
columnWidth = WidgetHelper.getPreferredWidth(column);
}
columnWidths[i] = columnWidth;
}
// Next, we adjust the widths of the relative columns upwards where
// necessary to reconcile their widths relative to one another while
// ensuring that they still get at least their preferred width
if (totalRelativeWeight > 0) {
int totalRelativeWidth = 0;
// Calculate the total relative width after the required upward
// adjustments
for (int i = 0; i < columnCount; i++) {
int columnWidth = columnWidths[i];
int relativeWeight = relativeWeights[i];
if (relativeWeight > 0) {
float weightPercentage = relativeWeight
/ (float) totalRelativeWeight;
totalRelativeWidth = Math.max(totalRelativeWidth,
(int) (columnWidth / weightPercentage));
}
}
// Perform the upward adjustments using the total relative width
for (int i = 0; i < columnCount; i++) {
int relativeWeight = relativeWeights[i];
if (relativeWeight > 0) {
float weightPercentage = relativeWeight
/ (float) totalRelativeWeight;
columnWidths[i] = (int) (weightPercentage * totalRelativeWidth);
}
}
}
// Finally, we account for spanning cells, which have been ignored thus
// far. If any spanned cell is default-width (including relative width
// columns), then we ensure that the sum of the widths of the spanned
// cells is enough to satisfy the preferred width of the spanning
// content
for (int i = 0; i < rowCount; i++) {
Grid.Row row = rows.get(i);
for (int j = 0, n = row.getWidgetCount(); j < n && j < columnCount; j++) {
Widget widget = row.getWidget(j);
if (widget != null && widget.isVisible()) {
int columnSpan = WidgetHelper.getColumnSpan(widget);
if (columnSpan > 1) {
// We might need to adjust column widths to accommodate
// this spanning cell. First, we find out if any of the
// spanned cells are default width and how much space
// we've allocated thus far for those cells
int spannedDefaultWidthCellCount = 0;
int spannedRelativeWeight = 0;
int spannedWidth = 0;
for (int k = 0; k < columnSpan && j + k < columnCount; k++) {
if (defaultWidthColumns[j + k]) {
spannedDefaultWidthCellCount++;
}
spannedRelativeWeight += relativeWeights[j + k];
spannedWidth += columnWidths[j + k];
}
if (spannedRelativeWeight > 0
|| spannedDefaultWidthCellCount > 0) {
int rowHeight = row.isRelative() ? -1
: WidgetHelper.getPreferredHeight(row);
int widgetPreferredWidth = WidgetHelper
.getPreferredWidth(widget, rowHeight);
if (widgetPreferredWidth > spannedWidth) {
// The widget's preferred width is larger than
// the width we've allocated thus far, so an
// adjustment is necessary
int adjustment = widgetPreferredWidth
- spannedWidth;
if (spannedRelativeWeight > 0) {
// We'll distribute the adjustment across
// the spanned relative columns and adjust
// other relative column widths to keep all
// relative column widths reconciled
float unitAdjustment = adjustment
/ (float) spannedRelativeWeight;
for (int k = 0; k < columnCount; k++) {
int relativeWeight = relativeWeights[k];
if (relativeWeight > 0) {
int columnAdjustment = Math
.round(unitAdjustment
* relativeWeight);
columnWidths[k] += columnAdjustment;
}
}
} else {
// We'll distribute the adjustment evenly
// among the default-width columns
for (int k = 0; k < columnSpan
&& j + k < columnCount; k++) {
if (defaultWidthColumns[j + k]) {
int columnAdjustment = adjustment
/ spannedDefaultWidthCellCount;
columnWidths[j + k] += columnAdjustment;
// Adjust these to avoid rounding
// errors
adjustment -= columnAdjustment;
spannedDefaultWidthCellCount--;
}
}
}
}
}
}
}
}
}
// The preferred width of the table is the sum of the column widths,
// plus margin+border+padding and spacing
boolean[][] occupiedCells = getOccupiedCells();
int visibleColumnCount = 0;
int preferredWidth = boxModel.getWidthContribution();
for (int j = 0; j < columnCount; j++) {
boolean columnVisible = false;
for (int i = 0; i < rowCount; i++) {