summaryrefslogtreecommitdiffstats
path: root/client
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 /client
parentc2d38fa6c2d67457065fd3dce7e0d939ae0a1278 (diff)
downloadvaadin-framework-4caa2f5b6e26ade52a4fba66a0a020b79f9008ea.tar.gz
vaadin-framework-4caa2f5b6e26ade52a4fba66a0a020b79f9008ea.zip
Multiple headers and footer rows #3153
Change-Id: Iadb0d8b051d0f0ef1303e0d7d740cf476cd81971
Diffstat (limited to 'client')
-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
4 files changed, 771 insertions, 108 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);
+ }
+ }
+ }
}