1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.google.gwt.sample.dynatable.client;
17 
18 import com.google.gwt.event.dom.client.ClickEvent;
19 import com.google.gwt.event.dom.client.ClickHandler;
20 import com.google.gwt.sample.dynatable.client.DynaTableDataProvider.RowDataAcceptor;
21 import com.google.gwt.user.client.rpc.InvocationException;
22 import com.google.gwt.user.client.ui.Button;
23 import com.google.gwt.user.client.ui.Composite;
24 import com.google.gwt.user.client.ui.DialogBox;
25 import com.google.gwt.user.client.ui.DockPanel;
26 import com.google.gwt.user.client.ui.Grid;
27 import com.google.gwt.user.client.ui.HTML;
28 import com.google.gwt.user.client.ui.HasAlignment;
29 import com.google.gwt.user.client.ui.HorizontalPanel;
30 import com.google.gwt.user.client.ui.VerticalPanel;
31 
32 /**
33  * A composite Widget that implements the main interface for the dynamic table,
34  * including the data table, status indicators, and paging buttons.
35  */
36 public class DynaTableWidget extends Composite {
37 
38   /**
39    * A dialog box for displaying an error.
40    */
41   private static class ErrorDialog extends DialogBox implements ClickHandler {
42     private HTML body = new HTML("");
43 
ErrorDialog()44     public ErrorDialog() {
45       setStylePrimaryName("DynaTable-ErrorDialog");
46       Button closeButton = new Button("Close", this);
47       VerticalPanel panel = new VerticalPanel();
48       panel.setSpacing(4);
49       panel.add(body);
50       panel.add(closeButton);
51       panel.setCellHorizontalAlignment(closeButton, VerticalPanel.ALIGN_RIGHT);
52       setWidget(panel);
53     }
54 
getBody()55     public String getBody() {
56       return body.getHTML();
57     }
58 
onClick(ClickEvent event)59     public void onClick(ClickEvent event) {
60       hide();
61     }
62 
setBody(String html)63     public void setBody(String html) {
64       body.setHTML(html);
65     }
66   }
67 
68   private class NavBar extends Composite implements ClickHandler {
69 
70     public final DockPanel bar = new DockPanel();
71     public final Button gotoFirst = new Button("<<", this);
72     public final Button gotoNext = new Button(">", this);
73     public final Button gotoPrev = new Button("<", this);
74     public final HTML status = new HTML();
75 
NavBar()76     public NavBar() {
77       initWidget(bar);
78       bar.setStyleName("navbar");
79       status.setStyleName("status");
80 
81       HorizontalPanel buttons = new HorizontalPanel();
82       buttons.add(gotoFirst);
83       buttons.add(gotoPrev);
84       buttons.add(gotoNext);
85       bar.add(buttons, DockPanel.EAST);
86       bar.setCellHorizontalAlignment(buttons, DockPanel.ALIGN_RIGHT);
87       bar.add(status, DockPanel.CENTER);
88       bar.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
89       bar.setCellHorizontalAlignment(status, HasAlignment.ALIGN_RIGHT);
90       bar.setCellVerticalAlignment(status, HasAlignment.ALIGN_MIDDLE);
91       bar.setCellWidth(status, "100%");
92 
93       // Initialize prev & first button to disabled.
94       //
95       gotoPrev.setEnabled(false);
96       gotoFirst.setEnabled(false);
97     }
98 
onClick(ClickEvent event)99     public void onClick(ClickEvent event) {
100       Object source = event.getSource();
101       if (source == gotoNext) {
102         startRow += getDataRowCount();
103         refresh();
104       } else if (source == gotoPrev) {
105         startRow -= getDataRowCount();
106         if (startRow < 0) {
107           startRow = 0;
108         }
109         refresh();
110       } else if (source == gotoFirst) {
111         startRow = 0;
112         refresh();
113       }
114     }
115   }
116 
117   private class RowDataAcceptorImpl implements RowDataAcceptor {
accept(int startRow, String[][] data)118     public void accept(int startRow, String[][] data) {
119 
120       int destRowCount = getDataRowCount();
121       int destColCount = grid.getCellCount(0);
122       assert (data.length <= destRowCount) : "Too many rows";
123 
124       int srcRowIndex = 0;
125       int srcRowCount = data.length;
126       int destRowIndex = 1; // skip navbar row
127       for (; srcRowIndex < srcRowCount; ++srcRowIndex, ++destRowIndex) {
128         String[] srcRowData = data[srcRowIndex];
129         assert (srcRowData.length == destColCount) : " Column count mismatch";
130         for (int srcColIndex = 0; srcColIndex < destColCount; ++srcColIndex) {
131           String cellHTML = srcRowData[srcColIndex];
132           grid.setText(destRowIndex, srcColIndex, cellHTML);
133         }
134       }
135 
136       // Clear remaining table rows.
137       //
138       boolean isLastPage = false;
139       for (; destRowIndex < destRowCount + 1; ++destRowIndex) {
140         isLastPage = true;
141         for (int destColIndex = 0; destColIndex < destColCount; ++destColIndex) {
142           grid.clearCell(destRowIndex, destColIndex);
143         }
144       }
145 
146       // Synchronize the nav buttons.
147       navbar.gotoNext.setEnabled(!isLastPage);
148       navbar.gotoFirst.setEnabled(startRow > 0);
149       navbar.gotoPrev.setEnabled(startRow > 0);
150 
151       // Update the status message.
152       //
153       setStatusText((startRow + 1) + " - " + (startRow + srcRowCount));
154     }
155 
failed(Throwable caught)156     public void failed(Throwable caught) {
157       setStatusText("Error");
158       if (errorDialog == null) {
159         errorDialog = new ErrorDialog();
160       }
161       if (caught instanceof InvocationException) {
162         errorDialog.setText("An RPC server could not be reached");
163         errorDialog.setBody(NO_CONNECTION_MESSAGE);
164       } else {
165         errorDialog.setText("Unexcepted Error processing remote call");
166         errorDialog.setBody(caught.getMessage());
167       }
168       errorDialog.center();
169     }
170   }
171 
172   private static final String NO_CONNECTION_MESSAGE = "<p>The DynaTable example uses a <a "
173       + "href=\"http://www.gwtproject.org/doc/latest/DevGuideServerCommunication.html"
174       + "#DevGuideRemoteProcedureCalls\" target=\"_blank\">Remote Procedure Call</a> "
175       + "(RPC) to request data from the server.  In order for the RPC to "
176       + "successfully return data, the server component must be available.</p>"
177       + "<p>If you are running this demo from compiled code, the server "
178       + "component may not be available to respond to the RPC requests from "
179       + "DynaTable.  Try running DynaTable in development mode to see the demo "
180       + "in action.</p> "
181       + "<p>Click on the Remote Procedure Call link above for more information "
182       + "on GWT's RPC infrastructure.";
183 
184   private final RowDataAcceptor acceptor = new RowDataAcceptorImpl();
185 
186   private final Grid grid = new Grid();
187 
188   private final NavBar navbar = new NavBar();
189 
190   private ErrorDialog errorDialog = null;
191 
192   private final DockPanel outer = new DockPanel();
193 
194   private final DynaTableDataProvider provider;
195 
196   private int startRow = 0;
197 
DynaTableWidget(DynaTableDataProvider provider, String[] columns, String[] columnStyles, int rowCount)198   public DynaTableWidget(DynaTableDataProvider provider, String[] columns,
199       String[] columnStyles, int rowCount) {
200 
201     if (columns.length == 0) {
202       throw new IllegalArgumentException(
203           "expecting a positive number of columns");
204     }
205 
206     if (columnStyles != null && columns.length != columnStyles.length) {
207       throw new IllegalArgumentException("expecting as many styles as columns");
208     }
209 
210     this.provider = provider;
211     initWidget(outer);
212     grid.setStyleName("table");
213     outer.add(navbar, DockPanel.NORTH);
214     outer.add(grid, DockPanel.CENTER);
215     initTable(columns, columnStyles, rowCount);
216     setStyleName("DynaTable-DynaTableWidget");
217   }
218 
clearStatusText()219   public void clearStatusText() {
220     navbar.status.setHTML("&nbsp;");
221   }
222 
refresh()223   public void refresh() {
224     // Disable buttons temporarily to stop the user from running off the end.
225     //
226     navbar.gotoFirst.setEnabled(false);
227     navbar.gotoPrev.setEnabled(false);
228     navbar.gotoNext.setEnabled(false);
229 
230     setStatusText("Please wait...");
231     provider.updateRowData(startRow, grid.getRowCount() - 1, acceptor);
232   }
233 
setRowCount(int rows)234   public void setRowCount(int rows) {
235     grid.resizeRows(rows);
236   }
237 
setStatusText(String text)238   public void setStatusText(String text) {
239     navbar.status.setText(text);
240   }
241 
getDataRowCount()242   private int getDataRowCount() {
243     return grid.getRowCount() - 1;
244   }
245 
initTable(String[] columns, String[] columnStyles, int rowCount)246   private void initTable(String[] columns, String[] columnStyles, int rowCount) {
247     // Set up the header row. It's one greater than the number of visible rows.
248     //
249     grid.resize(rowCount + 1, columns.length);
250     for (int i = 0, n = columns.length; i < n; i++) {
251       grid.setText(0, i, columns[i]);
252       if (columnStyles != null) {
253         grid.getCellFormatter().setStyleName(0, i, columnStyles[i] + " header");
254       }
255     }
256   }
257 }
258