summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Ahlroos <john@vaadin.com>2013-11-06 10:35:03 +0200
committerVaadin Code Review <review@vaadin.com>2013-11-22 12:59:10 +0000
commit4caa2f5b6e26ade52a4fba66a0a020b79f9008ea (patch)
tree4aa4d2b8d4d0566fda4b336d22a1ebbe7ac9d712
parentc2d38fa6c2d67457065fd3dce7e0d939ae0a1278 (diff)
downloadvaadin-framework-4caa2f5b6e26ade52a4fba66a0a020b79f9008ea.tar.gz
vaadin-framework-4caa2f5b6e26ade52a4fba66a0a020b79f9008ea.zip
Multiple headers and footer rows #3153
Change-Id: Iadb0d8b051d0f0ef1303e0d7d740cf476cd81971
-rw-r--r--client/src/com/vaadin/client/ui/grid/ColumnGroup.java117
-rw-r--r--client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java188
-rw-r--r--client/src/com/vaadin/client/ui/grid/Grid.java482
-rw-r--r--client/src/com/vaadin/client/ui/grid/GridConnector.java92
-rw-r--r--server/src/com/vaadin/ui/components/grid/ColumnGroup.java141
-rw-r--r--server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java255
-rw-r--r--server/src/com/vaadin/ui/components/grid/Grid.java134
-rw-r--r--server/src/com/vaadin/ui/components/grid/GridColumn.java15
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java92
-rw-r--r--shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java46
-rw-r--r--shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java45
-rw-r--r--shared/src/com/vaadin/shared/ui/grid/GridColumnState.java6
-rw-r--r--shared/src/com/vaadin/shared/ui/grid/GridState.java12
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java149
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java111
15 files changed, 1679 insertions, 206 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/ColumnGroup.java b/client/src/com/vaadin/client/ui/grid/ColumnGroup.java
new file mode 100644
index 0000000000..c37068def7
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/grid/ColumnGroup.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.ui.grid;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Column groups are used to group columns together for adding common auxiliary
+ * headers and footers. Columns groups are added to {@link ColumnGroupRow
+ * ColumnGroupRows}.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ColumnGroup {
+
+ /**
+ * The text shown in the header
+ */
+ private String header;
+
+ /**
+ * The text shown in the footer
+ */
+ private String footer;
+
+ /**
+ * The columns included in the group when also accounting for subgroup
+ * columns
+ */
+ private final List<GridColumn> columns;
+
+ /**
+ * The grid associated with the column group
+ */
+ private final Grid grid;
+
+ /**
+ * Constructs a new column group
+ */
+ ColumnGroup(Grid grid, Collection<GridColumn> columns) {
+ if (columns == null) {
+ throw new IllegalArgumentException(
+ "columns cannot be null. Pass an empty list instead.");
+ }
+ this.grid = grid;
+ this.columns = Collections.unmodifiableList(new ArrayList<GridColumn>(
+ columns));
+ }
+
+ /**
+ * Gets the header text.
+ *
+ * @return the header text
+ */
+ public String getHeaderCaption() {
+ return header;
+ }
+
+ /**
+ * Sets the text shown in the header.
+ *
+ * @param header
+ * the header to set
+ */
+ public void setHeaderCaption(String header) {
+ this.header = header;
+ grid.refreshHeader();
+ }
+
+ /**
+ * Gets the text shown in the footer.
+ *
+ * @return the text in the footer
+ */
+ public String getFooterCaption() {
+ return footer;
+ }
+
+ /**
+ * Sets the text displayed in the footer.
+ *
+ * @param footer
+ * the footer to set
+ */
+ public void setFooterCaption(String footer) {
+ this.footer = footer;
+ grid.refreshFooter();
+ }
+
+ /**
+ * Returns all column in this group. It includes the subgroups columns as
+ * well.
+ *
+ * @return unmodifiable list of columns
+ */
+ public List<GridColumn> getColumns() {
+ return columns;
+ }
+}
diff --git a/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java b/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java
new file mode 100644
index 0000000000..6bbc9bc9eb
--- /dev/null
+++ b/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.ui.grid;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A column group row represents an auxiliary header or footer row added to the
+ * grid. A column group row includes column groups that group columns together.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ColumnGroupRow {
+
+ /**
+ * The column groups in this row
+ */
+ private List<ColumnGroup> groups = new ArrayList<ColumnGroup>();
+
+ /**
+ * The grid associated with the column row
+ */
+ private final Grid grid;
+
+ /**
+ * Is the header shown
+ */
+ public boolean headerVisible = true;
+
+ /**
+ * Is the footer shown
+ */
+ public boolean footerVisible = false;
+
+ /**
+ * Constructs a new column group row
+ *
+ * @param grid
+ * Grid associated with this column
+ *
+ */
+ ColumnGroupRow(Grid grid) {
+ this.grid = grid;
+ }
+
+ /**
+ * Add a new group to the row by using column instances.
+ *
+ * @param columns
+ * The columns that should belong to the group
+ * @return a column group representing the collection of columns added to
+ * the group.
+ */
+ public ColumnGroup addGroup(GridColumn... columns) {
+
+ for (GridColumn column : columns) {
+ if (isColumnGrouped(column)) {
+ throw new IllegalArgumentException("Column "
+ + String.valueOf(column.getHeaderCaption())
+ + " already belongs to another group.");
+ }
+ }
+
+ ColumnGroup group = new ColumnGroup(grid, Arrays.asList(columns));
+ groups.add(group);
+ grid.refreshHeader();
+ grid.refreshFooter();
+ return group;
+ }
+
+ /**
+ * Add a new group to the row by using other already greated groups
+ *
+ * @param groups
+ * The subgroups of the group.
+ * @return a column group representing the collection of columns added to
+ * the group.
+ *
+ */
+ public ColumnGroup addGroup(ColumnGroup... groups) {
+ assert groups != null : "groups cannot be null";
+
+ Set<GridColumn> columns = new HashSet<GridColumn>();
+ for (ColumnGroup group : groups) {
+ columns.addAll(group.getColumns());
+ }
+
+ ColumnGroup group = new ColumnGroup(grid, columns);
+ this.groups.add(group);
+ grid.refreshHeader();
+ grid.refreshFooter();
+ return group;
+ }
+
+ /**
+ * Removes a group from the row.
+ *
+ * @param group
+ * The group to remove
+ */
+ public void removeGroup(ColumnGroup group) {
+ groups.remove(group);
+ grid.refreshHeader();
+ grid.refreshFooter();
+ }
+
+ /**
+ * Get the groups in the row
+ *
+ * @return unmodifiable list of groups in this row
+ */
+ public List<ColumnGroup> getGroups() {
+ return Collections.unmodifiableList(groups);
+ }
+
+ /**
+ * Is the header visible for the row.
+ *
+ * @return <code>true</code> if header is visible
+ */
+ public boolean isHeaderVisible() {
+ return headerVisible;
+ }
+
+ /**
+ * Sets the header visible for the row.
+ *
+ * @param visible
+ * should the header be shown
+ */
+ public void setHeaderVisible(boolean visible) {
+ headerVisible = visible;
+ grid.refreshHeader();
+ }
+
+ /**
+ * Is the footer visible for the row.
+ *
+ * @return <code>true</code> if footer is visible
+ */
+ public boolean isFooterVisible() {
+ return footerVisible;
+ }
+
+ /**
+ * Sets the footer visible for the row.
+ *
+ * @param visible
+ * should the footer be shown
+ */
+ public void setFooterVisible(boolean visible) {
+ footerVisible = visible;
+ grid.refreshFooter();
+ }
+
+ /**
+ * Iterates all the column groups and checks if the columns alread has been
+ * added to a group.
+ */
+ private boolean isColumnGrouped(GridColumn column) {
+ for (ColumnGroup group : groups) {
+ if (group.getColumns().contains(column)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java
index 3c4e2d6e13..67f14301f0 100644
--- a/client/src/com/vaadin/client/ui/grid/Grid.java
+++ b/client/src/com/vaadin/client/ui/grid/Grid.java
@@ -55,7 +55,7 @@ import com.vaadin.shared.util.SharedUtil;
public class Grid<T> extends Composite {
/**
- * Escalator used internally by the grid to render the rows
+ * Escalator used internally by grid to render the rows
*/
private Escalator escalator = GWT.create(Escalator.class);
@@ -65,8 +65,23 @@ public class Grid<T> extends Composite {
private final List<GridColumn<T>> columns = new ArrayList<GridColumn<T>>();
/**
- * Base class for grid columns internally used by the Grid. You should use
- * {@link GridColumn} when creating new columns.
+ * The column groups rows added to the grid
+ */
+ private final List<ColumnGroupRow> columnGroupRows = new ArrayList<ColumnGroupRow>();
+
+ /**
+ * Are the headers for the columns visible
+ */
+ private boolean columnHeadersVisible = false;
+
+ /**
+ * Are the footers for the columns visible
+ */
+ private boolean columnFootersVisible = false;
+
+ /**
+ * Base class for grid columns internally used by the Grid. The user should
+ * use {@link GridColumn} when creating new columns.
*
* @param <T>
* the row type
@@ -74,24 +89,24 @@ public class Grid<T> extends Composite {
public static abstract class AbstractGridColumn<T> {
/**
- * Grid associated with the column
+ * The grid the column is associated with
*/
private Grid<T> grid;
/**
- * Text displayed in the column header
+ * Should the column be visible in the grid
*/
- private String header;
+ private boolean visible;
/**
- * Text displayed in the column footer
+ * The text displayed in the header of the column
*/
- private String footer;
+ private String header;
/**
- * Is the column visible
+ * Text displayed in the column footer
*/
- private boolean visible;
+ private String footer;
/**
* Internally used by the grid to set itself
@@ -125,14 +140,15 @@ public class Grid<T> extends Composite {
* the text displayed in the column header
*/
public void setHeaderCaption(String caption) {
- if (SharedUtil.equals(caption, this.header)) {
+ if (SharedUtil.equals(caption, header)) {
return;
}
- this.header = caption;
+ header = caption;
if (grid != null) {
grid.refreshHeader();
+
}
}
@@ -153,11 +169,11 @@ public class Grid<T> extends Composite {
* the text displayed in the footer of the column
*/
public void setFooterCaption(String caption) {
- if (SharedUtil.equals(caption, this.footer)) {
+ if (SharedUtil.equals(caption, footer)) {
return;
}
- this.footer = caption;
+ footer = caption;
if (grid != null) {
grid.refreshFooter();
@@ -177,7 +193,8 @@ public class Grid<T> extends Composite {
* Sets a column as visible in the grid.
*
* @param visible
- * Set to <code>true</code> to show the column in the grid
+ * <code>true</code> if the column should be displayed in the
+ * grid
*/
public void setVisible(boolean visible) {
if (this.visible == visible) {
@@ -206,8 +223,9 @@ public class Grid<T> extends Composite {
* Returns the text that should be displayed in the cell.
*
* @param row
- * the row object that provides the cell content
- * @return The cell content of the row
+ * The row object that provides the cell content.
+ *
+ * @return The cell content
*/
public abstract String getValue(T row);
@@ -221,6 +239,122 @@ public class Grid<T> extends Composite {
}
/**
+ * Base class for header / footer escalator updater
+ */
+ protected abstract class HeaderFooterEscalatorUpdater implements
+ EscalatorUpdater {
+
+ /**
+ * The row container which contains the header or footer rows
+ */
+ private RowContainer rows;
+
+ /**
+ * Should the index be counted from 0-> or 0<-
+ */
+ private boolean inverted;
+
+ /**
+ * Constructs an updater for updating a header / footer
+ *
+ * @param rows
+ * The row container
+ * @param inverted
+ * Should index counting be inverted
+ */
+ public HeaderFooterEscalatorUpdater(RowContainer rows, boolean inverted) {
+ this.rows = rows;
+ this.inverted = inverted;
+ }
+
+ /**
+ * Gets the header/footer caption value
+ *
+ * @return The value that should be rendered for the column caption
+ */
+ public abstract String getColumnValue(GridColumn column);
+
+ /**
+ * Gets the group caption value
+ *
+ * @param group
+ * The group for with the caption value should be returned
+ * @return The value that should be rendered for the column caption
+ */
+ public abstract String getGroupValue(ColumnGroup group);
+
+ /**
+ * Is the row visible in the header/footer
+ *
+ * @return <code>true</code> if the row should be visible
+ */
+ public abstract boolean isRowVisible(ColumnGroupRow row);
+
+ /**
+ * Should the first row be visible
+ *
+ * @return <code>true</code> if the first row should be visible
+ */
+ public abstract boolean firstRowIsVisible();
+
+ @Override
+ public void updateCells(Row row, List<Cell> cellsToUpdate) {
+
+ int rowIndex;
+ if (inverted) {
+ rowIndex = rows.getRowCount() - row.getRow() - 1;
+ } else {
+ rowIndex = row.getRow();
+ }
+
+ if (firstRowIsVisible() && rowIndex == 0) {
+ // column headers
+ for (Cell cell : cellsToUpdate) {
+ int columnIndex = cell.getColumn();
+ GridColumn column = columns.get(columnIndex);
+ cell.getElement().setInnerText(getColumnValue(column));
+ }
+
+ } else if (columnGroupRows.size() > 0) {
+ // Adjust for headers
+ if (firstRowIsVisible()) {
+ rowIndex--;
+ }
+
+ // Adjust for previous invisible header rows
+ ColumnGroupRow groupRow = null;
+ for (int i = 0, realIndex = 0; i < columnGroupRows.size(); i++) {
+ groupRow = columnGroupRows.get(i);
+ if (isRowVisible(groupRow)) {
+ if (realIndex == rowIndex) {
+ rowIndex = realIndex;
+ break;
+ }
+ realIndex++;
+ }
+ }
+
+ assert groupRow != null;
+
+ for (Cell cell : cellsToUpdate) {
+ int columnIndex = cell.getColumn();
+ GridColumn column = columns.get(columnIndex);
+ ColumnGroup group = getGroupForColumn(groupRow, column);
+
+ if (group != null) {
+ // FIXME Should merge the group cells when escalator
+ // supports it
+ cell.getElement().setInnerText(getGroupValue(group));
+ } else {
+ // Cells are reused
+ cell.getElement().setInnerHTML(null);
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Creates a new instance.
*/
public Grid() {
@@ -229,6 +363,9 @@ public class Grid<T> extends Composite {
escalator.getHeader().setEscalatorUpdater(createHeaderUpdater());
escalator.getBody().setEscalatorUpdater(createBodyUpdater());
escalator.getFooter().setEscalatorUpdater(createFooterUpdater());
+
+ refreshHeader();
+ refreshFooter();
}
/**
@@ -238,18 +375,26 @@ public class Grid<T> extends Composite {
* @return the updater that updates the data in the escalator.
*/
private EscalatorUpdater createHeaderUpdater() {
- return new EscalatorUpdater() {
+ return new HeaderFooterEscalatorUpdater(escalator.getHeader(), true) {
@Override
- public void updateCells(Row row, List<Cell> cellsToUpdate) {
- if (isHeaderVisible()) {
- for (Cell cell : cellsToUpdate) {
- AbstractGridColumn<T> column = columns.get(cell
- .getColumn());
- cell.getElement().setInnerText(
- column.getHeaderCaption());
- }
- }
+ public boolean isRowVisible(ColumnGroupRow row) {
+ return row.isHeaderVisible();
+ }
+
+ @Override
+ public String getGroupValue(ColumnGroup group) {
+ return group.getHeaderCaption();
+ }
+
+ @Override
+ public String getColumnValue(GridColumn column) {
+ return column.getHeaderCaption();
+ }
+
+ @Override
+ public boolean firstRowIsVisible() {
+ return isColumnHeadersVisible();
}
};
}
@@ -275,40 +420,80 @@ public class Grid<T> extends Composite {
* @return the updater that updates the data in the escalator.
*/
private EscalatorUpdater createFooterUpdater() {
- return new EscalatorUpdater() {
+ return new HeaderFooterEscalatorUpdater(escalator.getFooter(), false) {
@Override
- public void updateCells(Row row, List<Cell> cellsToUpdate) {
- if (isFooterVisible()) {
- for (Cell cell : cellsToUpdate) {
- AbstractGridColumn<T> column = columns.get(cell
- .getColumn());
- cell.getElement().setInnerText(
- column.getFooterCaption());
- }
- }
+ public boolean isRowVisible(ColumnGroupRow row) {
+ return row.isFooterVisible();
+ }
+
+ @Override
+ public String getGroupValue(ColumnGroup group) {
+ return group.getFooterCaption();
+ }
+
+ @Override
+ public String getColumnValue(GridColumn column) {
+ return column.getFooterCaption();
+ }
+
+ @Override
+ public boolean firstRowIsVisible() {
+ return isColumnFootersVisible();
}
};
}
/**
- * Refreshes all header rows.
+ * Refreshes header or footer rows on demand
+ *
+ * @param rows
+ * The row container
+ * @param firstRowIsVisible
+ * is the first row visible
+ * @param isHeader
+ * <code>true</code> if we refreshing the header, else assumed
+ * the footer
*/
- private void refreshHeader() {
- RowContainer header = escalator.getHeader();
- if (isHeaderVisible() && header.getRowCount() > 0) {
- header.refreshRows(0, header.getRowCount());
+ private void refreshRowContainer(RowContainer rows,
+ boolean firstRowIsVisible, boolean isHeader) {
+
+ // Count needed rows
+ int totalRows = firstRowIsVisible ? 1 : 0;
+ for (ColumnGroupRow row : columnGroupRows) {
+ if (isHeader ? row.isHeaderVisible() : row.isFooterVisible()) {
+ totalRows++;
+ }
+ }
+
+ // Add or Remove rows on demand
+ int rowDiff = totalRows - rows.getRowCount();
+ if (rowDiff > 0) {
+ rows.insertRows(0, rowDiff);
+ } else if (rowDiff < 0) {
+ rows.removeRows(0, -rowDiff);
+ }
+
+ // Refresh all the rows
+ if (rows.getRowCount() > 0) {
+ rows.refreshRows(0, rows.getRowCount());
}
}
/**
- * Refreshes all footer rows.
+ * Refreshes all header rows
*/
- private void refreshFooter() {
- RowContainer footer = escalator.getFooter();
- if (isFooterVisible() && footer.getRowCount() > 0) {
- footer.refreshRows(0, footer.getRowCount());
- }
+ void refreshHeader() {
+ refreshRowContainer(escalator.getHeader(), isColumnHeadersVisible(),
+ true);
+ }
+
+ /**
+ * Refreshes all footer rows
+ */
+ void refreshFooter() {
+ refreshRowContainer(escalator.getFooter(), isColumnFootersVisible(),
+ false);
}
/**
@@ -388,71 +573,200 @@ public class Grid<T> extends Composite {
* if the column index does not exist in the grid
*/
public GridColumn<T> getColumn(int index) throws IllegalArgumentException {
- try {
- return columns.get(index);
- } catch (ArrayIndexOutOfBoundsException aioobe) {
- throw new IllegalStateException("Column not found.", aioobe);
+ if (index < 0 || index >= columns.size()) {
+ throw new IllegalStateException("Column not found.");
}
+ return columns.get(index);
}
/**
- * Sets the header row visible.
+ * Set the column headers visible.
+ *
+ * <p>
+ * A column header is a single cell header on top of each column reserved
+ * for a specific header for that column. The column header can be set by
+ * {@link GridColumn#setHeaderCaption(String)} and column headers cannot be
+ * merged with other column headers.
+ * </p>
+ *
+ * <p>
+ * All column headers occupy the first header row of the grid. If you do not
+ * wish to show the column headers in the grid you should hide the row by
+ * setting visibility of the header row to <code>false</code>.
+ * </p>
+ *
+ * <p>
+ * If you want to merge the column headers into groups you can use
+ * {@link ColumnGroupRow}s to group columns together and give them a common
+ * header. See {@link #addColumnGroupRow()} for details.
+ * </p>
+ *
+ * <p>
+ * The header row is by default visible.
+ * </p>
*
* @param visible
- * true if header rows should be visible
+ * <code>true</code> if header rows should be visible
*/
- public void setHeaderVisible(boolean visible) {
- if (visible == isHeaderVisible()) {
+ public void setColumnHeadersVisible(boolean visible) {
+ if (visible == isColumnHeadersVisible()) {
return;
}
-
- RowContainer header = escalator.getHeader();
-
- // TODO Should support multiple headers
- if (visible) {
- header.insertRows(0, 1);
- } else {
- header.removeRows(0, 1);
- }
+ columnHeadersVisible = visible;
+ refreshHeader();
}
/**
- * Are the header row(s) visible?
+ * Are the column headers visible
*
- * @return <code>true</code> if the header is visible
+ * @return <code>true</code> if they are visible
*/
- public boolean isHeaderVisible() {
- return escalator.getHeader().getRowCount() > 0;
+ public boolean isColumnHeadersVisible() {
+ return columnHeadersVisible;
}
/**
- * Sets the footer row(s) visible.
+ * Set the column footers visible.
+ *
+ * <p>
+ * A column footer is a single cell footer below of each column reserved for
+ * a specific footer for that column. The column footer can be set by
+ * {@link GridColumn#setFooterCaption(String)} and column footers cannot be
+ * merged with other column footers.
+ * </p>
+ *
+ * <p>
+ * All column footers occupy the first footer row of the grid. If you do not
+ * wish to show the column footers in the grid you should hide the row by
+ * setting visibility of the footer row to <code>false</code>.
+ * </p>
+ *
+ * <p>
+ * If you want to merge the column footers into groups you can use
+ * {@link ColumnGroupRow}s to group columns together and give them a common
+ * footer. See {@link #addColumnGroupRow()} for details.
+ * </p>
+ *
+ * <p>
+ * The footer row is by default hidden.
+ * </p>
*
* @param visible
- * true if header rows should be visible
+ * <code>true</code> if the footer row should be visible
*/
- public void setFooterVisible(boolean visible) {
- if (visible == isFooterVisible()) {
+ public void setColumnFootersVisible(boolean visible) {
+ if (visible == isColumnFootersVisible()) {
return;
}
+ this.columnFootersVisible = visible;
+ refreshFooter();
+ }
- RowContainer footer = escalator.getFooter();
+ /**
+ * Are the column footers visible
+ *
+ * @return <code>true</code> if they are visible
+ *
+ */
+ public boolean isColumnFootersVisible() {
+ return columnFootersVisible;
+ }
- // TODO Should support multiple footers
- if (visible) {
- footer.insertRows(0, 1);
- } else {
- footer.removeRows(0, 1);
- }
+ /**
+ * Adds a new column group row to the grid.
+ *
+ * <p>
+ * Column group rows are rendered in the header and footer of the grid.
+ * Column group rows are made up of column groups which groups together
+ * columns for adding a common auxiliary header or footer for the columns.
+ * </p>
+ *
+ * Example usage:
+ *
+ * <pre>
+ * // Add a new column group row to the grid
+ * ColumnGroupRow row = grid.addColumnGroupRow();
+ *
+ * // Group &quot;Column1&quot; and &quot;Column2&quot; together to form a header in the row
+ * ColumnGroup column12 = row.addGroup(&quot;Column1&quot;, &quot;Column2&quot;);
+ *
+ * // Set a common header for &quot;Column1&quot; and &quot;Column2&quot;
+ * column12.setHeader(&quot;Column 1&amp;2&quot;);
+ *
+ * // Set a common footer for &quot;Column1&quot; and &quot;Column2&quot;
+ * column12.setFooter(&quot;Column 1&amp;2&quot;);
+ * </pre>
+ *
+ * @return a column group row instance you can use to add column groups
+ */
+ public ColumnGroupRow addColumnGroupRow() {
+ ColumnGroupRow row = new ColumnGroupRow(this);
+ columnGroupRows.add(row);
+ refreshHeader();
+ refreshFooter();
+ return row;
+ }
+
+ /**
+ * Adds a new column group row to the grid at a specific index.
+ *
+ * @see #addColumnGroupRow() {@link Grid#addColumnGroupRow()} for example
+ * usage
+ *
+ * @param rowIndex
+ * the index where the column group row should be added
+ * @return a column group row instance you can use to add column groups
+ */
+ public ColumnGroupRow addColumnGroupRow(int rowIndex) {
+ ColumnGroupRow row = new ColumnGroupRow(this);
+ columnGroupRows.add(rowIndex, row);
+ refreshHeader();
+ refreshFooter();
+ return row;
}
/**
- * Are the footer row(s) visible?
+ * Removes a column group row
*
- * @return <code>true</code> if the footer is visible
+ * @param row
+ * The row to remove
*/
- public boolean isFooterVisible() {
- return escalator.getFooter().getRowCount() > 0;
+ public void removeColumnGroupRow(ColumnGroupRow row) {
+ columnGroupRows.remove(row);
+ refreshHeader();
+ refreshFooter();
+ }
+
+ /**
+ * Get the column group rows
+ *
+ * @return a unmodifiable list of column group rows
+ *
+ */
+ public List<ColumnGroupRow> getColumnGroupRows() {
+ return Collections.unmodifiableList(new ArrayList<ColumnGroupRow>(
+ columnGroupRows));
+ }
+
+ /**
+ * Returns the column group for a row and column
+ *
+ * @param row
+ * The row of the column
+ * @param column
+ * the column to get the group for
+ * @return A column group for the row and column or <code>null</code> if not
+ * found.
+ */
+ private static ColumnGroup getGroupForColumn(ColumnGroupRow row,
+ GridColumn column) {
+ for (ColumnGroup group : row.getGroups()) {
+ List<GridColumn> columns = group.getColumns();
+ if (columns.contains(column)) {
+ return group;
+ }
+ }
+ return null;
}
@Override
diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java
index c48c9936bc..32907e1e29 100644
--- a/client/src/com/vaadin/client/ui/grid/GridConnector.java
+++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java
@@ -16,15 +16,19 @@
package com.vaadin.client.ui.grid;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.grid.ColumnGroupRowState;
+import com.vaadin.shared.ui.grid.ColumnGroupState;
import com.vaadin.shared.ui.grid.GridColumnState;
import com.vaadin.shared.ui.grid.GridState;
@@ -38,6 +42,10 @@ import com.vaadin.shared.ui.grid.GridState;
@Connect(com.vaadin.ui.components.grid.Grid.class)
public class GridConnector extends AbstractComponentConnector {
+ /**
+ * Custom implementation of the custom grid column using a String[] to
+ * represent the cell value
+ */
private class CustomGridColumn extends GridColumn<String[]> {
@Override
@@ -47,7 +55,9 @@ public class GridConnector extends AbstractComponentConnector {
}
}
- // Maps a generated column id -> A grid column instance
+ /**
+ * Maps a generated column id to a grid column instance
+ */
private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<String, CustomGridColumn>();
@Override
@@ -71,16 +81,6 @@ public class GridConnector extends AbstractComponentConnector {
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
- // Header
- if (stateChangeEvent.hasPropertyChanged("headerVisible")) {
- getWidget().setHeaderVisible(getState().headerVisible);
- }
-
- // Footer
- if (stateChangeEvent.hasPropertyChanged("footerVisible")) {
- getWidget().setFooterVisible(getState().footerVisible);
- }
-
// Column updates
if (stateChangeEvent.hasPropertyChanged("columns")) {
@@ -92,7 +92,7 @@ public class GridConnector extends AbstractComponentConnector {
// Add new columns
for (int columnIndex = currentColumns; columnIndex < totalColumns; columnIndex++) {
- addColumnFromStateChangeEvent(columnIndex, stateChangeEvent);
+ addColumnFromStateChangeEvent(columnIndex);
}
// Update old columns
@@ -100,9 +100,26 @@ public class GridConnector extends AbstractComponentConnector {
// FIXME Currently updating all column header / footers when a
// change in made in one column. When the framework supports
// quering a specific item in a list then it should do so here.
- updateColumnFromStateChangeEvent(columnIndex, stateChangeEvent);
+ updateColumnFromStateChangeEvent(columnIndex);
}
}
+
+ // Header
+ if (stateChangeEvent.hasPropertyChanged("columnHeadersVisible")) {
+ getWidget()
+ .setColumnHeadersVisible(getState().columnHeadersVisible);
+ }
+
+ // Footer
+ if (stateChangeEvent.hasPropertyChanged("columnFootersVisible")) {
+ getWidget()
+ .setColumnFootersVisible(getState().columnFootersVisible);
+ }
+
+ // Column row groups
+ if (stateChangeEvent.hasPropertyChanged("columnGroupRows")) {
+ updateColumnGroupsFromStateChangeEvent();
+ }
}
/**
@@ -110,12 +127,8 @@ public class GridConnector extends AbstractComponentConnector {
*
* @param columnIndex
* The index of the column to update
- * @param stateChangeEvent
- * The state change event that contains the changes for the
- * column
*/
- private void updateColumnFromStateChangeEvent(int columnIndex,
- StateChangeEvent stateChangeEvent) {
+ private void updateColumnFromStateChangeEvent(int columnIndex) {
GridColumn<String[]> column = getWidget().getColumn(columnIndex);
GridColumnState columnState = getState().columns.get(columnIndex);
updateColumnFromState(column, columnState);
@@ -126,30 +139,30 @@ public class GridConnector extends AbstractComponentConnector {
*
* @param columnIndex
* The index of the column, according to how it
- * @param stateChangeEvent
*/
- private void addColumnFromStateChangeEvent(int columnIndex,
- StateChangeEvent stateChangeEvent) {
+ private void addColumnFromStateChangeEvent(int columnIndex) {
GridColumnState state = getState().columns.get(columnIndex);
CustomGridColumn column = new CustomGridColumn();
updateColumnFromState(column, state);
+
columnIdToColumn.put(state.id, column);
+
getWidget().addColumn(column, columnIndex);
}
/**
- * Updates fields in column from a {@link GridColumnState} DTO
+ * Updates the column values from a state
*
* @param column
* The column to update
* @param state
- * The state to update from
+ * The state to get the data from
*/
private static void updateColumnFromState(GridColumn<String[]> column,
GridColumnState state) {
+ column.setVisible(state.visible);
column.setHeaderCaption(state.header);
column.setFooterCaption(state.footer);
- column.setVisible(state.visible);
}
/**
@@ -176,4 +189,35 @@ public class GridConnector extends AbstractComponentConnector {
}
}
}
+
+ /**
+ * Updates the column groups from a state change
+ */
+ private void updateColumnGroupsFromStateChangeEvent() {
+
+ // FIXME When something changes the header/footer rows will be
+ // re-created. At some point we should optimize this so partial updates
+ // can be made on the header/footer.
+ for (ColumnGroupRow row : getWidget().getColumnGroupRows()) {
+ getWidget().removeColumnGroupRow(row);
+ }
+
+ for (ColumnGroupRowState rowState : getState().columnGroupRows) {
+ ColumnGroupRow row = getWidget().addColumnGroupRow();
+ row.setFooterVisible(rowState.footerVisible);
+ row.setHeaderVisible(rowState.headerVisible);
+
+ for (ColumnGroupState groupState : rowState.groups) {
+ List<GridColumn> columns = new ArrayList<GridColumn>();
+ for (String columnId : groupState.columns) {
+ CustomGridColumn column = columnIdToColumn.get(columnId);
+ columns.add(column);
+ }
+ ColumnGroup group = row.addGroup(columns
+ .toArray(new GridColumn[columns.size()]));
+ group.setFooterCaption(groupState.footer);
+ group.setHeaderCaption(groupState.header);
+ }
+ }
+ }
}
diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java b/server/src/com/vaadin/ui/components/grid/ColumnGroup.java
new file mode 100644
index 0000000000..0ab1f61a46
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/grid/ColumnGroup.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.ui.components.grid;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.vaadin.shared.ui.grid.ColumnGroupState;
+
+/**
+ * Column groups are used to group columns together for adding common auxiliary
+ * headers and footers. Columns groups are added to {@link ColumnGroupRow}'s.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ColumnGroup implements Serializable {
+
+ /**
+ * List of property ids belonging to this group
+ */
+ private List<Object> columns;
+
+ /**
+ * The grid the column group is associated with
+ */
+ private final Grid grid;
+
+ /**
+ * The common state between the server and the client
+ */
+ private final ColumnGroupState state;
+
+ /**
+ * Constructs a new column group
+ *
+ * @param grid
+ * the grid the column group is associated with
+ * @param state
+ * the state representing the data of the grid. Sent to the
+ * client
+ * @param propertyIds
+ * the property ids of the columns that belongs to the group
+ * @param groups
+ * the sub groups who should be included in this group
+ *
+ */
+ ColumnGroup(Grid grid, ColumnGroupState state, List<Object> propertyIds) {
+ if (propertyIds == null) {
+ throw new IllegalArgumentException(
+ "propertyIds cannot be null. Use empty list instead.");
+ }
+
+ this.state = state;
+ columns = Collections.unmodifiableList(new ArrayList<Object>(
+ propertyIds));
+ this.grid = grid;
+ }
+
+ /**
+ * Sets the text displayed in the header of the column group.
+ *
+ * @param header
+ * the text displayed in the header of the column
+ */
+ public void setHeaderCaption(String header) {
+ state.header = header;
+ grid.markAsDirty();
+ }
+
+ /**
+ * Sets the text displayed in the header of the column group.
+ *
+ * @return the text displayed in the header of the column
+ */
+ public String getHeaderCaption() {
+ return state.header;
+ }
+
+ /**
+ * Sets the text displayed in the footer of the column group.
+ *
+ * @param footer
+ * the text displayed in the footer of the column
+ */
+ public void setFooterCaption(String footer) {
+ state.footer = footer;
+ grid.markAsDirty();
+ }
+
+ /**
+ * The text displayed in the footer of the column group.
+ *
+ * @return the text displayed in the footer of the column
+ */
+ public String getFooterCaption() {
+ return state.footer;
+ }
+
+ /**
+ * Is a property id in this group or in some sub group of this group.
+ *
+ * @param propertyId
+ * the property id to check for
+ * @return <code>true</code> if the property id is included in this group.
+ */
+ public boolean isColumnInGroup(Object propertyId) {
+ if (columns.contains(propertyId)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a list of property ids where all also the child groups property
+ * ids are included.
+ *
+ * @return a unmodifiable list with all the columns in the group. Includes
+ * any subgroup columns as well.
+ */
+ public List<Object> getColumns() {
+ return columns;
+ }
+
+}
diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java b/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java
new file mode 100644
index 0000000000..326d2826f5
--- /dev/null
+++ b/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.ui.components.grid;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import com.vaadin.server.KeyMapper;
+import com.vaadin.shared.ui.grid.ColumnGroupRowState;
+import com.vaadin.shared.ui.grid.ColumnGroupState;
+
+/**
+ * A column group row represents an auxiliary header or footer row added to the
+ * grid. A column group row includes column groups that group columns together.
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ColumnGroupRow implements Serializable {
+
+ /**
+ * The common state shared between the client and server
+ */
+ private final ColumnGroupRowState state;
+
+ /**
+ * The column groups in this row
+ */
+ private List<ColumnGroup> groups = new ArrayList<ColumnGroup>();
+
+ /**
+ * Grid that the group row belongs to
+ */
+ private final Grid grid;
+
+ /**
+ * The column keys used to identify the column on the client side
+ */
+ private final KeyMapper<Object> columnKeys;
+
+ /**
+ * Constructs a new column group
+ *
+ * @param grid
+ * The grid that the column group is associated to
+ * @param state
+ * The shared state which contains the data shared between server
+ * and client
+ * @param columnKeys
+ * The column key mapper for converting property ids to client
+ * side column identifiers
+ */
+ ColumnGroupRow(Grid grid, ColumnGroupRowState state,
+ KeyMapper<Object> columnKeys) {
+ this.grid = grid;
+ this.columnKeys = columnKeys;
+ this.state = state;
+ }
+
+ /**
+ * Gets the shared state for the column group row. Used internally to send
+ * the group row to the client.
+ *
+ * @return The current state of the row
+ */
+ ColumnGroupRowState getState() {
+ return state;
+ }
+
+ /**
+ * Add a new group to the row by using property ids for the columns.
+ *
+ * @param propertyIds
+ * The property ids of the columns that should be included in the
+ * group. A column can only belong in group on a row at a time.
+ * @return a column group representing the collection of columns added to
+ * the group
+ */
+ public ColumnGroup addGroup(Object... propertyIds) {
+ assert propertyIds != null : "propertyIds cannot be null.";
+
+ for (Object propertyId : propertyIds) {
+ if (hasColumnBeenGrouped(propertyId)) {
+ throw new IllegalArgumentException("Column "
+ + String.valueOf(propertyId)
+ + " already belongs to another group.");
+ }
+ }
+
+ ColumnGroupState state = new ColumnGroupState();
+ for (Object propertyId : propertyIds) {
+ assert propertyId != null : "null items in columns array not supported.";
+ state.columns.add(columnKeys.key(propertyId));
+ }
+ this.state.groups.add(state);
+
+ ColumnGroup group = new ColumnGroup(grid, state,
+ Arrays.asList(propertyIds));
+ groups.add(group);
+
+ grid.markAsDirty();
+ return group;
+ }
+
+ /**
+ * Add a new group to the row by using column instances.
+ *
+ * @param columns
+ * the columns that should belong to the group
+ * @return a column group representing the collection of columns added to
+ * the group
+ */
+ public ColumnGroup addGroup(GridColumn... columns) {
+ assert columns != null : "columns cannot be null";
+
+ List<Object> propertyIds = new ArrayList<Object>();
+ for (GridColumn column : columns) {
+ assert column != null : "null items in columns array not supported.";
+
+ String columnId = column.getState().id;
+ Object propertyId = grid.getPropertyIdByColumnId(columnId);
+ propertyIds.add(propertyId);
+ }
+ return addGroup(propertyIds.toArray());
+ }
+
+ /**
+ * Add a new group to the row by using other already greated groups
+ *
+ * @param groups
+ * the subgroups of the group
+ * @return a column group representing the collection of columns added to
+ * the group
+ *
+ */
+ public ColumnGroup addGroup(ColumnGroup... groups) {
+ assert groups != null : "groups cannot be null";
+
+ // Gather all groups columns into one list
+ List<Object> propertyIds = new ArrayList<Object>();
+ for (ColumnGroup group : groups) {
+ propertyIds.addAll(group.getColumns());
+ }
+
+ ColumnGroupState state = new ColumnGroupState();
+ ColumnGroup group = new ColumnGroup(grid, state, propertyIds);
+ this.groups.add(group);
+
+ // Update state
+ for (Object propertyId : group.getColumns()) {
+ state.columns.add(columnKeys.key(propertyId));
+ }
+ this.state.groups.add(state);
+
+ grid.markAsDirty();
+ return group;
+ }
+
+ /**
+ * Removes a group from the row. Does not remove the group from subgroups,
+ * to remove it from the subgroup invoke removeGroup on the subgroup.
+ *
+ * @param group
+ * the group to remove
+ */
+ public void removeGroup(ColumnGroup group) {
+ int index = groups.indexOf(group);
+ groups.remove(index);
+ state.groups.remove(index);
+ grid.markAsDirty();
+ }
+
+ /**
+ * Get the groups in the row.
+ *
+ * @return unmodifiable list of groups in this row
+ */
+ public List<ColumnGroup> getGroups() {
+ return Collections.unmodifiableList(groups);
+ }
+
+ /**
+ * Checks if a property id has been added to a group in this row.
+ *
+ * @param propertyId
+ * the property id to check for
+ * @return <code>true</code> if the column is included in a group
+ */
+ private boolean hasColumnBeenGrouped(Object propertyId) {
+ for (ColumnGroup group : groups) {
+ if (group.isColumnInGroup(propertyId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Is the header visible for the row.
+ *
+ * @return <code>true</code> if header is visible
+ */
+ public boolean isHeaderVisible() {
+ return state.headerVisible;
+ }
+
+ /**
+ * Sets the header visible for the row.
+ *
+ * @param visible
+ * should the header be shown
+ */
+ public void setHeaderVisible(boolean visible) {
+ state.headerVisible = visible;
+ grid.markAsDirty();
+ }
+
+ /**
+ * Is the footer visible for the row.
+ *
+ * @return <code>true</code> if footer is visible
+ */
+ public boolean isFooterVisible() {
+ return state.footerVisible;
+ }
+
+ /**
+ * Sets the footer visible for the row.
+ *
+ * @param visible
+ * should the footer be shown
+ */
+ public void setFooterVisible(boolean visible) {
+ state.footerVisible = visible;
+ grid.markAsDirty();
+ }
+
+}
diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java
index 25ac796d47..2b19043d93 100644
--- a/server/src/com/vaadin/ui/components/grid/Grid.java
+++ b/server/src/com/vaadin/ui/components/grid/Grid.java
@@ -16,7 +16,9 @@
package com.vaadin.ui.components.grid;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -28,6 +30,7 @@ import com.vaadin.data.Container.PropertySetChangeEvent;
import com.vaadin.data.Container.PropertySetChangeListener;
import com.vaadin.data.Container.PropertySetChangeNotifier;
import com.vaadin.server.KeyMapper;
+import com.vaadin.shared.ui.grid.ColumnGroupRowState;
import com.vaadin.shared.ui.grid.GridColumnState;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.ui.AbstractComponent;
@@ -52,19 +55,27 @@ import com.vaadin.ui.AbstractComponent;
*/
public class Grid extends AbstractComponent {
+ /**
+ * The data source attached to the grid
+ */
private Container.Indexed datasource;
/**
- * Property id -> Column instance mapping
+ * Property id to column instance mapping
*/
private final Map<Object, GridColumn> columns = new HashMap<Object, GridColumn>();
/**
- * Key generator for column server->client communication
+ * Key generator for column server-to-client communication
*/
private final KeyMapper<Object> columnKeys = new KeyMapper<Object>();
/**
+ * The column groups added to the grid
+ */
+ private final List<ColumnGroupRow> columnGroupRows = new ArrayList<ColumnGroupRow>();
+
+ /**
* Property listener for listening to changes in data source properties.
*/
private final PropertySetChangeListener propertyListener = new PropertySetChangeListener() {
@@ -144,11 +155,11 @@ public class Grid extends AbstractComponent {
if (!columns.containsKey(propertyId)) {
GridColumn column = appendColumn(propertyId);
- // By default use property id as column caption
+ // Add by default property id as column header
column.setHeaderCaption(String.valueOf(propertyId));
-
}
}
+
}
/**
@@ -177,27 +188,27 @@ public class Grid extends AbstractComponent {
* @param visible
* <code>true</code> if the header rows should be visible
*/
- public void setHeaderVisible(boolean visible) {
- getState().headerVisible = visible;
+ public void setColumnHeadersVisible(boolean visible) {
+ getState().columnHeadersVisible = visible;
}
/**
* Are the header rows visible?
*
- * @return <code>true</code> if the header is visible
+ * @return <code>true</code> if the headers of the columns are visible
*/
- public boolean isHeaderVisible() {
- return getState(false).headerVisible;
+ public boolean isColumnHeadersVisible() {
+ return getState(false).columnHeadersVisible;
}
/**
* Sets the footer rows visible.
*
* @param visible
- * <code>true</code> if the header rows should be visible
+ * <code>true</code> if the footer rows should be visible
*/
- public void setFooterVisible(boolean visible) {
- getState().footerVisible = visible;
+ public void setColumnFootersVisible(boolean visible) {
+ getState().columnFootersVisible = visible;
}
/**
@@ -205,25 +216,110 @@ public class Grid extends AbstractComponent {
*
* @return <code>true</code> if the footer rows should be visible
*/
- public boolean isFooterVisible() {
- return getState(false).footerVisible;
+ public boolean isColumnFootersVisible() {
+ return getState(false).columnFootersVisible;
+ }
+
+ /**
+ * <p>
+ * Adds a new column group to the grid.
+ *
+ * <p>
+ * Column group rows are rendered in the header and footer of the grid.
+ * Column group rows are made up of column groups which groups together
+ * columns for adding a common auxiliary header or footer for the columns.
+ * </p>
+ * </p>
+ *
+ * <p>
+ * Example usage:
+ *
+ * <pre>
+ * // Add a new column group row to the grid
+ * ColumnGroupRow row = grid.addColumnGroupRow();
+ *
+ * // Group &quot;Column1&quot; and &quot;Column2&quot; together to form a header in the row
+ * ColumnGroup column12 = row.addGroup(&quot;Column1&quot;, &quot;Column2&quot;);
+ *
+ * // Set a common header for &quot;Column1&quot; and &quot;Column2&quot;
+ * column12.setHeader(&quot;Column 1&amp;2&quot;);
+ * </pre>
+ *
+ * </p>
+ *
+ * @return a column group instance you can use to add column groups
+ */
+ public ColumnGroupRow addColumnGroupRow() {
+ ColumnGroupRowState state = new ColumnGroupRowState();
+ ColumnGroupRow row = new ColumnGroupRow(this, state, columnKeys);
+ columnGroupRows.add(row);
+ getState().columnGroupRows.add(state);
+ return row;
+ }
+
+ /**
+ * Adds a new column group to the grid at a specific index
+ *
+ * @param rowIndex
+ * the index of the row
+ * @return a column group instance you can use to add column groups
+ */
+ public ColumnGroupRow addColumnGroupRow(int rowIndex) {
+ ColumnGroupRowState state = new ColumnGroupRowState();
+ ColumnGroupRow row = new ColumnGroupRow(this, state, columnKeys);
+ columnGroupRows.add(rowIndex, row);
+ getState().columnGroupRows.add(rowIndex, state);
+ return row;
+ }
+
+ /**
+ * Removes a column group.
+ *
+ * @param row
+ * the row to remove
+ */
+ public void removeColumnGroupRow(ColumnGroupRow row) {
+ columnGroupRows.remove(row);
+ getState().columnGroupRows.remove(row.getState());
+ }
+
+ /**
+ * Gets the column group rows.
+ *
+ * @return an unmodifiable list of column group rows
+ */
+ public List<ColumnGroupRow> getColumnGroupRows() {
+ return Collections.unmodifiableList(new ArrayList<ColumnGroupRow>(
+ columnGroupRows));
}
/**
* Used internally by the {@link Grid} to get a {@link GridColumn} by
* referencing its generated state id. Also used by {@link GridColumn} to
- * verify if it has been detached from the {@link Grid}
+ * verify if it has been detached from the {@link Grid}.
*
* @param columnId
- * The client id generated for the column when the column is
+ * the client id generated for the column when the column is
* added to the grid
- * @return The column with the id or <code>null</code> if not found
+ * @return the column with the id or <code>null</code> if not found
*/
GridColumn getColumnByColumnId(String columnId) {
- Object propertyId = columnKeys.get(columnId);
+ Object propertyId = getPropertyIdByColumnId(columnId);
return getColumn(propertyId);
}
+ /**
+ * Used internally by the {@link Grid} to get a property id by referencing
+ * the columns generated state id.
+ *
+ * @param columnId
+ * The state id of the column
+ * @return The column instance or null if not found
+ */
+ Object getPropertyIdByColumnId(String columnId) {
+ return columnKeys.get(columnId);
+ }
+
@Override
protected GridState getState() {
return (GridState) super.getState();
@@ -241,7 +337,7 @@ public class Grid extends AbstractComponent {
* @param datasourcePropertyId
* The property id of a property in the datasource
*/
- protected GridColumn appendColumn(Object datasourcePropertyId) {
+ private GridColumn appendColumn(Object datasourcePropertyId) {
if (datasourcePropertyId == null) {
throw new IllegalArgumentException("Property id cannot be null");
}
diff --git a/server/src/com/vaadin/ui/components/grid/GridColumn.java b/server/src/com/vaadin/ui/components/grid/GridColumn.java
index 505919b3cf..dde0669238 100644
--- a/server/src/com/vaadin/ui/components/grid/GridColumn.java
+++ b/server/src/com/vaadin/ui/components/grid/GridColumn.java
@@ -16,6 +16,8 @@
package com.vaadin.ui.components.grid;
+import java.io.Serializable;
+
import com.vaadin.shared.ui.grid.GridColumnState;
/**
@@ -25,10 +27,10 @@ import com.vaadin.shared.ui.grid.GridColumnState;
* @since 7.2
* @author Vaadin Ltd
*/
-public class GridColumn {
+public class GridColumn implements Serializable {
/**
- * The shared state of the column
+ * The state of the column shared to the client
*/
private final GridColumnState state;
@@ -138,9 +140,16 @@ public class GridColumn {
* the new pixel width of the column
* @throws IllegalStateException
* if the column is no longer attached to any grid
+ * @throws IllegalArgumentException
+ * thrown if pixel width is less than zero
*/
- public void setWidth(int pixelWidth) throws IllegalStateException {
+ public void setWidth(int pixelWidth) throws IllegalStateException,
+ IllegalArgumentException {
checkColumnIsAttached();
+ if (pixelWidth < 0) {
+ throw new IllegalArgumentException(
+ "Pixel width should be greated than 0");
+ }
state.width = pixelWidth;
grid.markAsDirty();
}
diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java
index 5989d537b4..85864160a8 100644
--- a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java
+++ b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java
@@ -32,6 +32,8 @@ import com.vaadin.data.util.IndexedContainer;
import com.vaadin.server.KeyMapper;
import com.vaadin.shared.ui.grid.GridColumnState;
import com.vaadin.shared.ui.grid.GridState;
+import com.vaadin.ui.components.grid.ColumnGroup;
+import com.vaadin.ui.components.grid.ColumnGroupRow;
import com.vaadin.ui.components.grid.Grid;
import com.vaadin.ui.components.grid.GridColumn;
@@ -110,9 +112,15 @@ public class GridColumns {
assertEquals(100, column.getWidth());
assertEquals(column.getWidth(), getColumnState("column1").width);
- column.setWidth(-1);
- assertEquals(-1, column.getWidth());
- assertEquals(-1, getColumnState("column1").width);
+ try {
+ column.setWidth(-1);
+ fail("Setting width to -1 should throw exception");
+ } catch (IllegalArgumentException iae) {
+
+ }
+
+ assertEquals(100, column.getWidth());
+ assertEquals(100, getColumnState("column1").width);
}
@Test
@@ -126,6 +134,7 @@ public class GridColumns {
try {
column.setHeaderCaption("asd");
+
fail("Succeeded in modifying a detached column");
} catch (IllegalStateException ise) {
// Detached state should throw exception
@@ -157,7 +166,7 @@ public class GridColumns {
}
@Test
- public void testAddingColumn() {
+ public void testAddingColumn() throws Exception {
grid.getContainerDatasource().addContainerProperty("columnX",
String.class, "");
GridColumn column = grid.getColumn("columnX");
@@ -165,33 +174,72 @@ public class GridColumns {
}
@Test
- public void testHeaderVisiblility() {
+ public void testHeaderVisiblility() throws Exception {
- assertTrue(grid.isHeaderVisible());
- assertTrue(state.headerVisible);
+ assertTrue(grid.isColumnHeadersVisible());
+ assertTrue(state.columnHeadersVisible);
- grid.setHeaderVisible(false);
- assertFalse(grid.isHeaderVisible());
- assertFalse(state.headerVisible);
+ grid.setColumnHeadersVisible(false);
+ assertFalse(grid.isColumnHeadersVisible());
+ assertFalse(state.columnHeadersVisible);
- grid.setHeaderVisible(true);
- assertTrue(grid.isHeaderVisible());
- assertTrue(state.headerVisible);
+ grid.setColumnHeadersVisible(true);
+ assertTrue(grid.isColumnHeadersVisible());
+ assertTrue(state.columnHeadersVisible);
}
@Test
- public void testFooterVisibility() {
+ public void testFooterVisibility() throws Exception {
+
+ assertFalse(grid.isColumnFootersVisible());
+ assertFalse(state.columnFootersVisible);
- assertTrue(grid.isFooterVisible());
- assertTrue(state.footerVisible);
+ grid.setColumnFootersVisible(false);
+ assertFalse(grid.isColumnFootersVisible());
+ assertFalse(state.columnFootersVisible);
- grid.setFooterVisible(false);
- assertFalse(grid.isFooterVisible());
- assertFalse(state.footerVisible);
+ grid.setColumnFootersVisible(true);
+ assertTrue(grid.isColumnFootersVisible());
+ assertTrue(state.columnFootersVisible);
+ }
- grid.setFooterVisible(true);
- assertTrue(grid.isFooterVisible());
- assertTrue(state.footerVisible);
+ @Test
+ public void testColumnGroups() throws Exception {
+
+ // Add a new row
+ ColumnGroupRow row = grid.addColumnGroupRow();
+ assertTrue(state.columnGroupRows.size() == 1);
+
+ // Add a group by property id
+ ColumnGroup columns12 = row.addGroup("column1", "column2");
+ assertTrue(state.columnGroupRows.get(0).groups.size() == 1);
+
+ // Set header of column
+ columns12.setHeaderCaption("Column12");
+ assertEquals("Column12",
+ state.columnGroupRows.get(0).groups.get(0).header);
+
+ // Set footer of column
+ columns12.setFooterCaption("Footer12");
+ assertEquals("Footer12",
+ state.columnGroupRows.get(0).groups.get(0).footer);
+
+ // Add another group by column instance
+ ColumnGroup columns34 = row.addGroup(grid.getColumn("column3"),
+ grid.getColumn("column4"));
+ assertTrue(state.columnGroupRows.get(0).groups.size() == 2);
+
+ // add another group row
+ ColumnGroupRow row2 = grid.addColumnGroupRow();
+ assertTrue(state.columnGroupRows.size() == 2);
+
+ // add a group by combining the two previous groups
+ ColumnGroup columns1234 = row2.addGroup(columns12, columns34);
+ assertTrue(columns1234.getColumns().size() == 4);
+
+ // Insert a group as the second group
+ ColumnGroupRow newRow2 = grid.addColumnGroupRow(1);
+ assertTrue(state.columnGroupRows.size() == 3);
}
private GridColumnState getColumnState(Object propertyId) {
diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java
new file mode 100644
index 0000000000..a8e0f87457
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.shared.ui.grid;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The column group row data shared between the server and client
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ColumnGroupRowState implements Serializable {
+
+ /**
+ * The groups that has been added to the row
+ */
+ public List<ColumnGroupState> groups = new ArrayList<ColumnGroupState>();
+
+ /**
+ * Is the header shown
+ */
+ public boolean headerVisible = true;
+
+ /**
+ * Is the footer shown
+ */
+ public boolean footerVisible = false;
+
+}
diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java
new file mode 100644
index 0000000000..3992b6611f
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.shared.ui.grid;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The column group data shared between the server and the client
+ *
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class ColumnGroupState implements Serializable {
+
+ /**
+ * The columns that is included in the group
+ */
+ public List<String> columns = new ArrayList<String>();
+
+ /**
+ * The header text of the group
+ */
+ public String header;
+
+ /**
+ * The footer text of the group
+ */
+ public String footer;
+}
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java
index 391eb2a65c..0301c5ead2 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java
@@ -34,17 +34,11 @@ public class GridColumnState implements Serializable {
/**
* Header caption for the column
- *
- * FIXME Only single header currently supported. Should support many
- * headers.
*/
public String header;
/**
* Footer caption for the column
- *
- * FIXME Only single footer currently supported. Should support many
- * footers.
*/
public String footer;
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java
index e1e0fff354..d1167f3d4f 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java
@@ -40,13 +40,17 @@ public class GridState extends AbstractComponentState {
public List<GridColumnState> columns = new ArrayList<GridColumnState>();
/**
- * Are the header row(s) visible. By default they are visible.
+ * Is the column header row visible
*/
- public boolean headerVisible = true;
+ public boolean columnHeadersVisible = true;
/**
- * Are the footer row(s) visible. By default they are visible.
+ * Is the column footer row visible
*/
- public boolean footerVisible = true;
+ public boolean columnFootersVisible = false;
+ /**
+ * The column groups added to the grid
+ */
+ public List<ColumnGroupRowState> columnGroupRows = new ArrayList<ColumnGroupRowState>();
}
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
index 5b3d742f19..bd3e96f84a 100644
--- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
+++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
@@ -19,6 +19,8 @@ import java.util.ArrayList;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.tests.components.AbstractComponentTest;
+import com.vaadin.ui.components.grid.ColumnGroup;
+import com.vaadin.ui.components.grid.ColumnGroupRow;
import com.vaadin.ui.components.grid.Grid;
import com.vaadin.ui.components.grid.GridColumn;
@@ -32,6 +34,8 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
private final int COLUMNS = 10;
+ private int columnGroupRows = 0;
+
@Override
protected Grid constructComponent() {
@@ -42,20 +46,51 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
ds.addContainerProperty("Column" + col, String.class, "");
}
+ // Create grid
Grid grid = new Grid(ds);
- // Headers and footers
+ // Add footer values (header values are automatically created)
for (int col = 0; col < COLUMNS; col++) {
- GridColumn column = grid.getColumn("Column" + col);
- column.setHeaderCaption("Column " + col);
- column.setFooterCaption("Footer " + col);
+ grid.getColumn("Column" + col).setFooterCaption("Footer " + col);
}
createColumnActions();
+ createHeaderActions();
+
+ createFooterActions();
+
+ createColumnGroupActions();
+
return grid;
}
+ protected void createHeaderActions() {
+ createCategory("Headers", null);
+
+ createBooleanAction("Visible", "Headers", true,
+ new Command<Grid, Boolean>() {
+
+ @Override
+ public void execute(Grid grid, Boolean value, Object data) {
+ grid.setColumnHeadersVisible(value);
+ }
+ });
+ }
+
+ protected void createFooterActions() {
+ createCategory("Footers", null);
+
+ createBooleanAction("Visible", "Footers", false,
+ new Command<Grid, Boolean>() {
+
+ @Override
+ public void execute(Grid grid, Boolean value, Object data) {
+ grid.setColumnFootersVisible(value);
+ }
+ });
+ }
+
protected void createColumnActions() {
createCategory("Columns", null);
@@ -77,46 +112,6 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
}
}, c);
- createBooleanAction("Footer", "Column" + c, true,
- new Command<Grid, Boolean>() {
-
- @Override
- public void execute(Grid grid, Boolean value,
- Object columnIndex) {
- Object propertyId = (new ArrayList(grid
- .getContainerDatasource()
- .getContainerPropertyIds())
- .get((Integer) columnIndex));
- GridColumn column = grid.getColumn(propertyId);
- String footer = column.getFooterCaption();
- if (footer == null) {
- column.setFooterCaption("Footer " + columnIndex);
- } else {
- column.setFooterCaption(null);
- }
- }
- }, c);
-
- createBooleanAction("Header", "Column" + c, true,
- new Command<Grid, Boolean>() {
-
- @Override
- public void execute(Grid grid, Boolean value,
- Object columnIndex) {
- Object propertyId = (new ArrayList(grid
- .getContainerDatasource()
- .getContainerPropertyIds())
- .get((Integer) columnIndex));
- GridColumn column = grid.getColumn(propertyId);
- String header = column.getHeaderCaption();
- if (header == null) {
- column.setHeaderCaption("Column " + columnIndex);
- } else {
- column.setHeaderCaption(null);
- }
- }
- }, c);
-
createClickAction("Remove", "Column" + c,
new Command<Grid, String>() {
@@ -131,6 +126,72 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
}
+ protected void createColumnGroupActions() {
+ createCategory("Column groups", null);
+
+ createClickAction("Add group row", "Column groups",
+ new Command<Grid, String>() {
+
+ @Override
+ public void execute(Grid grid, String value, Object data) {
+ final ColumnGroupRow row = grid.addColumnGroupRow();
+ columnGroupRows++;
+ createCategory("Column group row " + columnGroupRows,
+ "Column groups");
+
+ createBooleanAction("Header Visible",
+ "Column group row " + columnGroupRows, true,
+ new Command<Grid, Boolean>() {
+
+ @Override
+ public void execute(Grid grid,
+ Boolean value, Object columnIndex) {
+ row.setHeaderVisible(value);
+ }
+ }, row);
+
+ createBooleanAction("Footer Visible",
+ "Column group row " + columnGroupRows, false,
+ new Command<Grid, Boolean>() {
+
+ @Override
+ public void execute(Grid grid,
+ Boolean value, Object columnIndex) {
+ row.setFooterVisible(value);
+ }
+ }, row);
+
+ for (int i = 0; i < COLUMNS; i += 2) {
+ final int columnIndex = i;
+ createClickAction("Group Column " + columnIndex
+ + " & " + (columnIndex + 1),
+ "Column group row " + columnGroupRows,
+ new Command<Grid, Integer>() {
+
+ @Override
+ public void execute(Grid c,
+ Integer value, Object data) {
+ final ColumnGroup group = row
+ .addGroup(
+ "Column" + value,
+ "Column"
+ + (value + 1));
+
+ group.setHeaderCaption("Column "
+ + value + " & "
+ + (value + 1));
+
+ group.setFooterCaption("Column "
+ + value + " & "
+ + (value + 1));
+ }
+ }, i, row);
+ }
+ }
+ }, null, null);
+
+ }
+
@Override
protected Integer getTicketNumber() {
return 12829;
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java b/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java
new file mode 100644
index 0000000000..66e7651f76
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ *
+ */
+package com.vaadin.tests.components.grid;
+
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.components.grid.ColumnGroup;
+import com.vaadin.ui.components.grid.ColumnGroupRow;
+import com.vaadin.ui.components.grid.Grid;
+import com.vaadin.ui.components.grid.GridColumn;
+
+/**
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class GridColumnGroups extends AbstractTestUI {
+
+ private final int COLUMNS = 4;
+
+ @Override
+ protected void setup(VaadinRequest request) {
+
+ // Setup grid
+ IndexedContainer ds = new IndexedContainer();
+ for (int col = 0; col < COLUMNS; col++) {
+ ds.addContainerProperty("Column" + col, String.class, "");
+ }
+ Grid grid = new Grid(ds);
+ addComponent(grid);
+
+ /*-
+ * ---------------------------------------------
+ * | Header 1 | <- Auxiliary row 2
+ * |-------------------------------------------|
+ * | Header 2 | Header 3 | <- Auxiliary row 1
+ * |-------------------------------------------|
+ * | Column 1 | Column 2 | Column 3 | Column 4 | <- Column headers
+ * --------------------------------------------|
+ * | ... | ... | ... | ... |
+ * | ... | ... | ... | ... |
+ * --------------------------------------------|
+ * | Column 1 | Column 2 | Column 3 | Column 4 | <- Column footers
+ * --------------------------------------------|
+ * | Footer 2 | Footer 3 | <- Auxiliary row 1
+ * --------------------------------------------|
+ * | Footer 1 | <- Auxiliary row 2
+ * ---------------------------------------------
+ -*/
+
+ // Set column footers (headers are generated automatically)
+ grid.setColumnFootersVisible(true);
+ for (Object propertyId : ds.getContainerPropertyIds()) {
+ GridColumn column = grid.getColumn(propertyId);
+ column.setFooterCaption(String.valueOf(propertyId));
+ }
+
+ // First auxiliary row
+ ColumnGroupRow auxRow1 = grid.addColumnGroupRow();
+
+ // Using property id to create a column group
+ ColumnGroup columns12 = auxRow1.addGroup("Column0", "Column1");
+ columns12.setHeaderCaption("Header 2");
+ columns12.setFooterCaption("Footer 2");
+
+ // Using grid columns to create a column group
+ GridColumn column3 = grid.getColumn("Column2");
+ GridColumn column4 = grid.getColumn("Column3");
+ ColumnGroup columns34 = auxRow1.addGroup(column3, column4);
+ columns34.setHeaderCaption("Header 3");
+ columns34.setFooterCaption("Footer 3");
+
+ // Second auxiliary row
+ ColumnGroupRow auxRow2 = grid.addColumnGroupRow();
+
+ // Using previous groups to create a column group
+ ColumnGroup columns1234 = auxRow2.addGroup(columns12, columns34);
+ columns1234.setHeaderCaption("Header 1");
+ columns1234.setFooterCaption("Footer 1");
+
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Grid should support headers and footer groups";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 12894;
+ }
+
+}