Changes CustomizedShell to use the new Table widget.

Review at http://gwt-code-reviews.appspot.com/318801

Review by: rjrjr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7896 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java
index d8c359d..310467d 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/DateCell.java
@@ -25,7 +25,11 @@
 public class DateCell extends Cell<Date, Void> {
 
   private final DateTimeFormat format;
-  
+
+  public DateCell() {
+    this(DateTimeFormat.getFullDateFormat());
+  }
+
   public DateCell(DateTimeFormat format) {
     this.format = format;
   }
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/EditTextCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/EditTextCell.java
new file mode 100644
index 0000000..d19822a
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/EditTextCell.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.google.gwt.bikeshed.cells.client;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.KeyCodes;
+
+/**
+ * An editable text cell. Click to edit, escape to cancel, return to commit.
+ */
+public class EditTextCell extends Cell<String, String> {
+
+  @Override
+  public String onBrowserEvent(Element parent, String value, String viewData,
+      NativeEvent event, ValueUpdater<String, String> valueUpdater) {
+    if (viewData != null) {
+      return editEvent(parent, value, viewData, event, valueUpdater);
+    }
+    return nonEditEvent(parent, value, viewData, event, valueUpdater);
+  }
+
+  @Override
+  public void render(String value, String viewData, StringBuilder sb) {
+    if (viewData != null) {
+      sb.append("<input type='text' value='" + viewData + "'></input>");
+    } else {
+      sb.append(value);
+    }
+  }
+
+  protected String edit(Element parent, String value) {
+    setValue(parent, value, value);
+    InputElement input = (InputElement) parent.getFirstChild();
+    input.focus();
+    input.select();
+    return value;
+  }
+
+  private String cancel(Element parent, String value) {
+    setValue(parent, value, null);
+    return null;
+  }
+
+  private String commit(Element parent,
+      ValueUpdater<String, String> valueUpdater) {
+    String value;
+    InputElement input = (InputElement) parent.getFirstChild();
+    value = input.getValue();
+    valueUpdater.update(value, null);
+    return cancel(parent, value);
+  }
+
+  private String editEvent(Element parent, String value, String viewData,
+      NativeEvent event, ValueUpdater<String, String> valueUpdater) {
+    if ("keydown".equals(event.getType())) {
+      if (event.getKeyCode() == KeyCodes.KEY_ENTER) {
+        return commit(parent, valueUpdater);
+      }
+      if (event.getKeyCode() == KeyCodes.KEY_ESCAPE) {
+        return cancel(parent, value);
+      }
+    }
+    return viewData;
+  }
+
+  private String nonEditEvent(Element parent, String value, String viewData,
+      NativeEvent event, ValueUpdater<String, String> valueUpdater) {
+    if ("click".equals(event.getType())) {
+      return edit(parent, value);
+    }
+    return viewData;
+  }
+}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/Customized.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/Customized.java
index 1fa5b9e..17cfaca 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/Customized.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/Customized.java
@@ -55,9 +55,8 @@
     root.add(shell);
 
     shell.setListener(new CustomizedShell.Listener() {
-      public void setFirstPurpose(String purpose) {
+      public void setPurpose(Values<ReportKey> report, String purpose) {
         DeltaValueStore deltaValueStore = requestFactory.getValueStore().spawnDeltaView();
-        Values<ReportKey> report = shell.getValues().get(0);
         deltaValueStore.set(report.getKey().getPurpose(), report, purpose);
         requestFactory.syncRequest(deltaValueStore).fire();
       }
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.java
index 2e059f4..50026c1 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.java
@@ -15,30 +15,25 @@
  */
 package com.google.gwt.sample.expenses.gwt.customized;
 
+import com.google.gwt.bikeshed.cells.client.DateCell;
+import com.google.gwt.bikeshed.cells.client.EditTextCell;
+import com.google.gwt.bikeshed.cells.client.FieldUpdater;
+import com.google.gwt.bikeshed.cells.client.TextCell;
+import com.google.gwt.bikeshed.list.client.Column;
+import com.google.gwt.bikeshed.list.client.PagingTableListView;
+import com.google.gwt.bikeshed.list.shared.ListListModel;
 import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NodeList;
-import com.google.gwt.dom.client.TableCellElement;
-import com.google.gwt.dom.client.TableElement;
-import com.google.gwt.dom.client.TableRowElement;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.i18n.client.DateTimeFormat;
 import com.google.gwt.sample.expenses.gwt.request.ReportChanged;
 import com.google.gwt.sample.expenses.gwt.request.ReportKey;
 import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiFactory;
 import com.google.gwt.uibinder.client.UiField;
-import com.google.gwt.uibinder.client.UiHandler;
-import com.google.gwt.user.client.ui.Button;
 import com.google.gwt.user.client.ui.Composite;
 import com.google.gwt.user.client.ui.ListBox;
 import com.google.gwt.user.client.ui.TakesValueList;
-import com.google.gwt.user.client.ui.TextBox;
 import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.valuestore.shared.Property;
 import com.google.gwt.valuestore.shared.Values;
-import com.google.gwt.valuestore.shared.ValuesKey;
 
 import java.util.Date;
 import java.util.List;
@@ -49,53 +44,72 @@
  */
 public class CustomizedShell extends Composite implements TakesValueList<Values<ReportKey>>, ReportChanged.Handler {
   interface Listener {
-    void setFirstPurpose(String purpose);
+    void setPurpose(Values<ReportKey> report, String purpose);
   }
-  
+
   interface ShellUiBinder extends UiBinder<Widget, CustomizedShell> {
   } 
 
   private static ShellUiBinder uiBinder = GWT.create(ShellUiBinder.class);
 
   private Listener listener;
-
-  @UiField
-  Element error;
-  @UiField
-  TableElement table;
-  @UiField
-  TableRowElement header;
-  @UiField
-  ListBox users;
-  @UiField
-  TextBox purpose;
-  @UiField
-  Button save;
   private List<Values<ReportKey>> values;
+  private final ListListModel<Values<ReportKey>> model;
+
+  @UiField Element error;
+  @UiField ListBox users;
+  @UiField PagingTableListView<Values<ReportKey>> listView;
+
+  private Column<Values<ReportKey>, Date, Void> createdCol = new Column<Values<ReportKey>, Date, Void>(
+      new DateCell()) {
+    @Override
+    public Date getValue(Values<ReportKey> object) {
+      return object.get(ReportKey.get().getCreated());
+    }
+  };
+
+  private Column<Values<ReportKey>, String, Void> statusCol = new Column<Values<ReportKey>, String, Void>(
+      TextCell.getInstance()) {
+    @Override
+    public String getValue(Values<ReportKey> object) {
+      return "...";
+    }
+  };
+
+  private Column<Values<ReportKey>, String, String> purposeCol = new Column<Values<ReportKey>, String, String>(
+      new EditTextCell()) {
+    @Override
+    public String getValue(Values<ReportKey> object) {
+      return object.get(ReportKey.get().getPurpose());
+    }
+  };
 
   public CustomizedShell() {
+    model = new ListListModel<Values<ReportKey>>();
     initWidget(uiBinder.createAndBindUi(this));
+
+    listView.addColumn(createdCol, "Created");
+    listView.addColumn(statusCol, "Status (tbd)");
+    listView.addColumn(purposeCol, "Purpose");
+
+    purposeCol.setFieldUpdater(new FieldUpdater<Values<ReportKey>, String, String>() {
+      @Override
+      public void update(int index, Values<ReportKey> object, String value,
+          String viewData) {
+        model.getList().set(index, object);
+        listener.setPurpose(object, value);
+      }
+    });
   }
 
   public List<Values<ReportKey>> getValues() {
     return values;
   }
-  
-  @UiHandler("purpose") 
-  public void onPurposeChange(ValueChangeEvent<String> e) {
-    listener.setFirstPurpose(e.getValue());
-  }
-  
+
   public void onReportChanged(ReportChanged event) {
     refresh();
   }
-  
-  @UiHandler("save")
-  @SuppressWarnings("unused")
-  public void onSaveClick(ClickEvent e) {
-    listener.setFirstPurpose(purpose.getValue());
-  }
-  
+
   public void setListener(Listener listener) {
     this.listener = listener;
   }
@@ -106,59 +120,11 @@
   }
 
   private void refresh() {
-    int r = 1; // skip header
-    NodeList<TableRowElement> tableRows = table.getRows();
-    purpose.setText("");
-    boolean enabled = values.size() > 0;
-    purpose.setEnabled(enabled);
-    save.setEnabled(enabled);
-    for (int i = 0; i < values.size(); i++) {
-      Values<ReportKey> valueRow = values.get(i);
-
-      if (i == 0) {
-        purpose.setText(valueRow.get(ReportKey.get().getPurpose()));
-      }
-      if (r < tableRows.getLength()) {
-        reuseRow(r, tableRows, valueRow);
-      } else {
-        TableRowElement tableRow = Document.get().createTRElement();
-
-        TableCellElement tableCell = Document.get().createTDElement();
-        tableCell.setInnerText(renderDate(valueRow, ReportKey.get().getCreated()));
-        tableRow.appendChild(tableCell);
-
-        tableCell = Document.get().createTDElement();
-        /* status goes here */
-        tableRow.appendChild(tableCell);
-
-        tableCell = Document.get().createTDElement();
-        tableCell.setInnerText(valueRow.get(ReportKey.get().getPurpose()));
-        tableRow.appendChild(tableCell);
-
-        table.appendChild(tableRow);
-      }
-      r++;
-    }
-    while (r < tableRows.getLength()) {
-      table.removeChild(tableRows.getItem(r));
-    }
+    model.setList(values);
   }
 
-  private <K extends ValuesKey<K>> String renderDate(Values<K> values, Property<K, Date> property) {
-    return DateTimeFormat.getShortDateFormat().format(values.get(property));
-  }
-
-  /**
-   * @param r
-   * @param tableRows
-   * @param valueRow
-   */
-  private void reuseRow(int r, NodeList<TableRowElement> tableRows,
-      Values<ReportKey> valueRow) {
-    TableRowElement tableRow = tableRows.getItem(r);
-    NodeList<TableCellElement> tableCells = tableRow.getCells();
-
-    // tableCells.getItem(0).setInnerText(valueRow.get(Report.instance().CREATED).toString());
-    tableCells.getItem(2).setInnerText(valueRow.get(ReportKey.get().getPurpose()));
+  @UiFactory
+  PagingTableListView<Values<ReportKey>> createListView() {
+    return new PagingTableListView<Values<ReportKey>>(model, 10);
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.ui.xml b/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.ui.xml
index 4c36cc6..af0a02a 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.ui.xml
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/customized/CustomizedShell.ui.xml
@@ -1,6 +1,7 @@
 <!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
 <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
-  xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+  xmlns:g='urn:import:com.google.gwt.user.client.ui'
+  xmlns:l='urn:import:com.google.gwt.bikeshed.list.client'>
 
   <ui:style>
     .disabled {
@@ -34,6 +35,7 @@
         <g:ListBox addStyleNames='{style.users}' ui:field='users'></g:ListBox>
       </g:HTMLPanel>
     </g:north>
+
     <g:west size='15'>
       <g:HTML styleName='{style.disabled}'>
         <div>New expense report</div>
@@ -46,21 +48,7 @@
     <g:center>
       <g:HTMLPanel width='100%' height='100%'>
         <h1>Expenses</h1>
-        <table ui:field='table' class='{style.reports}' width='100%'>
-          <col width='0%' span='2'></col>
-          <col width='100%'></col>
-          <tr ui:field='header'>
-            <th>Created</th>
-            <th>Status&nbsp;(tbd)</th>
-            <th align='left'>Purpose</th>
-          </tr>
-        </table>
-
-        <div>
-          Edit purpose of first report:
-          <g:TextBox ui:field='purpose' enabled='false'/>
-          <g:Button ui:field='save' enabled='false'>Save</g:Button>
-        </div>
+        <l:PagingTableListView ui:field='listView'/>
       </g:HTMLPanel>
     </g:center>
   </g:DockLayoutPanel>