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(" "); 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