2 * Copyright (C) 2011 Openismus GmbH
4 * This file is part of GWT-Glom.
6 * GWT-Glom is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
11 * GWT-Glom is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with GWT-Glom. If not, see <http://www.gnu.org/licenses/>.
20 package org.glom.web.client.ui.list;
22 import java.util.ArrayList;
24 import org.glom.web.client.Utils;
25 import org.glom.web.client.ui.cell.BooleanCell;
26 import org.glom.web.client.ui.cell.NumericCell;
27 import org.glom.web.client.ui.cell.OpenButtonCell;
28 import org.glom.web.client.ui.cell.TextCell;
29 import org.glom.web.shared.DataItem;
30 import org.glom.web.shared.GlomNumericFormat;
31 import org.glom.web.shared.layout.Formatting;
32 import org.glom.web.shared.layout.LayoutGroup;
33 import org.glom.web.shared.layout.LayoutItem;
34 import org.glom.web.shared.layout.LayoutItemField;
35 import org.glom.web.shared.layout.LayoutItemField.GlomFieldType;
37 import com.google.gwt.dom.client.Style.Unit;
38 import com.google.gwt.event.shared.EventBus;
39 import com.google.gwt.i18n.client.NumberFormat;
40 import com.google.gwt.safehtml.shared.SafeHtmlUtils;
41 import com.google.gwt.user.cellview.client.CellTable;
42 import com.google.gwt.user.cellview.client.Column;
43 import com.google.gwt.user.cellview.client.ColumnSortEvent.AsyncHandler;
44 import com.google.gwt.user.cellview.client.SafeHtmlHeader;
45 import com.google.gwt.user.cellview.client.SimplePager;
46 import com.google.gwt.user.client.ui.Composite;
47 import com.google.gwt.user.client.ui.FlowPanel;
48 import com.google.gwt.user.client.ui.HasHorizontalAlignment;
49 import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
50 import com.google.gwt.view.client.AbstractDataProvider;
51 import com.google.gwt.view.client.ProvidesKey;
54 * @author Ben Konrath <ben@bagu.org>
57 public abstract class ListTable extends Composite {
59 private class ListTablePager extends SimplePager {
60 public ListTablePager() {
61 super(SimplePager.TextLocation.CENTER);
65 * A custom version of createText to display the correct number of data row when empty rows have been added to
66 * the CellTable. This method is needed because the row count change event handler
67 * (AbstractPager.handleRowCountChange()) doesn't use the row count that is sent along with the
68 * RowCountChangeEvent.
70 * @see com.google.gwt.user.cellview.client.AbstractPager#handleRowCountChange(int, boolean)
72 * @see com.google.gwt.user.cellview.client.SimplePager#createText()
75 protected String createText() {
76 int numNonEmptyRows = getNumNonEmptyRows();
77 if (numNonEmptyRows < getMinNumVisibleRows()) {
78 NumberFormat formatter = NumberFormat.getFormat("#,###");
79 return formatter.format(1) + "-" + formatter.format(numNonEmptyRows) + " of "
80 + formatter.format(numNonEmptyRows);
82 return super.createText();
87 final private FlowPanel mainPanel = new FlowPanel();
88 final private ListTablePager pager = new ListTablePager();
89 protected String documentID;
90 protected String tableName;
91 protected CellTable<DataItem[]> cellTable;
92 protected EventBus eventBus;
93 Column<DataItem[], String> openButtonColumn;
95 abstract protected AbstractDataProvider<DataItem[]> getDataProvider();
97 @SuppressWarnings("unused")
99 // disable default constructor
102 public ListTable(String documentID) {
103 this.documentID = documentID;
106 public void createCellTable(LayoutGroup layoutGroup, int numVisibleRows, String openButtonLabel,
107 OpenButtonCell openButtonCell) {
108 tableName = layoutGroup.getTableName();
109 ArrayList<LayoutItem> layoutItems = layoutGroup.getItems();
111 final int primaryKeyIndex = layoutGroup.getPrimaryKeyIndex();
112 LayoutItemField primaryKeyLayoutItem = (LayoutItemField) layoutItems.get(primaryKeyIndex);
113 final GlomFieldType primaryKeyFieldType = primaryKeyLayoutItem.getType();
114 ProvidesKey<DataItem[]> keyProvider = new ProvidesKey<DataItem[]>() {
116 public Object getKey(DataItem[] row) {
117 if (row.length == 1 && row[0] == null)
120 return Utils.getTypedDataItem(primaryKeyFieldType, row[primaryKeyIndex]);
124 // create the CellTable with the requested number of rows and the key provider
125 cellTable = new CellTable<DataItem[]>(numVisibleRows, keyProvider);
128 cellTable.setStyleName("data-list");
129 cellTable.getElement().getStyle().setProperty("whiteSpace", "nowrap"); // this prevents the header and row text
132 // add columns to the CellTable and deal with the case of the hidden primary key
133 int numItems = layoutGroup.hasHiddenPrimaryKey() ? layoutItems.size() - 1 : layoutItems.size();
134 for (int i = 0; i < numItems; i++) {
135 LayoutItem layoutItem = layoutItems.get(i);
137 // only add columns for LayoutItemField types
138 if (layoutItem instanceof LayoutItemField) {
139 addColumn((LayoutItemField) layoutItem);
144 // add the navigation buttons as the last column
145 addOpenButtonColumn(openButtonLabel, openButtonCell);
147 // create and set the data provider
148 AbstractDataProvider<DataItem[]> dataProvider = getDataProvider();
149 dataProvider.addDataDisplay(cellTable);
151 // add an AsyncHandler to activate sorting for the data provider
152 cellTable.addColumnSortHandler(new AsyncHandler(cellTable));
154 // pack the widgets into the container
155 pager.setDisplay(cellTable);
156 mainPanel.add(cellTable);
157 mainPanel.add(pager);
159 // initialize composite widget
160 initWidget(mainPanel);
163 private void addColumn(final LayoutItemField layoutItemField) {
164 // Setup the default alignment of the column.
165 HorizontalAlignmentConstant columnAlignment;
166 Formatting formatting = layoutItemField.getFormatting();
167 switch (formatting.getHorizontalAlignment()) {
168 case HORIZONTAL_ALIGNMENT_LEFT:
169 columnAlignment = HasHorizontalAlignment.ALIGN_LEFT;
171 case HORIZONTAL_ALIGNMENT_RIGHT:
172 columnAlignment = HasHorizontalAlignment.ALIGN_RIGHT;
174 case HORIZONTAL_ALIGNMENT_AUTO:
176 columnAlignment = HasHorizontalAlignment.ALIGN_DEFAULT;
180 // create a new column
181 Column<DataItem[], ?> column = null;
182 final int j = cellTable.getColumnCount();
183 switch (layoutItemField.getType()) {
186 column = new Column<DataItem[], Boolean>(new BooleanCell()) {
188 public Boolean getValue(DataItem[] row) {
189 if (row.length == 1 && row[0] == null)
192 return row[j].getBoolean();
195 // override the configured horizontal alignment
196 columnAlignment = HasHorizontalAlignment.ALIGN_CENTER;
200 // create a GWT NumberFormat for the column
201 GlomNumericFormat glomNumericFormat = formatting.getGlomNumericFormat();
202 NumberFormat gwtNumberFormat = Utils.getNumberFormat(glomNumericFormat);
204 // create the actual column
205 column = new Column<DataItem[], Double>(new NumericCell(formatting.getTextFormatColourForeground(),
206 formatting.getTextFormatColourBackground(), gwtNumberFormat,
207 glomNumericFormat.getUseAltForegroundColourForNegatives(), glomNumericFormat.getCurrencyCode())) {
209 public Double getValue(DataItem[] row) {
210 if (row.length == 1 && row[0] == null)
213 return row[j].getNumber();
219 // use a text rendering cell for types we don't know about but log an error
220 // TODO log error here
226 column = new Column<DataItem[], String>(new TextCell(formatting.getTextFormatColourForeground(),
227 formatting.getTextFormatColourBackground())) {
229 public String getValue(DataItem[] row) {
230 if (row.length == 1 && row[0] == null)
233 return row[j].getText();
239 // set column properties and add to cell cellTable
240 column.setHorizontalAlignment(columnAlignment);
241 column.setSortable(true);
242 cellTable.addColumn(column, new SafeHtmlHeader(SafeHtmlUtils.fromString(layoutItemField.getTitle())));
245 private void addOpenButtonColumn(final String openButtonLabel, OpenButtonCell openButtonCell) {
247 openButtonColumn = new Column<DataItem[], String>(openButtonCell) {
249 public String getValue(DataItem[] row) {
250 if (row.length == 1 && row[0] == null)
253 return openButtonLabel;
257 // Firefox, Chrome, and Safari only support the span and width attributes of the col element so we need to set
258 // the alignment with code
259 openButtonColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
261 cellTable.addColumn(openButtonColumn, "");
263 // the style name for the details column is set on the col element
264 cellTable.addColumnStyleName(cellTable.getColumnCount() - 1, "details");
269 * Sets the row count for the pager.
271 public void setRowCount(int rowCount) {
272 cellTable.setRowCount(rowCount);
275 public void hideNavigationButtons() {
276 if (openButtonColumn != null) {
277 cellTable.setColumnWidth(openButtonColumn, 0, Unit.PX);
282 * Gets the minimum number of rows the should be displayed. Empty rows will be added when the query returns fewer
283 * rows than this minimum.
285 * @return The minimum number of rows that should be displayed.
287 public abstract int getMinNumVisibleRows();
289 public abstract int getNumNonEmptyRows();