diff options
author | Teemu Suo-Anttila <teemusa@vaadin.com> | 2014-12-04 11:35:14 +0200 |
---|---|---|
committer | Teemu Suo-Anttila <teemusa@vaadin.com> | 2014-12-08 14:06:55 +0200 |
commit | e1d1673422cd31ac2fa9e7476ca7f695a0143868 (patch) | |
tree | 9cc4bb4cbc4cb1539e7de6194e7b46f5835a95a8 | |
parent | aaba0aca69d0093f30d6fa04f2695d8272b7314a (diff) | |
download | vaadin-framework-e1d1673422cd31ac2fa9e7476ca7f695a0143868.tar.gz vaadin-framework-e1d1673422cd31ac2fa9e7476ca7f695a0143868.zip |
Flatten Header and Footer API into client-side Grid (#13334)
Change-Id: I70d6d79efbf8d6b14d89d836779cbf16173887fc
7 files changed, 1135 insertions, 903 deletions
diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index fe14ddef30..ad970b3af1 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -19,8 +19,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,9 +51,6 @@ import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.SubPartAware; import com.vaadin.client.ui.grid.EditorRow.State; -import com.vaadin.client.ui.grid.GridFooter.FooterRow; -import com.vaadin.client.ui.grid.GridHeader.HeaderRow; -import com.vaadin.client.ui.grid.GridStaticSection.StaticCell; import com.vaadin.client.ui.grid.events.AbstractGridKeyEventHandler; import com.vaadin.client.ui.grid.events.BodyKeyDownHandler; import com.vaadin.client.ui.grid.events.BodyKeyPressHandler; @@ -159,6 +158,757 @@ public class Grid<T> extends ResizeComposite implements GridColumn<?, T> column, int columnIndex); } + /** + * Abstract base class for Grid header and footer sections. + * + * @param <ROWTYPE> + * the type of the rows in the section + */ + protected abstract static class StaticSection<ROWTYPE extends StaticSection.StaticRow<?>> { + + /** + * A header or footer cell. Has a simple textual caption. + * + */ + static class StaticCell { + + private Object content = null; + + private int colspan = 1; + + private StaticSection<?> section; + + private GridStaticCellType type = GridStaticCellType.TEXT; + + private String styleName = null; + + /** + * Sets the text displayed in this cell. + * + * @param text + * a plain text caption + */ + public void setText(String text) { + this.content = text; + this.type = GridStaticCellType.TEXT; + section.requestSectionRefresh(); + } + + /** + * Returns the text displayed in this cell. + * + * @return the plain text caption + */ + public String getText() { + if (type != GridStaticCellType.TEXT) { + throw new IllegalStateException( + "Cannot fetch Text from a cell with type " + type); + } + return (String) content; + } + + protected StaticSection<?> getSection() { + assert section != null; + return section; + } + + protected void setSection(StaticSection<?> section) { + this.section = section; + } + + /** + * Returns the amount of columns the cell spans. By default is 1. + * + * @return The amount of columns the cell spans. + */ + public int getColspan() { + return colspan; + } + + /** + * Sets the amount of columns the cell spans. Must be more or equal + * to 1. By default is 1. + * + * @param colspan + * the colspan to set + */ + public void setColspan(int colspan) { + if (colspan < 1) { + throw new IllegalArgumentException( + "Colspan cannot be less than 1"); + } + + this.colspan = colspan; + section.requestSectionRefresh(); + } + + /** + * Returns the html inside the cell. + * + * @throws IllegalStateException + * if trying to retrive HTML from a cell with a type + * other than {@link GridStaticCellType#HTML}. + * @return the html content of the cell. + */ + public String getHtml() { + if (type != GridStaticCellType.HTML) { + throw new IllegalStateException( + "Cannot fetch HTML from a cell with type " + type); + } + return (String) content; + } + + /** + * Sets the content of the cell to the provided html. All previous + * content is discarded and the cell type is set to + * {@link GridStaticCellType#HTML}. + * + * @param html + * The html content of the cell + */ + public void setHtml(String html) { + this.content = html; + this.type = GridStaticCellType.HTML; + section.requestSectionRefresh(); + } + + /** + * Returns the widget in the cell. + * + * @throws IllegalStateException + * if the cell is not {@link GridStaticCellType#WIDGET} + * + * @return the widget in the cell + */ + public Widget getWidget() { + if (type != GridStaticCellType.WIDGET) { + throw new IllegalStateException( + "Cannot fetch Widget from a cell with type " + type); + } + return (Widget) content; + } + + /** + * Set widget as the content of the cell. The type of the cell + * becomes {@link GridStaticCellType#WIDGET}. All previous content + * is discarded. + * + * @param widget + * The widget to add to the cell. Should not be + * previously attached anywhere (widget.getParent == + * null). + */ + public void setWidget(Widget widget) { + this.content = widget; + this.type = GridStaticCellType.WIDGET; + section.requestSectionRefresh(); + } + + /** + * Returns the type of the cell. + * + * @return the type of content the cell contains. + */ + public GridStaticCellType getType() { + return type; + } + + /** + * Returns the custom style name for this cell. + * + * @return the style name or null if no style name has been set + */ + public String getStyleName() { + return styleName; + } + + /** + * Sets a custom style name for this cell. + * + * @param styleName + * the style name to set or null to not use any style + * name + */ + public void setStyleName(String styleName) { + this.styleName = styleName; + section.requestSectionRefresh(); + + } + + } + + /** + * Abstract base class for Grid header and footer rows. + * + * @param <CELLTYPE> + * the type of the cells in the row + */ + abstract static class StaticRow<CELLTYPE extends StaticCell> { + + private Map<GridColumn<?, ?>, CELLTYPE> cells = new HashMap<GridColumn<?, ?>, CELLTYPE>(); + + private StaticSection<?> section; + + /** + * Map from set of spanned columns to cell meta data. + */ + private Map<Set<GridColumn<?, ?>>, CELLTYPE> cellGroups = new HashMap<Set<GridColumn<?, ?>>, CELLTYPE>(); + + /** + * A custom style name for the row or null if none is set. + */ + private String styleName = null; + + /** + * Returns the cell on given GridColumn. If the column is merged + * returned cell is the cell for the whole group. + * + * @param column + * the column in grid + * @return the cell on given column, merged cell for merged columns, + * null if not found + */ + public CELLTYPE getCell(GridColumn<?, ?> column) { + Set<GridColumn<?, ?>> cellGroup = getCellGroupForColumn(column); + if (cellGroup != null) { + return cellGroups.get(cellGroup); + } + return cells.get(column); + } + + /** + * Merges columns cells in a row + * + * @param columns + * the columns which header should be merged + * @return the remaining visible cell after the merge, or the cell + * on first column if all are hidden + */ + public CELLTYPE join(GridColumn<?, ?>... columns) { + if (columns.length <= 1) { + throw new IllegalArgumentException( + "You can't merge less than 2 columns together."); + } + + HashSet<GridColumn<?, ?>> columnGroup = new HashSet<GridColumn<?, ?>>(); + for (GridColumn<?, ?> column : columns) { + if (!cells.containsKey(column)) { + throw new IllegalArgumentException( + "Given column does not exists on row " + column); + } else if (getCellGroupForColumn(column) != null) { + throw new IllegalStateException( + "Column is already in a group."); + } + columnGroup.add(column); + } + + CELLTYPE joinedCell = createCell(); + cellGroups.put(columnGroup, joinedCell); + joinedCell.setSection(getSection()); + + calculateColspans(); + + return joinedCell; + } + + /** + * Merges columns cells in a row + * + * @param cells + * The cells to merge. Must be from the same row. + * @return The remaining visible cell after the merge, or the first + * cell if all columns are hidden + */ + public CELLTYPE join(CELLTYPE... cells) { + if (cells.length <= 1) { + throw new IllegalArgumentException( + "You can't merge less than 2 cells together."); + } + + GridColumn<?, ?>[] columns = new GridColumn<?, ?>[cells.length]; + + int j = 0; + for (GridColumn<?, ?> column : this.cells.keySet()) { + CELLTYPE cell = this.cells.get(column); + if (!this.cells.containsValue(cells[j])) { + throw new IllegalArgumentException( + "Given cell does not exists on row"); + } else if (cell.equals(cells[j])) { + columns[j++] = column; + if (j == cells.length) { + break; + } + } + } + + return join(columns); + } + + private Set<GridColumn<?, ?>> getCellGroupForColumn( + GridColumn<?, ?> column) { + for (Set<GridColumn<?, ?>> group : cellGroups.keySet()) { + if (group.contains(column)) { + return group; + } + } + return null; + } + + void calculateColspans() { + + // Reset all cells + for (CELLTYPE cell : this.cells.values()) { + cell.setColspan(1); + } + + List<GridColumn<?, ?>> columnOrder = new ArrayList<GridColumn<?, ?>>( + section.grid.getColumns()); + // Set colspan for grouped cells + for (Set<GridColumn<?, ?>> group : cellGroups.keySet()) { + if (!checkCellGroupAndOrder(columnOrder, group)) { + cellGroups.get(group).setColspan(1); + } else { + int colSpan = group.size(); + cellGroups.get(group).setColspan(colSpan); + } + } + + } + + private boolean checkCellGroupAndOrder( + List<GridColumn<?, ?>> columnOrder, + Set<GridColumn<?, ?>> cellGroup) { + if (!columnOrder.containsAll(cellGroup)) { + return false; + } + + for (int i = 0; i < columnOrder.size(); ++i) { + if (!cellGroup.contains(columnOrder.get(i))) { + continue; + } + + for (int j = 1; j < cellGroup.size(); ++j) { + if (!cellGroup.contains(columnOrder.get(i + j))) { + return false; + } + } + return true; + } + return false; + } + + protected void addCell(GridColumn<?, ?> column) { + CELLTYPE cell = createCell(); + cell.setSection(getSection()); + cells.put(column, cell); + } + + protected void removeCell(GridColumn<?, ?> column) { + cells.remove(column); + } + + protected abstract CELLTYPE createCell(); + + protected StaticSection<?> getSection() { + return section; + } + + protected void setSection(StaticSection<?> section) { + this.section = section; + } + + /** + * Returns the custom style name for this row. + * + * @return the style name or null if no style name has been set + */ + public String getStyleName() { + return styleName; + } + + /** + * Sets a custom style name for this row. + * + * @param styleName + * the style name to set or null to not use any style + * name + */ + public void setStyleName(String styleName) { + this.styleName = styleName; + section.requestSectionRefresh(); + } + } + + private Grid<?> grid; + + private List<ROWTYPE> rows = new ArrayList<ROWTYPE>(); + + private boolean visible = true; + + /** + * Creates and returns a new instance of the row type. + * + * @return the created row + */ + protected abstract ROWTYPE createRow(); + + /** + * Informs the grid that this section should be re-rendered. + * <p> + * <b>Note</b> that re-render means calling update() on each cell, + * preAttach()/postAttach()/preDetach()/postDetach() is not called as + * the cells are not removed from the DOM. + */ + protected abstract void requestSectionRefresh(); + + /** + * Sets the visibility of the whole section. + * + * @param visible + * true to show this section, false to hide + */ + public void setVisible(boolean visible) { + this.visible = visible; + requestSectionRefresh(); + } + + /** + * Returns the visibility of this section. + * + * @return true if visible, false otherwise. + */ + public boolean isVisible() { + return visible; + } + + /** + * Inserts a new row at the given position. Shifts the row currently at + * that position and any subsequent rows down (adds one to their + * indices). + * + * @param index + * the position at which to insert the row + * @return the new row + * + * @throws IndexOutOfBoundsException + * if the index is out of bounds + * @see #appendRow() + * @see #prependRow() + * @see #removeRow(int) + * @see #removeRow(StaticRow) + */ + public ROWTYPE addRowAt(int index) { + ROWTYPE row = createRow(); + row.setSection(this); + for (int i = 0; i < getGrid().getColumnCount(); ++i) { + row.addCell(grid.getColumn(i)); + } + rows.add(index, row); + + requestSectionRefresh(); + return row; + } + + /** + * Adds a new row at the top of this section. + * + * @return the new row + * @see #appendRow() + * @see #addRowAt(int) + * @see #removeRow(int) + * @see #removeRow(StaticRow) + */ + public ROWTYPE prependRow() { + return addRowAt(0); + } + + /** + * Adds a new row at the bottom of this section. + * + * @return the new row + * @see #prependRow() + * @see #addRowAt(int) + * @see #removeRow(int) + * @see #removeRow(StaticRow) + */ + public ROWTYPE appendRow() { + return addRowAt(rows.size()); + } + + /** + * Removes the row at the given position. + * + * @param index + * the position of the row + * + * @throws IndexOutOfBoundsException + * if the index is out of bounds + * @see #addRowAt(int) + * @see #appendRow() + * @see #prependRow() + * @see #removeRow(StaticRow) + */ + public void removeRow(int index) { + rows.remove(index); + requestSectionRefresh(); + } + + /** + * Removes the given row from the section. + * + * @param row + * the row to be removed + * + * @throws IllegalArgumentException + * if the row does not exist in this section + * @see #addRowAt(int) + * @see #appendRow() + * @see #prependRow() + * @see #removeRow(int) + */ + public void removeRow(ROWTYPE row) { + try { + removeRow(rows.indexOf(row)); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Section does not contain the given row"); + } + } + + /** + * Returns the row at the given position. + * + * @param index + * the position of the row + * @return the row with the given index + * + * @throws IndexOutOfBoundsException + * if the index is out of bounds + */ + public ROWTYPE getRow(int index) { + try { + return rows.get(index); + } catch (IndexOutOfBoundsException e) { + throw new IllegalArgumentException("Row with index " + index + + " does not exist"); + } + } + + /** + * Returns the number of rows in this section. + * + * @return the number of rows + */ + public int getRowCount() { + return rows.size(); + } + + protected List<ROWTYPE> getRows() { + return rows; + } + + protected int getVisibleRowCount() { + return isVisible() ? getRowCount() : 0; + } + + protected void addColumn(GridColumn<?, ?> column) { + for (ROWTYPE row : rows) { + row.addCell(column); + } + } + + protected void removeColumn(GridColumn<?, ?> column) { + for (ROWTYPE row : rows) { + row.removeCell(column); + } + } + + protected void setGrid(Grid<?> grid) { + this.grid = grid; + } + + protected Grid<?> getGrid() { + assert grid != null; + return grid; + } + } + + /** + * Represents the header section of a Grid. A header consists of a single + * header row containing a header cell for each column. Each cell has a + * simple textual caption. + */ + protected static class Header extends StaticSection<HeaderRow> { + private HeaderRow defaultRow; + + private boolean markAsDirty = false; + + @Override + public void removeRow(int index) { + HeaderRow removedRow = getRow(index); + super.removeRow(index); + if (removedRow == defaultRow) { + setDefaultRow(null); + } + } + + /** + * Sets the default row of this header. The default row is a special + * header row providing a user interface for sorting columns. + * + * @param row + * the new default row, or null for no default row + * + * @throws IllegalArgumentException + * this header does not contain the row + */ + public void setDefaultRow(HeaderRow row) { + if (row == defaultRow) { + return; + } + if (row != null && !getRows().contains(row)) { + throw new IllegalArgumentException( + "Cannot set a default row that does not exist in the container"); + } + if (defaultRow != null) { + defaultRow.setDefault(false); + } + if (row != null) { + row.setDefault(true); + } + defaultRow = row; + requestSectionRefresh(); + } + + /** + * Returns the current default row of this header. The default row is a + * special header row providing a user interface for sorting columns. + * + * @return the default row or null if no default row set + */ + public HeaderRow getDefaultRow() { + return defaultRow; + } + + @Override + protected HeaderRow createRow() { + return new HeaderRow(); + } + + @Override + protected void requestSectionRefresh() { + markAsDirty = true; + + /* + * Defer the refresh so if we multiple times call refreshSection() + * (for example when updating cell values) we only get one actual + * refresh in the end. + */ + Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() { + + @Override + public void execute() { + if (markAsDirty) { + markAsDirty = false; + getGrid().refreshHeader(); + } + } + }); + } + + /** + * Returns the events consumed by the header + * + * @return a collection of BrowserEvents + */ + public Collection<String> getConsumedEvents() { + return Arrays.asList(BrowserEvents.TOUCHSTART, + BrowserEvents.TOUCHMOVE, BrowserEvents.TOUCHEND, + BrowserEvents.TOUCHCANCEL, BrowserEvents.CLICK); + } + } + + /** + * A single row in a grid header section. + * + */ + public static class HeaderRow extends StaticSection.StaticRow<HeaderCell> { + + private boolean isDefault = false; + + protected void setDefault(boolean isDefault) { + this.isDefault = isDefault; + } + + public boolean isDefault() { + return isDefault; + } + + @Override + protected HeaderCell createCell() { + return new HeaderCell(); + } + } + + /** + * A single cell in a grid header row. Has a textual caption. + * + */ + public static class HeaderCell extends StaticSection.StaticCell { + } + + /** + * Represents the footer section of a Grid. The footer is always empty. + */ + protected static class Footer extends StaticSection<FooterRow> { + private boolean markAsDirty = false; + + @Override + protected FooterRow createRow() { + return new FooterRow(); + } + + @Override + protected void requestSectionRefresh() { + markAsDirty = true; + + /* + * Defer the refresh so if we multiple times call refreshSection() + * (for example when updating cell values) we only get one actual + * refresh in the end. + */ + Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() { + + @Override + public void execute() { + if (markAsDirty) { + markAsDirty = false; + getGrid().refreshFooter(); + } + } + }); + } + } + + /** + * A single cell in a grid Footer row. Has a textual caption. + * + */ + public static class FooterCell extends StaticSection.StaticCell { + } + + /** + * A single row in a grid Footer section. + * + */ + public static class FooterRow extends StaticSection.StaticRow<FooterCell> { + + @Override + protected FooterCell createCell() { + return new FooterCell(); + } + } + public static abstract class AbstractGridKeyEvent<HANDLER extends AbstractGridKeyEventHandler> extends KeyEvent<HANDLER> { @@ -753,9 +1503,9 @@ public class Grid<T> extends ResizeComposite implements */ private Escalator escalator = GWT.create(Escalator.class); - private final GridHeader header = GWT.create(GridHeader.class); + private final Header header = GWT.create(Header.class); - private final GridFooter footer = GWT.create(GridFooter.class); + private final Footer footer = GWT.create(Footer.class); /** * List of columns in the grid. Order defines the visible order. @@ -1320,10 +2070,10 @@ public class Grid<T> extends ResizeComposite implements protected class StaticSectionUpdater implements EscalatorUpdater { - private GridStaticSection<?> section; + private StaticSection<?> section; private RowContainer container; - public StaticSectionUpdater(GridStaticSection<?> section, + public StaticSectionUpdater(StaticSection<?> section, RowContainer container) { super(); this.section = section; @@ -1332,15 +2082,14 @@ public class Grid<T> extends ResizeComposite implements @Override public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) { - GridStaticSection.StaticRow<?> staticRow = section.getRow(row - .getRow()); + StaticSection.StaticRow<?> staticRow = section.getRow(row.getRow()); final List<GridColumn<?, T>> columns = getColumns(); setCustomStyleName(row.getElement(), staticRow.getStyleName()); for (FlyweightCell cell : cellsToUpdate) { - final StaticCell metadata = staticRow.getCell(columns.get(cell - .getColumn())); + final StaticSection.StaticCell metadata = staticRow + .getCell(columns.get(cell.getColumn())); // Decorate default row with sorting indicators if (staticRow instanceof HeaderRow) { @@ -1426,13 +2175,12 @@ public class Grid<T> extends ResizeComposite implements @Override public void postAttach(Row row, Iterable<FlyweightCell> attachedCells) { - GridStaticSection.StaticRow<?> gridRow = section.getRow(row - .getRow()); + StaticSection.StaticRow<?> gridRow = section.getRow(row.getRow()); List<GridColumn<?, T>> columns = getColumns(); for (FlyweightCell cell : attachedCells) { - StaticCell metadata = gridRow.getCell(columns.get(cell - .getColumn())); + StaticSection.StaticCell metadata = gridRow.getCell(columns + .get(cell.getColumn())); /* * If the cell contains widgets that are not currently attach * then attach them now. @@ -1456,12 +2204,12 @@ public class Grid<T> extends ResizeComposite implements @Override public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) { if (section.getRowCount() > row.getRow()) { - GridStaticSection.StaticRow<?> gridRow = section.getRow(row + StaticSection.StaticRow<?> gridRow = section.getRow(row .getRow()); List<GridColumn<?, T>> columns = getColumns(); for (FlyweightCell cell : cellsToDetach) { - StaticCell metadata = gridRow.getCell(columns.get(cell - .getColumn())); + StaticSection.StaticCell metadata = gridRow.getCell(columns + .get(cell.getColumn())); if (GridStaticCellType.WIDGET.equals(metadata.getType()) && metadata.getWidget().isAttached()) { @@ -1642,8 +2390,7 @@ public class Grid<T> extends ResizeComposite implements * <code>true</code> if we refreshing the header, else assumed * the footer */ - private void refreshRowContainer(RowContainer rows, - GridStaticSection<?> section) { + private void refreshRowContainer(RowContainer rows, StaticSection<?> section) { // Add or Remove rows on demand int rowDiff = section.getVisibleRowCount() - rows.getRowCount(); @@ -1861,19 +2608,288 @@ public class Grid<T> extends ResizeComposite implements * * @return the header */ - public GridHeader getHeader() { + protected Header getHeader() { return header; } /** + * Gets the header row at given index. + * + * @param rowIndex + * 0 based index for row. Counted from top to bottom + * @return header row at given index + * @throws IllegalArgumentException + * if no row exists at given index + */ + public HeaderRow getHeaderRow(int rowIndex) { + return header.getRow(rowIndex); + } + + /** + * Inserts a new row at the given position to the header section. Shifts the + * row currently at that position and any subsequent rows down (adds one to + * their indices). + * + * @param index + * the position at which to insert the row + * @return the new row + * + * @throws IllegalArgumentException + * if the index is less than 0 or greater than row count + * @see #appendHeaderRow() + * @see #prependHeaderRow() + * @see #removeHeaderRow(HeaderRow) + * @see #removeHeaderRow(int) + */ + public HeaderRow addHeaderRowAt(int index) { + return header.addRowAt(index); + } + + /** + * Adds a new row at the bottom of the header section. + * + * @return the new row + * @see #prependHeaderRow() + * @see #addHeaderRowAt(int) + * @see #removeHeaderRow(HeaderRow) + * @see #removeHeaderRow(int) + */ + public HeaderRow appendHeaderRow() { + return header.appendRow(); + } + + /** + * Returns the current default row of the header section. The default row is + * a special header row providing a user interface for sorting columns. + * Setting a header text for column updates cells in the default header. + * + * @return the default row or null if no default row set + */ + public HeaderRow getDefaultHeaderRow() { + return header.getDefaultRow(); + } + + /** + * Gets the row count for the header section. + * + * @return row count + */ + public int getHeaderRowCount() { + return header.getRowCount(); + } + + /** + * Adds a new row at the top of the header section. + * + * @return the new row + * @see #appendHeaderRow() + * @see #addHeaderRowAt(int) + * @see #removeHeaderRow(HeaderRow) + * @see #removeHeaderRow(int) + */ + public HeaderRow prependHeaderRow() { + return header.prependRow(); + } + + /** + * Removes the given row from the header section. + * + * @param row + * the row to be removed + * + * @throws IllegalArgumentException + * if the row does not exist in this section + * @see #removeHeaderRow(int) + * @see #addHeaderRowAt(int) + * @see #appendHeaderRow() + * @see #prependHeaderRow() + */ + public void removeHeaderRow(HeaderRow row) { + header.removeRow(row); + } + + /** + * Removes the row at the given position from the header section. + * + * @param index + * the position of the row + * + * @throws IllegalArgumentException + * if no row exists at given index + * @see #removeHeaderRow(HeaderRow) + * @see #addHeaderRowAt(int) + * @see #appendHeaderRow() + * @see #prependHeaderRow() + */ + public void removeHeaderRow(int rowIndex) { + header.removeRow(rowIndex); + } + + /** + * Sets the default row of the header. The default row is a special header + * row providing a user interface for sorting columns. + * + * @param row + * the new default row, or null for no default row + * + * @throws IllegalArgumentException + * header does not contain the row + */ + public void setDefaultHeaderRow(HeaderRow row) { + header.setDefaultRow(row); + } + + /** + * Sets the visibility of the header section. + * + * @param visible + * true to show header section, false to hide + */ + public void setHeaderVisible(boolean visible) { + header.setVisible(visible); + } + + /** + * Returns the visibility of the header section. + * + * @return true if visible, false otherwise. + */ + public boolean isHeaderVisible() { + return header.isVisible(); + } + + /* Grid Footers */ + + /** * Returns the footer section of this grid. The default footer is empty. * * @return the footer */ - public GridFooter getFooter() { + protected Footer getFooter() { return footer; } + /** + * Gets the footer row at given index. + * + * @param rowIndex + * 0 based index for row. Counted from top to bottom + * @return footer row at given index + * @throws IllegalArgumentException + * if no row exists at given index + */ + public FooterRow getFooterRow(int rowIndex) { + return footer.getRow(rowIndex); + } + + /** + * Inserts a new row at the given position to the footer section. Shifts the + * row currently at that position and any subsequent rows down (adds one to + * their indices). + * + * @param index + * the position at which to insert the row + * @return the new row + * + * @throws IllegalArgumentException + * if the index is less than 0 or greater than row count + * @see #appendFooterRow() + * @see #prependFooterRow() + * @see #removeFooterRow(FooterRow) + * @see #removeFooterRow(int) + */ + public FooterRow addFooterRowAt(int index) { + return footer.addRowAt(index); + } + + /** + * Adds a new row at the bottom of the footer section. + * + * @return the new row + * @see #prependFooterRow() + * @see #addFooterRowAt(int) + * @see #removeFooterRow(FooterRow) + * @see #removeFooterRow(int) + */ + public FooterRow appendFooterRow() { + return footer.appendRow(); + } + + /** + * Gets the row count for the footer. + * + * @return row count + */ + public int getFooterRowCount() { + return footer.getRowCount(); + } + + /** + * Adds a new row at the top of the footer section. + * + * @return the new row + * @see #appendFooterRow() + * @see #addFooterRowAt(int) + * @see #removeFooterRow(FooterRow) + * @see #removeFooterRow(int) + */ + public FooterRow prependFooterRow() { + return footer.prependRow(); + } + + /** + * Removes the given row from the footer section. + * + * @param row + * the row to be removed + * + * @throws IllegalArgumentException + * if the row does not exist in this section + * @see #removeFooterRow(int) + * @see #addFooterRowAt(int) + * @see #appendFooterRow() + * @see #prependFooterRow() + */ + public void removeFooterRow(FooterRow row) { + footer.removeRow(row); + } + + /** + * Removes the row at the given position from the footer section. + * + * @param index + * the position of the row + * + * @throws IllegalArgumentException + * if no row exists at given index + * @see #removeFooterRow(FooterRow) + * @see #addFooterRowAt(int) + * @see #appendFooterRow() + * @see #prependFooterRow() + */ + public void removeFooterRow(int rowIndex) { + footer.removeRow(rowIndex); + } + + /** + * Sets the visibility of the footer section. + * + * @param visible + * true to show footer section, false to hide + */ + public void setFooterVisible(boolean visible) { + footer.setVisible(visible); + } + + /** + * Returns the visibility of the footer section. + * + * @return true if visible, false otherwise. + */ + public boolean isFooterVisible() { + return footer.isVisible(); + } + public EditorRow<T> getEditorRow() { return editorRow; } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index ae78860d79..ff55106175 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -42,7 +42,10 @@ import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractHasComponentsConnector; import com.vaadin.client.ui.SimpleManagedLayout; import com.vaadin.client.ui.grid.Grid.CellStyleGenerator; -import com.vaadin.client.ui.grid.GridHeader.HeaderRow; +import com.vaadin.client.ui.grid.Grid.FooterCell; +import com.vaadin.client.ui.grid.Grid.FooterRow; +import com.vaadin.client.ui.grid.Grid.HeaderCell; +import com.vaadin.client.ui.grid.Grid.HeaderRow; import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; import com.vaadin.client.ui.grid.selection.AbstractRowHandleSelectionModel; import com.vaadin.client.ui.grid.selection.SelectionChangeEvent; @@ -442,13 +445,11 @@ public class GridConnector extends AbstractHasComponentsConnector implements } if (stateChangeEvent.hasPropertyChanged("header")) { - updateSectionFromState(getWidget().getHeader(), - getState().header); + updateHeaderFromState(getState().header); } if (stateChangeEvent.hasPropertyChanged("footer")) { - updateSectionFromState(getWidget().getFooter(), - getState().footer); + updateFooterFromState(getState().footer); } if (stateChangeEvent.hasPropertyChanged("editorRowEnabled")) { @@ -485,21 +486,20 @@ public class GridConnector extends AbstractHasComponentsConnector implements return true; } - private void updateSectionFromState(GridStaticSection<?> section, - GridStaticSectionState state) { + private void updateHeaderFromState(GridStaticSectionState state) { + getWidget().setHeaderVisible(state.visible); - while (section.getRowCount() != 0) { - section.removeRow(0); + while (getWidget().getHeaderRowCount() > 0) { + getWidget().removeHeaderRow(0); } for (RowState rowState : state.rows) { - GridStaticSection.StaticRow<?> row = section.appendRow(); + HeaderRow row = getWidget().appendHeaderRow(); for (CellState cellState : rowState.cells) { CustomGridColumn column = columnIdToColumn .get(cellState.columnId); - GridStaticSection.StaticCell cell = row.getCell(column); - updateStaticCellFromState(cell, cellState); + updateHeaderCellFromState(row.getCell(column), cellState); } for (Set<String> group : rowState.cellGroups.keySet()) { @@ -513,23 +513,71 @@ public class GridConnector extends AbstractHasComponentsConnector implements } // Set state to be the same as first in group. - updateStaticCellFromState(row.join(columns), cellState); + updateHeaderCellFromState(row.join(columns), cellState); } - if (section instanceof GridHeader && rowState.defaultRow) { - ((GridHeader) section).setDefaultRow((HeaderRow) row); + if (rowState.defaultRow) { + getWidget().setDefaultHeaderRow(row); } row.setStyleName(rowState.styleName); } + } + + private void updateHeaderCellFromState(HeaderCell cell, CellState cellState) { + switch (cellState.type) { + case TEXT: + cell.setText(cellState.text); + break; + case HTML: + cell.setHtml(cellState.html); + break; + case WIDGET: + ComponentConnector connector = (ComponentConnector) cellState.connector; + cell.setWidget(connector.getWidget()); + break; + default: + throw new IllegalStateException("unexpected cell type: " + + cellState.type); + } + cell.setStyleName(cellState.styleName); + } - section.setVisible(state.visible); + private void updateFooterFromState(GridStaticSectionState state) { + getWidget().setFooterVisible(state.visible); - section.requestSectionRefresh(); + while (getWidget().getFooterRowCount() > 0) { + getWidget().removeFooterRow(0); + } + + for (RowState rowState : state.rows) { + FooterRow row = getWidget().appendFooterRow(); + + for (CellState cellState : rowState.cells) { + CustomGridColumn column = columnIdToColumn + .get(cellState.columnId); + updateFooterCellFromState(row.getCell(column), cellState); + } + + for (Set<String> group : rowState.cellGroups.keySet()) { + GridColumn<?, ?>[] columns = new GridColumn<?, ?>[group.size()]; + CellState cellState = rowState.cellGroups.get(group); + + int i = 0; + for (String columnId : group) { + columns[i] = columnIdToColumn.get(columnId); + i++; + } + + // Set state to be the same as first in group. + updateFooterCellFromState(row.join(columns), cellState); + } + + row.setStyleName(rowState.styleName); + } } - private void updateStaticCellFromState(GridStaticSection.StaticCell cell, - CellState cellState) { + private void updateFooterCellFromState(FooterCell cell, CellState cellState) { switch (cellState.type) { case TEXT: cell.setText(cellState.text); diff --git a/client/src/com/vaadin/client/ui/grid/GridFooter.java b/client/src/com/vaadin/client/ui/grid/GridFooter.java deleted file mode 100644 index e798139b9a..0000000000 --- a/client/src/com/vaadin/client/ui/grid/GridFooter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2000-2014 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 com.google.gwt.core.client.Scheduler; - -/** - * Represents the footer section of a Grid. The footer is always empty. - * - * @since - * @author Vaadin Ltd - */ -public class GridFooter extends GridStaticSection<GridFooter.FooterRow> { - - /** - * A single row in a grid Footer section. - * - */ - public class FooterRow extends GridStaticSection.StaticRow<FooterCell> { - - @Override - protected FooterCell createCell() { - return new FooterCell(); - } - } - - /** - * A single cell in a grid Footer row. Has a textual caption. - * - */ - public class FooterCell extends GridStaticSection.StaticCell { - } - - private boolean markAsDirty = false; - - @Override - protected FooterRow createRow() { - return new FooterRow(); - } - - @Override - protected void requestSectionRefresh() { - markAsDirty = true; - - /* - * Defer the refresh so if we multiple times call refreshSection() (for - * example when updating cell values) we only get one actual refresh in - * the end. - */ - Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() { - - @Override - public void execute() { - if (markAsDirty) { - markAsDirty = false; - getGrid().refreshFooter(); - } - } - }); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/GridHeader.java b/client/src/com/vaadin/client/ui/grid/GridHeader.java deleted file mode 100644 index 2867ae4d1c..0000000000 --- a/client/src/com/vaadin/client/ui/grid/GridHeader.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2000-2014 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.Arrays; -import java.util.Collection; - -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.dom.client.BrowserEvents; - -/** - * Represents the header section of a Grid. A header consists of a single header - * row containing a header cell for each column. Each cell has a simple textual - * caption. - * - * @since - * @author Vaadin Ltd - */ -public class GridHeader extends GridStaticSection<GridHeader.HeaderRow> { - - /** - * A single row in a grid header section. - * - */ - public class HeaderRow extends GridStaticSection.StaticRow<HeaderCell> { - - private boolean isDefault = false; - - protected void setDefault(boolean isDefault) { - this.isDefault = isDefault; - } - - public boolean isDefault() { - return isDefault; - } - - @Override - protected HeaderCell createCell() { - return new HeaderCell(); - } - } - - /** - * A single cell in a grid header row. Has a textual caption. - * - */ - public class HeaderCell extends GridStaticSection.StaticCell { - } - - private HeaderRow defaultRow; - - private boolean markAsDirty = false; - - @Override - public void removeRow(int index) { - HeaderRow removedRow = getRow(index); - super.removeRow(index); - if (removedRow == defaultRow) { - setDefaultRow(null); - } - } - - /** - * Sets the default row of this header. The default row is a special header - * row providing a user interface for sorting columns. - * - * @param row - * the new default row, or null for no default row - * - * @throws IllegalArgumentException - * this header does not contain the row - */ - public void setDefaultRow(HeaderRow row) { - if (row == defaultRow) { - return; - } - if (row != null && !getRows().contains(row)) { - throw new IllegalArgumentException( - "Cannot set a default row that does not exist in the container"); - } - if (defaultRow != null) { - defaultRow.setDefault(false); - } - if (row != null) { - row.setDefault(true); - } - defaultRow = row; - requestSectionRefresh(); - } - - /** - * Returns the current default row of this header. The default row is a - * special header row providing a user interface for sorting columns. - * - * @return the default row or null if no default row set - */ - public HeaderRow getDefaultRow() { - return defaultRow; - } - - @Override - protected HeaderRow createRow() { - return new HeaderRow(); - } - - @Override - protected void requestSectionRefresh() { - markAsDirty = true; - - /* - * Defer the refresh so if we multiple times call refreshSection() (for - * example when updating cell values) we only get one actual refresh in - * the end. - */ - Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() { - - @Override - public void execute() { - if (markAsDirty) { - markAsDirty = false; - getGrid().refreshHeader(); - } - } - }); - } - - /** - * Returns the events consumed by the header - * - * @return a collection of BrowserEvents - */ - public Collection<String> getConsumedEvents() { - return Arrays.asList(BrowserEvents.TOUCHSTART, BrowserEvents.TOUCHMOVE, - BrowserEvents.TOUCHEND, BrowserEvents.TOUCHCANCEL, - BrowserEvents.CLICK); - } -} diff --git a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java deleted file mode 100644 index 228ff73894..0000000000 --- a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright 2000-2014 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.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.shared.ui.grid.GridStaticCellType; - -/** - * Abstract base class for Grid header and footer sections. - * - * @since - * @author Vaadin Ltd - * @param <ROWTYPE> - * the type of the rows in the section - */ -abstract class GridStaticSection<ROWTYPE extends GridStaticSection.StaticRow<?>> { - - /** - * A header or footer cell. Has a simple textual caption. - * - */ - static class StaticCell { - - private Object content = null; - - private int colspan = 1; - - private GridStaticSection<?> section; - - private GridStaticCellType type = GridStaticCellType.TEXT; - - private String styleName = null; - - /** - * Sets the text displayed in this cell. - * - * @param text - * a plain text caption - */ - public void setText(String text) { - this.content = text; - this.type = GridStaticCellType.TEXT; - section.requestSectionRefresh(); - } - - /** - * Returns the text displayed in this cell. - * - * @return the plain text caption - */ - public String getText() { - if (type != GridStaticCellType.TEXT) { - throw new IllegalStateException( - "Cannot fetch Text from a cell with type " + type); - } - return (String) content; - } - - protected GridStaticSection<?> getSection() { - assert section != null; - return section; - } - - protected void setSection(GridStaticSection<?> section) { - this.section = section; - } - - /** - * Returns the amount of columns the cell spans. By default is 1. - * - * @return The amount of columns the cell spans. - */ - public int getColspan() { - return colspan; - } - - /** - * Sets the amount of columns the cell spans. Must be more or equal to - * 1. By default is 1. - * - * @param colspan - * the colspan to set - */ - public void setColspan(int colspan) { - if (colspan < 1) { - throw new IllegalArgumentException( - "Colspan cannot be less than 1"); - } - - this.colspan = colspan; - section.requestSectionRefresh(); - } - - /** - * Returns the html inside the cell. - * - * @throws IllegalStateException - * if trying to retrive HTML from a cell with a type other - * than {@link Type#HTML}. - * @return the html content of the cell. - */ - public String getHtml() { - if (type != GridStaticCellType.HTML) { - throw new IllegalStateException( - "Cannot fetch HTML from a cell with type " + type); - } - return (String) content; - } - - /** - * Sets the content of the cell to the provided html. All previous - * content is discarded and the cell type is set to {@link Type#HTML}. - * - * @param html - * The html content of the cell - */ - public void setHtml(String html) { - this.content = html; - this.type = GridStaticCellType.HTML; - section.requestSectionRefresh(); - } - - /** - * Returns the widget in the cell. - * - * @throws IllegalStateException - * if the cell is not {@link Type#WIDGET} - * - * @return the widget in the cell - */ - public Widget getWidget() { - if (type != GridStaticCellType.WIDGET) { - throw new IllegalStateException( - "Cannot fetch Widget from a cell with type " + type); - } - return (Widget) content; - } - - /** - * Set widget as the content of the cell. The type of the cell becomes - * {@link Type#WIDGET}. All previous content is discarded. - * - * @param widget - * The widget to add to the cell. Should not be previously - * attached anywhere (widget.getParent == null). - */ - public void setWidget(Widget widget) { - this.content = widget; - this.type = GridStaticCellType.WIDGET; - section.requestSectionRefresh(); - } - - /** - * Returns the type of the cell. - * - * @return the type of content the cell contains. - */ - public GridStaticCellType getType() { - return type; - } - - /** - * Returns the custom style name for this cell. - * - * @return the style name or null if no style name has been set - */ - public String getStyleName() { - return styleName; - } - - /** - * Sets a custom style name for this cell. - * - * @param styleName - * the style name to set or null to not use any style name - */ - public void setStyleName(String styleName) { - this.styleName = styleName; - section.requestSectionRefresh(); - - } - - } - - /** - * Abstract base class for Grid header and footer rows. - * - * @param <CELLTYPE> - * the type of the cells in the row - */ - abstract static class StaticRow<CELLTYPE extends StaticCell> { - - private Map<GridColumn<?, ?>, CELLTYPE> cells = new HashMap<GridColumn<?, ?>, CELLTYPE>(); - - private GridStaticSection<?> section; - - /** - * Map from set of spanned columns to cell meta data. - */ - private Map<Set<GridColumn<?, ?>>, CELLTYPE> cellGroups = new HashMap<Set<GridColumn<?, ?>>, CELLTYPE>(); - - /** - * A custom style name for the row or null if none is set. - */ - private String styleName = null; - - /** - * Returns the cell on given GridColumn. If the column is merged - * returned cell is the cell for the whole group. - * - * @param column - * the column in grid - * @return the cell on given column, merged cell for merged columns, - * null if not found - */ - public CELLTYPE getCell(GridColumn<?, ?> column) { - Set<GridColumn<?, ?>> cellGroup = getCellGroupForColumn(column); - if (cellGroup != null) { - return cellGroups.get(cellGroup); - } - return cells.get(column); - } - - /** - * Merges columns cells in a row - * - * @param columns - * the columns which header should be merged - * @return the remaining visible cell after the merge, or the cell on - * first column if all are hidden - */ - public CELLTYPE join(GridColumn<?, ?>... columns) { - if (columns.length <= 1) { - throw new IllegalArgumentException( - "You can't merge less than 2 columns together."); - } - - HashSet<GridColumn<?, ?>> columnGroup = new HashSet<GridColumn<?, ?>>(); - for (GridColumn<?, ?> column : columns) { - if (!cells.containsKey(column)) { - throw new IllegalArgumentException( - "Given column does not exists on row " + column); - } else if (getCellGroupForColumn(column) != null) { - throw new IllegalStateException( - "Column is already in a group."); - } - columnGroup.add(column); - } - - CELLTYPE joinedCell = createCell(); - cellGroups.put(columnGroup, joinedCell); - joinedCell.setSection(getSection()); - - calculateColspans(); - - return joinedCell; - } - - /** - * Merges columns cells in a row - * - * @param cells - * The cells to merge. Must be from the same row. - * @return The remaining visible cell after the merge, or the first cell - * if all columns are hidden - */ - public CELLTYPE join(CELLTYPE... cells) { - if (cells.length <= 1) { - throw new IllegalArgumentException( - "You can't merge less than 2 cells together."); - } - - GridColumn<?, ?>[] columns = new GridColumn<?, ?>[cells.length]; - - int j = 0; - for (GridColumn<?, ?> column : this.cells.keySet()) { - CELLTYPE cell = this.cells.get(column); - if (!this.cells.containsValue(cells[j])) { - throw new IllegalArgumentException( - "Given cell does not exists on row"); - } else if (cell.equals(cells[j])) { - columns[j++] = column; - if (j == cells.length) { - break; - } - } - } - - return join(columns); - } - - private Set<GridColumn<?, ?>> getCellGroupForColumn( - GridColumn<?, ?> column) { - for (Set<GridColumn<?, ?>> group : cellGroups.keySet()) { - if (group.contains(column)) { - return group; - } - } - return null; - } - - void calculateColspans() { - - // Reset all cells - for (CELLTYPE cell : this.cells.values()) { - cell.setColspan(1); - } - - List<GridColumn<?, ?>> columnOrder = new ArrayList<GridColumn<?, ?>>( - section.grid.getColumns()); - // Set colspan for grouped cells - for (Set<GridColumn<?, ?>> group : cellGroups.keySet()) { - if (!checkCellGroupAndOrder(columnOrder, group)) { - cellGroups.get(group).setColspan(1); - } else { - int colSpan = group.size(); - cellGroups.get(group).setColspan(colSpan); - } - } - - } - - private boolean checkCellGroupAndOrder( - List<GridColumn<?, ?>> columnOrder, - Set<GridColumn<?, ?>> cellGroup) { - if (!columnOrder.containsAll(cellGroup)) { - return false; - } - - for (int i = 0; i < columnOrder.size(); ++i) { - if (!cellGroup.contains(columnOrder.get(i))) { - continue; - } - - for (int j = 1; j < cellGroup.size(); ++j) { - if (!cellGroup.contains(columnOrder.get(i + j))) { - return false; - } - } - return true; - } - return false; - } - - protected void addCell(GridColumn<?, ?> column) { - CELLTYPE cell = createCell(); - cell.setSection(getSection()); - cells.put(column, cell); - } - - protected void removeCell(GridColumn<?, ?> column) { - cells.remove(column); - } - - protected abstract CELLTYPE createCell(); - - protected GridStaticSection<?> getSection() { - return section; - } - - protected void setSection(GridStaticSection<?> section) { - this.section = section; - } - - /** - * Returns the custom style name for this row. - * - * @return the style name or null if no style name has been set - */ - public String getStyleName() { - return styleName; - } - - /** - * Sets a custom style name for this row. - * - * @param styleName - * the style name to set or null to not use any style name - */ - public void setStyleName(String styleName) { - this.styleName = styleName; - section.requestSectionRefresh(); - } - - } - - private Grid<?> grid; - - private List<ROWTYPE> rows = new ArrayList<ROWTYPE>(); - - private boolean visible = true; - - /** - * Creates and returns a new instance of the row type. - * - * @return the created row - */ - protected abstract ROWTYPE createRow(); - - /** - * Informs the grid that this section should be re-rendered. - * <p> - * <b>Note</b> that re-render means calling update() on each cell, - * preAttach()/postAttach()/preDetach()/postDetach() is not called as the - * cells are not removed from the DOM. - */ - protected abstract void requestSectionRefresh(); - - /** - * Sets the visibility of the whole section. - * - * @param visible - * true to show this section, false to hide - */ - public void setVisible(boolean visible) { - this.visible = visible; - requestSectionRefresh(); - } - - /** - * Returns the visibility of this section. - * - * @return true if visible, false otherwise. - */ - public boolean isVisible() { - return visible; - } - - /** - * Inserts a new row at the given position. Shifts the row currently at that - * position and any subsequent rows down (adds one to their indices). - * - * @param index - * the position at which to insert the row - * @return the new row - * - * @throws IndexOutOfBoundsException - * if the index is out of bounds - * @see #appendRow() - * @see #prependRow() - * @see #removeRow(int) - * @see #removeRow(StaticRow) - */ - public ROWTYPE addRow(int index) { - ROWTYPE row = createRow(); - row.setSection(this); - for (int i = 0; i < getGrid().getColumnCount(); ++i) { - row.addCell(grid.getColumn(i)); - } - rows.add(index, row); - - requestSectionRefresh(); - return row; - } - - /** - * Adds a new row at the top of this section. - * - * @return the new row - * @see #appendRow() - * @see #addRow(int) - * @see #removeRow(int) - * @see #removeRow(StaticRow) - */ - public ROWTYPE prependRow() { - return addRow(0); - } - - /** - * Adds a new row at the bottom of this section. - * - * @return the new row - * @see #prependRow() - * @see #addRow(int) - * @see #removeRow(int) - * @see #removeRow(StaticRow) - */ - public ROWTYPE appendRow() { - return addRow(rows.size()); - } - - /** - * Removes the row at the given position. - * - * @param index - * the position of the row - * - * @throws IndexOutOfBoundsException - * if the index is out of bounds - * @see #addRow(int) - * @see #appendRow() - * @see #prependRow() - * @see #removeRow(StaticRow) - */ - public void removeRow(int index) { - rows.remove(index); - requestSectionRefresh(); - } - - /** - * Removes the given row from the section. - * - * @param row - * the row to be removed - * - * @throws IllegalArgumentException - * if the row does not exist in this section - * @see #addRow(int) - * @see #appendRow() - * @see #prependRow() - * @see #removeRow(int) - */ - public void removeRow(ROWTYPE row) { - try { - removeRow(rows.indexOf(row)); - } catch (IndexOutOfBoundsException e) { - throw new IllegalArgumentException( - "Section does not contain the given row"); - } - } - - /** - * Returns the row at the given position. - * - * @param index - * the position of the row - * @return the row with the given index - * - * @throws IndexOutOfBoundsException - * if the index is out of bounds - */ - public ROWTYPE getRow(int index) { - try { - return rows.get(index); - } catch (IndexOutOfBoundsException e) { - throw new IllegalArgumentException("Row with index " + index - + " does not exist"); - } - } - - /** - * Returns the number of rows in this section. - * - * @return the number of rows - */ - public int getRowCount() { - return rows.size(); - } - - protected List<ROWTYPE> getRows() { - return rows; - } - - protected int getVisibleRowCount() { - return isVisible() ? getRowCount() : 0; - } - - protected void addColumn(GridColumn<?, ?> column) { - for (ROWTYPE row : rows) { - row.addCell(column); - } - } - - protected void removeColumn(GridColumn<?, ?> column) { - for (ROWTYPE row : rows) { - row.removeCell(column); - } - } - - protected void setGrid(Grid<?> grid) { - this.grid = grid; - } - - protected Grid<?> getGrid() { - assert grid != null; - return grid; - } -} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java index ce899be8f9..cdf674e570 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeaturesWidget.java @@ -42,12 +42,10 @@ import com.vaadin.client.ui.grid.EditorRowHandler; import com.vaadin.client.ui.grid.FlyweightCell; import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.Grid.CellStyleGenerator; +import com.vaadin.client.ui.grid.Grid.FooterRow; +import com.vaadin.client.ui.grid.Grid.HeaderRow; import com.vaadin.client.ui.grid.Grid.SelectionMode; import com.vaadin.client.ui.grid.GridColumn; -import com.vaadin.client.ui.grid.GridFooter; -import com.vaadin.client.ui.grid.GridFooter.FooterRow; -import com.vaadin.client.ui.grid.GridHeader; -import com.vaadin.client.ui.grid.GridHeader.HeaderRow; import com.vaadin.client.ui.grid.Renderer; import com.vaadin.client.ui.grid.datasources.ListDataSource; import com.vaadin.client.ui.grid.datasources.ListSorter; @@ -343,7 +341,7 @@ public class GridBasicClientFeaturesWidget extends column.setHeaderText("Header (0," + c + ")"); } - HeaderRow row = grid.getHeader().getDefaultRow(); + HeaderRow row = grid.getDefaultHeaderRow(); for (int i = 0; i < col; ++i) { String caption = "Header (0," + i + ")"; GridColumn<?, ?> column = grid.getColumn(i); @@ -628,7 +626,7 @@ public class GridBasicClientFeaturesWidget extends addMenuCommand("HTML Header", new ScheduledCommand() { @Override public void execute() { - grid.getHeader().getRow(0).getCell(column) + grid.getHeaderRow(0).getCell(column) .setHtml("<b>HTML Header</b>"); } }, "Component", "Columns", "Column " + i, "Header Type"); @@ -643,8 +641,7 @@ public class GridBasicClientFeaturesWidget extends button.setText("Clicked"); } }); - grid.getHeader().getRow(0).getCell(column) - .setWidget(button); + grid.getHeaderRow(0).getCell(column).setWidget(button); } }, "Component", "Columns", "Column " + i, "Header Type"); @@ -652,14 +649,13 @@ public class GridBasicClientFeaturesWidget extends addMenuCommand("Text Footer", new ScheduledCommand() { @Override public void execute() { - grid.getFooter().getRow(0).getCell(column) - .setText("Text Footer"); + grid.getFooterRow(0).getCell(column).setText("Text Footer"); } }, "Component", "Columns", "Column " + i, "Footer Type"); addMenuCommand("HTML Footer", new ScheduledCommand() { @Override public void execute() { - grid.getFooter().getRow(0).getCell(column) + grid.getFooterRow(0).getCell(column) .setHtml("<b>HTML Footer</b>"); } }, "Component", "Columns", "Column " + i, "Footer Type"); @@ -674,8 +670,7 @@ public class GridBasicClientFeaturesWidget extends button.setText("Clicked"); } }); - grid.getFooter().getRow(0).getCell(column) - .setWidget(button); + grid.getFooterRow(0).getCell(column).setWidget(button); } }, "Component", "Columns", "Column " + i, "Footer Type"); } @@ -719,66 +714,65 @@ public class GridBasicClientFeaturesWidget extends } private void createHeaderMenu() { - final GridHeader header = grid.getHeader(); final String[] menuPath = { "Component", "Header" }; addMenuCommand("Visible", new ScheduledCommand() { @Override public void execute() { - header.setVisible(!header.isVisible()); + grid.setHeaderVisible(!grid.isHeaderVisible()); } }, menuPath); addMenuCommand("Top", new ScheduledCommand() { @Override public void execute() { - header.setDefaultRow(header.getRow(0)); + grid.setDefaultHeaderRow(grid.getHeaderRow(0)); } }, "Component", "Header", "Default row"); addMenuCommand("Bottom", new ScheduledCommand() { @Override public void execute() { - header.setDefaultRow(header.getRow(header.getRowCount() - 1)); + grid.setDefaultHeaderRow(grid.getHeaderRow(grid + .getHeaderRowCount() - 1)); } }, "Component", "Header", "Default row"); addMenuCommand("Unset", new ScheduledCommand() { @Override public void execute() { - header.setDefaultRow(null); + grid.setDefaultHeaderRow(null); } }, "Component", "Header", "Default row"); addMenuCommand("Prepend row", new ScheduledCommand() { @Override public void execute() { - configureHeaderRow(header.prependRow()); + configureHeaderRow(grid.prependHeaderRow()); } }, menuPath); addMenuCommand("Append row", new ScheduledCommand() { @Override public void execute() { - configureHeaderRow(header.appendRow()); + configureHeaderRow(grid.appendHeaderRow()); } }, menuPath); addMenuCommand("Remove top row", new ScheduledCommand() { @Override public void execute() { - header.removeRow(0); + grid.removeHeaderRow(0); } }, menuPath); addMenuCommand("Remove bottom row", new ScheduledCommand() { @Override public void execute() { - header.removeRow(header.getRowCount() - 1); + grid.removeHeaderRow(grid.getHeaderRowCount() - 1); } }, menuPath); } private void configureHeaderRow(final HeaderRow row) { - final GridHeader header = grid.getHeader(); setHeaderTexts(row); - String rowTitle = "Row " + header.getRowCount(); + String rowTitle = "Row " + grid.getHeaderRowCount(); final String[] menuPath = { "Component", "Header", rowTitle }; addMenuCommand("Join column cells 0, 1", new ScheduledCommand() { @@ -828,39 +822,38 @@ public class GridBasicClientFeaturesWidget extends } private void createFooterMenu() { - final GridFooter footer = grid.getFooter(); final String[] menuPath = { "Component", "Footer" }; addMenuCommand("Visible", new ScheduledCommand() { @Override public void execute() { - footer.setVisible(!footer.isVisible()); + grid.setFooterVisible(!grid.isFooterVisible()); } }, menuPath); addMenuCommand("Prepend row", new ScheduledCommand() { @Override public void execute() { - configureFooterRow(footer.prependRow()); + configureFooterRow(grid.prependFooterRow()); } }, menuPath); addMenuCommand("Append row", new ScheduledCommand() { @Override public void execute() { - configureFooterRow(footer.appendRow()); + configureFooterRow(grid.appendFooterRow()); } }, menuPath); addMenuCommand("Remove top row", new ScheduledCommand() { @Override public void execute() { - footer.removeRow(0); + grid.removeFooterRow(0); } }, menuPath); addMenuCommand("Remove bottom row", new ScheduledCommand() { @Override public void execute() { - assert footer.getRowCount() > 0; - footer.removeRow(footer.getRowCount() - 1); + assert grid.getFooterRowCount() > 0; + grid.removeFooterRow(grid.getFooterRowCount() - 1); } }, menuPath); } @@ -912,9 +905,8 @@ public class GridBasicClientFeaturesWidget extends } private void configureFooterRow(final FooterRow row) { - final GridFooter footer = grid.getFooter(); setFooterTexts(row); - String rowTitle = "Row " + footer.getRowCount(); + String rowTitle = "Row " + grid.getFooterRowCount(); final String[] menuPath = { "Component", "Footer", rowTitle }; addMenuCommand("Join column cells 0, 1", new ScheduledCommand() { diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java index bde16b817e..3290c67467 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java @@ -145,12 +145,12 @@ public class GridClientColumnRendererConnector extends // Add a column to display the data in GridColumn<String, String> c = createColumnWithRenderer(Renderers.TEXT_RENDERER); grid.addColumn(c); - grid.getHeader().getDefaultRow().getCell(c).setText("Column 1"); + grid.getDefaultHeaderRow().getCell(c).setText("Column 1"); // Add another column with a custom complex renderer c = createColumnWithRenderer(Renderers.CPLX_RENDERER); grid.addColumn(c); - grid.getHeader().getDefaultRow().getCell(c).setText("Column 2"); + grid.getDefaultHeaderRow().getCell(c).setText("Column 2"); // Add method for testing sort event firing grid.addSortHandler(new SortHandler<String>() { @@ -161,9 +161,8 @@ public class GridClientColumnRendererConnector extends String text = "Client-side sort event received<br>" + "Columns: " + event.getOrder().size() + ", order: "; for (SortOrder order : event.getOrder()) { - String columnHeader = getWidget().getHeader() - .getDefaultRow().getCell(order.getColumn()) - .getText(); + String columnHeader = getWidget().getDefaultHeaderRow() + .getCell(order.getColumn()).getText(); text += columnHeader + ": " + order.getDirection().toString(); } @@ -189,8 +188,7 @@ public class GridClientColumnRendererConnector extends getWidget().addColumn(column); getWidget() - .getHeader() - .getDefaultRow() + .getDefaultHeaderRow() .getCell(column) .setText( "Column " |