Checkpoint


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7558 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/cells/client/Cell.java b/bikeshed/src/com/google/gwt/cells/client/Cell.java
index a767282..e92dbe1 100644
--- a/bikeshed/src/com/google/gwt/cells/client/Cell.java
+++ b/bikeshed/src/com/google/gwt/cells/client/Cell.java
@@ -1,3 +1,18 @@
+/*
+ * 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.cells.client;
 
 import com.google.gwt.dom.client.Element;
@@ -5,6 +20,12 @@
 
 public abstract class Cell<C> {
 
+  /**
+   * @param parent
+   * @param value
+   * @param event
+   * @param mutator
+   */
   public void onBrowserEvent(Element parent, C value, NativeEvent event,
       Mutator<C, C> mutator) {
   }
diff --git a/bikeshed/src/com/google/gwt/cells/client/CurrencyCell.java b/bikeshed/src/com/google/gwt/cells/client/CurrencyCell.java
new file mode 100644
index 0000000..a09fbb4
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/cells/client/CurrencyCell.java
@@ -0,0 +1,33 @@
+/*
+ * 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.cells.client;
+
+public class CurrencyCell extends Cell<Integer> {
+
+  @Override
+  public void render(Integer price, StringBuilder sb) {
+    int dollars = price / 100;
+    int cents = price % 100;
+    
+    sb.append("$ ");
+    sb.append(dollars);
+    sb.append('.');
+    if (cents < 10) {
+      sb.append('0');
+    }
+    sb.append(cents);
+  }
+}
diff --git a/bikeshed/src/com/google/gwt/cells/client/Mutator.java b/bikeshed/src/com/google/gwt/cells/client/Mutator.java
index cefab79..4d5584d 100644
--- a/bikeshed/src/com/google/gwt/cells/client/Mutator.java
+++ b/bikeshed/src/com/google/gwt/cells/client/Mutator.java
@@ -1,3 +1,18 @@
+/*
+ * 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.cells.client;
 
 public interface Mutator<T, C> {
diff --git a/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java b/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java
index 88c4fd8..318fd6d 100644
--- a/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java
+++ b/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java
@@ -1,3 +1,18 @@
+/*
+ * 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.cells.client;
 
 import com.google.gwt.dom.client.Element;
diff --git a/bikeshed/src/com/google/gwt/list/client/Column.java b/bikeshed/src/com/google/gwt/list/client/Column.java
index 1de6841..2c41412 100644
--- a/bikeshed/src/com/google/gwt/list/client/Column.java
+++ b/bikeshed/src/com/google/gwt/list/client/Column.java
@@ -1,5 +1,17 @@
-/**
+/*
+ * 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.list.client;
 
diff --git a/bikeshed/src/com/google/gwt/list/client/PagingTableListView2.java b/bikeshed/src/com/google/gwt/list/client/PagingTableListView.java
similarity index 91%
rename from bikeshed/src/com/google/gwt/list/client/PagingTableListView2.java
rename to bikeshed/src/com/google/gwt/list/client/PagingTableListView.java
index bc88145..0c30243 100644
--- a/bikeshed/src/com/google/gwt/list/client/PagingTableListView2.java
+++ b/bikeshed/src/com/google/gwt/list/client/PagingTableListView.java
@@ -1,3 +1,18 @@
+/*
+ * 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.list.client;
 
 import com.google.gwt.cells.client.ButtonCell;
@@ -23,7 +38,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class PagingTableListView2<T> extends Widget {
+public class PagingTableListView<T> extends Widget {
 
   private int pageSize;
   private int numPages;
@@ -34,9 +49,8 @@
   private ArrayList<T> data = new ArrayList<T>();
   private ButtonCell prevButton = new ButtonCell();
   private ButtonCell nextButton = new ButtonCell();
-  private String pageText = "Page 1 of ??";
 
-  public PagingTableListView2(ListModel<T> listModel, final int pageSize) {
+  public PagingTableListView(ListModel<T> listModel, final int pageSize) {
     this.pageSize = pageSize;
     setElement(Document.get().createTableElement());
     createRows();
@@ -222,7 +236,6 @@
     int numCols = columns.size();
     
     // TODO - only delete as needed
-    NodeList<TableRowElement> rows = table.getRows();
     int numRows = table.getRows().getLength();
     while (numRows-- > 0) {
       table.deleteRow(0);
diff --git a/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java b/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java
index 51b90fd..35ee5f4 100644
--- a/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java
+++ b/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java
@@ -1,3 +1,18 @@
+/*
+ * 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.list.client;
 
 import com.google.gwt.cells.client.Cell;
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/ApplicationCache.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/ApplicationCache.java
deleted file mode 100644
index 3785de7..0000000
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/ApplicationCache.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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.sample.datawidgets.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.list.shared.AsyncListModel;
-import com.google.gwt.list.shared.Range;
-import com.google.gwt.list.shared.AsyncListModel.DataSource;
-import com.google.gwt.sample.datawidgets.shared.StockQuote;
-import com.google.gwt.sample.datawidgets.shared.StockQuoteList;
-import com.google.gwt.sample.datawidgets.shared.StockResponse;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * The application level cache used by this app.
- */
-public class ApplicationCache implements DataSource<StockQuote> {
-
-  /**
-   * The delay between updates in milliseconds.
-   */
-  private static final int UPDATE_DELAY = 5000;
-
-  /**
-   * The list models used in this application.
-   */
-  private List<AsyncListModel<StockQuote>> asyncListModels = new ArrayList<AsyncListModel<StockQuote>>();
-
-  /**
-   * The {@link StockService} used to retrieve data.
-   */
-  private final StockServiceAsync dataService = GWT.create(StockService.class);
-
-  /**
-   * User supplied notes, indexed by ticker symbol.
-   */
-  private HashMap<String,String> notesByTicker = new HashMap<String,String>();
-
-  /**
-   * The current query string.
-   */
-  private String query;
-
-  /**
-   * The timer used to update the stock quotes.
-   */
-  private Timer updateTimer = new Timer() {
-    @Override
-    public void run() {
-      update();
-    }
-  };
-
-  /**
-   * A set of user-marked 'favorite' ticker symbols.
-   */
-  private HashSet<String> favoritesByTicker = new HashSet<String>();
-
-  /**
-   * Subscribe a list model to this cache.
-   * 
-   * @param listModel the list model to subscribe
-   */
-  public void addAsyncListModel(AsyncListModel<StockQuote> listModel) {
-    asyncListModels.add(listModel);
-    // (TODO): Need to update the new listModel
-  }
-
-  /**
-   * Request data from the server.
-   * 
-   * @param query the query string
-   */
-  public void query(String query) {
-    this.query = query;
-    update();
-  }
-
-  public void requestData(AsyncListModel<StockQuote> listModel) {
-    sendRequest(listModel.getRanges());
-  }
-  
-  /**
-   * Set or unset a ticker symbol as a 'favorite.'
-   *  
-   * @param ticker the ticker symbol
-   * @param favorite if true, make the stock a favorite
-   */
-  public void setFavorite(String ticker, boolean favorite) {
-    if (favorite) {
-      favoritesByTicker.add(ticker);
-    } else {
-      favoritesByTicker.remove(ticker);
-    }
-  }
-
-  /**
-   * Set or unset a note on a ticker symbol.
-   * 
-   * @param ticker the ticker symbol
-   * @param note a note to associate with the stock, or null
-   */
-  public void setNotes(String ticker, String note) {
-    if (note == null || note.length() == 0) {
-      notesByTicker.remove(ticker);
-    } else {
-      notesByTicker.put(ticker, note);
-    }
-  }
-
-  /**
-   * Request data from the server using the last query string.
-   */
-  public void update() {
-    if (query == null || query.length() < 1) {
-      return;
-    }
-
-    List<Range> ranges = new ArrayList<Range>();
-    for (AsyncListModel<StockQuote> listModel : asyncListModels) {
-      Range[] curRanges = listModel.getRanges();
-      for (Range range : curRanges) {
-        ranges.add(range);
-      }
-    }
-    sendRequest(ranges.toArray(new Range[ranges.size()]));
-  }
-
-  private void sendRequest(Range[] ranges) {
-    if (query == null) {
-      return;
-    }
-    dataService.getStockQuotes(query, ranges,
-        new AsyncCallback<StockResponse>() {
-      public void onFailure(Throwable caught) {
-        Window.alert("ERROR: " + caught.getMessage());
-        updateTimer.schedule(UPDATE_DELAY);
-      }
-
-      public void onSuccess(StockResponse result) {
-        setTransientData(result);
-
-        for (AsyncListModel<StockQuote> listModel : asyncListModels) {
-          listModel.updateDataSize(result.getNumRows(), true);
-          for (StockQuoteList list : result.getLists()) {
-            listModel.updateViewData(list.getStartIndex(), list.size(),
-                list);
-          }
-        }
-        updateTimer.schedule(UPDATE_DELAY);
-      }
-    });
-  }
-
-  private void setTransientData(StockResponse result) {
-    for (StockQuoteList list : result.getLists()) {
-      for (StockQuote quote : list) {
-        String ticker = quote.getTicker();
-        
-        // Set notes
-        String notes = notesByTicker.get(ticker);
-        if (notes != null) {
-          quote.setNotes(notes);
-        }
-        
-        // Set 'favorite' status
-        quote.setFavorite(favoritesByTicker.contains(ticker));
-      }
-    }
-  }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/CurrencyCell.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/CurrencyCell.java
deleted file mode 100644
index 21dca9d..0000000
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/CurrencyCell.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.google.gwt.sample.datawidgets.client;
-
-import com.google.gwt.cells.client.Cell;
-
-public class CurrencyCell extends Cell<Integer> {
-
-  @Override
-  public void render(Integer price, StringBuilder sb) {
-    int dollars = price / 100;
-    int cents = price % 100;
-    
-    sb.append("$ ");
-    sb.append(dollars);
-    sb.append('.');
-    if (cents < 10) {
-      sb.append('0');
-    }
-    sb.append(cents);
-  }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java
index 8f983d7..c9699ec 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java
@@ -16,50 +16,61 @@
 package com.google.gwt.sample.datawidgets.client;
 
 import com.google.gwt.cells.client.CheckboxCell;
+import com.google.gwt.cells.client.CurrencyCell;
 import com.google.gwt.cells.client.Mutator;
 import com.google.gwt.cells.client.TextCell;
 import com.google.gwt.cells.client.TextInputCell;
 import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.dom.client.KeyUpEvent;
 import com.google.gwt.event.dom.client.KeyUpHandler;
 import com.google.gwt.list.client.Column;
-import com.google.gwt.list.client.PagingTableListView2;
+import com.google.gwt.list.client.PagingTableListView;
 import com.google.gwt.list.shared.AsyncListModel;
+import com.google.gwt.list.shared.Range;
+import com.google.gwt.list.shared.AsyncListModel.DataSource;
 import com.google.gwt.sample.datawidgets.shared.StockQuote;
+import com.google.gwt.sample.datawidgets.shared.StockQuoteList;
+import com.google.gwt.sample.datawidgets.shared.StockRequest;
+import com.google.gwt.sample.datawidgets.shared.StockResponse;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.TextBox;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
 /**
  * Entry point classes define <code>onModuleLoad()</code>.
  */
 public class DataBackedWidgets implements EntryPoint {
+  
+  /**
+   * The delay between updates in milliseconds.
+   */
+  private static final int UPDATE_DELAY = 5000;
+  
+  /**
+   * The {@link StockService} used to retrieve data.
+   */
+  private final StockServiceAsync dataService = GWT.create(StockService.class);
 
-  final TextBox queryField = new TextBox();
-  final Label errorLabel = new Label();
-  private PagingTableListView2<StockQuote> resultsTable0;
-
-  private ApplicationCache appCache = new ApplicationCache();
-  private AsyncListModel<StockQuote> listModel1 = new AsyncListModel<StockQuote>(
-      appCache);
-
-  Column<StockQuote, Boolean> favoriteColumn = new Column<StockQuote, Boolean>(
+  private final Label errorLabel = new Label();
+  
+  private Column<StockQuote, Boolean> favoriteColumn = new Column<StockQuote, Boolean>(
       new CheckboxCell()) {
     @Override
     protected Boolean getValue(StockQuote object) {
       return object.isFavorite();
     }
   };
-
-  Column<StockQuote, String> tickerColumn = new Column<StockQuote, String>(
-      new TextCell()) {
-    @Override
-    protected String getValue(StockQuote object) {
-      return object.getTicker();
-    }
-  };
-
-  Column<StockQuote, String> nameColumn = new Column<StockQuote, String>(
+  
+  private Column<StockQuote, String> nameColumn = new Column<StockQuote, String>(
       new TextCell()) {
     @Override
     protected String getValue(StockQuote object) {
@@ -67,7 +78,14 @@
     }
   };
 
-  Column<StockQuote, Integer> priceColumn = new Column<StockQuote, Integer>(
+  private Column<StockQuote, String> notesColumn = new Column<StockQuote, String>(new TextInputCell()) {
+    @Override
+    protected String getValue(StockQuote object) {
+      return object.getNotes();
+    }
+  };
+
+  private Column<StockQuote, Integer> priceColumn = new Column<StockQuote, Integer>(
       new CurrencyCell()) {
     @Override
     protected Integer getValue(StockQuote object) {
@@ -75,19 +93,50 @@
     }
   };
 
-  Column<StockQuote, String> notesColumn = new Column<StockQuote, String>(new TextInputCell()) {
+  private final TextBox queryField = new TextBox();
+
+  private Column<StockQuote, String> tickerColumn = new Column<StockQuote, String>(
+      new TextCell()) {
     @Override
     protected String getValue(StockQuote object) {
-      return object.getNotes();
+      return object.getTicker();
     }
   };
 
   /**
+   * A set of user-marked 'favorite' ticker symbols.
+   */
+  private HashSet<String> favoritesByTicker = new HashSet<String>();
+
+  private AsyncListModel<StockQuote> favoritesListModel;
+
+  /**
+   * User supplied notes, indexed by ticker symbol.
+   */
+  private HashMap<String,String> notesByTicker = new HashMap<String,String>();
+
+  private PagingTableListView<StockQuote> resultsTable0;
+
+  private AsyncListModel<StockQuote> searchListModel;
+
+  private String searchQuery;
+
+  /**
+   * The timer used to update the stock quotes.
+   */
+  private Timer updateTimer = new Timer() {
+    @Override
+    public void run() {
+      update();
+    }
+  };
+
+  private Range[] searchRanges;
+
+  /**
    * This is the entry point method.
    */
   public void onModuleLoad() {
-    appCache.addAsyncListModel(listModel1);
-
     queryField.setText("G");
 
     // Add the nameField and sendButton to the RootPanel
@@ -98,9 +147,22 @@
     // Focus the cursor on the name field when the app loads
     queryField.setFocus(true);
     queryField.selectAll();
+    
+    // Create the list models
+    searchListModel = new AsyncListModel<StockQuote>(new DataSource<StockQuote>() {
+      public void requestData(AsyncListModel<StockQuote> listModel) {
+        sendSearchRequest(searchListModel.getRanges());
+      }
+    });
+    
+    favoritesListModel = new AsyncListModel<StockQuote>(new DataSource<StockQuote>() {
+      public void requestData(AsyncListModel<StockQuote> listModel) {
+        sendFavoritesRequest(favoritesListModel.getRanges());
+      }
+    });
 
     // Create the results table.
-    resultsTable0 = new PagingTableListView2<StockQuote>(listModel1, 10);
+    resultsTable0 = new PagingTableListView<StockQuote>(searchListModel, 10);
     resultsTable0.addColumn(favoriteColumn);
     resultsTable0.addColumn(tickerColumn);
     resultsTable0.addColumn(nameColumn);
@@ -109,13 +171,13 @@
     
     favoriteColumn.setMutator(new Mutator<StockQuote, Boolean>() {
       public void mutate(StockQuote object, Boolean after) {
-        appCache.setFavorite(object.getTicker(), after);
+        setFavorite(object.getTicker(), after);
       }
     });
 
     notesColumn.setMutator(new Mutator<StockQuote, String>() {
       public void mutate(StockQuote object, String after) {
-        appCache.setNotes(object.getTicker(), after);
+        setNotes(object.getTicker(), after);
       }
     });
 
@@ -124,21 +186,123 @@
     // Add a handler to send the name to the server
     queryField.addKeyUpHandler(new KeyUpHandler() {
       public void onKeyUp(KeyUpEvent event) {
-        sendQueryToServer();
+        setSearchQuery();
       }
     });
 
-    sendQueryToServer();
+    setSearchQuery();
+  }
+  
+  /**
+   * Set or unset a ticker symbol as a 'favorite.'
+   *  
+   * @param ticker the ticker symbol
+   * @param favorite if true, make the stock a favorite
+   */
+  public void setFavorite(String ticker, boolean favorite) {
+    if (favorite) {
+      favoritesByTicker.add(ticker);
+    } else {
+      favoritesByTicker.remove(ticker);
+    }
+  }
+
+  /**
+   * Set or unset a note on a ticker symbol.
+   * 
+   * @param ticker the ticker symbol
+   * @param note a note to associate with the stock, or null
+   */
+  public void setNotes(String ticker, String note) {
+    if (note == null || note.length() == 0) {
+      notesByTicker.remove(ticker);
+    } else {
+      notesByTicker.put(ticker, note);
+    }
+  }
+  
+  /**
+   * Request data from the server using the last query string.
+   */
+  public void update() {
+    if (searchQuery == null || searchQuery.length() < 1) {
+      return;
+    }
+
+    sendSearchRequest(searchListModel.getRanges());
+  }
+  
+  @SuppressWarnings("unused")
+  private void sendFavoritesRequest(Range[] ranges) {
+    sendRequest();
+  }
+  
+  private void sendSearchRequest(Range[] ranges) {
+    if (searchQuery == null) {
+      return;
+    }
+    searchRanges = ranges;
+    sendRequest();
+  }
+  
+  private void sendRequest() {
+    for (Range range : searchRanges) {
+      List<StockRequest> requests = new ArrayList<StockRequest>();
+      requests.add(new StockRequest(searchQuery, range));
+      
+      dataService.getStockQuotes(requests, new AsyncCallback<List<StockResponse>>() {
+        public void onFailure(Throwable caught) {
+          Window.alert("ERROR: " + caught.getMessage());
+          updateTimer.schedule(UPDATE_DELAY);
+        }
+
+        public void onSuccess(List<StockResponse> responses) {
+          
+          for (StockResponse response : responses) {
+            StockQuoteList stocks = response.getStocks();
+            // Refresh 'notes' and 'favorite' fields
+            // TODO (rice) keep this info on the server
+            setTransientData(stocks);
+            
+            if (response instanceof StockResponse.Search) {
+              searchListModel.updateDataSize(((StockResponse.Search) response).getNumRows(), true);
+              searchListModel.updateViewData(stocks.getStartIndex(), stocks.size(), stocks);
+            } else if (response instanceof StockResponse.Favorites) {
+              // TODO (rice) implement
+            } else {
+              throw new RuntimeException("Unknown response type: " + response.getClass().getName());
+            }
+          }
+          updateTimer.schedule(UPDATE_DELAY);
+        }
+      });
+    }
   }
 
   /**
    * Send the query to the server.
    */
-  private void sendQueryToServer() {
-    String ticker = queryField.getText();
-    if (ticker.length() > 0) {
+  private void setSearchQuery() {
+    String query = queryField.getText();
+    if (query.length() > 0) {
       errorLabel.setText("");
-      appCache.query(ticker);
+      this.searchQuery = query;
+      update();
+    }
+  }
+
+  private void setTransientData(StockQuoteList stocks) {
+    for (StockQuote quote : stocks) {
+      String ticker = quote.getTicker();
+
+      // Set notes
+      String notes = notesByTicker.get(ticker);
+      if (notes != null) {
+        quote.setNotes(notes);
+      }
+
+      // Set 'favorite' status
+      quote.setFavorite(favoritesByTicker.contains(ticker));
     }
   }
 }
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/PagingTableListView.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/PagingTableListView.java
deleted file mode 100644
index 62f72f1..0000000
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/PagingTableListView.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.sample.datawidgets.client;
-
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.list.shared.ListEvent;
-import com.google.gwt.list.shared.ListHandler;
-import com.google.gwt.list.shared.ListModel;
-import com.google.gwt.list.shared.ListRegistration;
-import com.google.gwt.list.shared.SizeChangeEvent;
-import com.google.gwt.sample.datawidgets.shared.StockQuote;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlexTable;
-
-import com.google.gwt.user.client.ui.Composite;
-
-import java.util.List;
-
-/**
- * A view of a list model that can be paged.
- */
-public class PagingTableListView extends Composite {
-
-  /**
-   * The main widget.
-   */
-  private FlexTable table = new FlexTable();
-
-  /**
-   * The current page.
-   */
-  private int curPage = 0;
-
-  /**
-   * The total number of pages.
-   */
-  private int numPages = 0;
-
-  /**
-   * The page size.
-   */
-  private int pageSize;
-
-  private ListRegistration listReg;
-
-  /**
-   * Construct a new {@link PagingTableListView}.
-   * 
-   * @param listModel the listModel that backs the table
-   * @param pageSize the page size
-   */
-  public PagingTableListView(ListModel<StockQuote> listModel, final int pageSize) {
-    this.pageSize = pageSize;
-    initWidget(table);
-    table.setBorderWidth(3);
-
-    // Create the next and previous buttons.
-    final Button nextButton = new Button("Next", new ClickHandler() {
-      public void onClick(ClickEvent event) {
-        setPage(curPage + 1);
-      }
-    });
-    final Button prevButton = new Button("Prev", new ClickHandler() {
-      public void onClick(ClickEvent event) {
-        setPage(curPage - 1);
-      }
-    });
-
-    // Attach to the list model.
-    listReg = listModel.addListHandler(new ListHandler<StockQuote>() {
-      public void onDataChanged(ListEvent<StockQuote> event) {
-        // Clear existing data.
-        table.removeAllRows();
-
-        // Add the headers.
-        table.setHTML(0, 0, "<b>Ticker</b>");
-        table.setHTML(0, 1, "<b>Company</b>");
-        table.setHTML(0, 2, "<b>Price</b>");
-
-        // Add the new data.
-        int row = table.getRowCount();
-        List<StockQuote> values = event.getValues();
-        for (StockQuote value : values) {
-          table.setText(row, 0, value.getTicker());
-          table.setText(row, 1, value.getName());
-          table.setText(row, 2, "" + value.getDisplayPrice());
-          row++;
-        }
-
-        // Add next/prev buttons.
-        table.setWidget(row, 0, prevButton);
-        table.setText(row, 1, "Page " + (curPage + 1) + " of " + numPages);
-        table.setWidget(row, 2, nextButton);
-      }
-
-      public void onSizeChanged(SizeChangeEvent event) {
-        int size = event.getSize();
-        if (size <= 0) {
-          numPages = 0;
-        } else {
-          numPages = 1 + (size - 1) / pageSize;
-        }
-        setPage(curPage);
-      }
-    });
-    listReg.setRangeOfInterest(0, pageSize);
-  }
-
-  /**
-   * Get the current page.
-   * 
-   * @return the current page
-   */
-  public int getPage() {
-    return curPage;
-  }
-
-  /**
-   * Set the current visible page.
-   * 
-   * @param page the page index
-   */
-  public void setPage(int page) {
-    int newPage = Math.min(page, numPages - 1);
-    newPage = Math.max(0, newPage);
-
-    // Update the text showing the page number.
-    updatePageText(newPage);
-
-    // Early exit if we are already on the right page.
-    if (curPage != newPage) {
-      curPage = newPage;
-      listReg.setRangeOfInterest(curPage * pageSize, pageSize);
-    }
-  }
-
-  /**
-   * Set the number of rows per page.
-   * 
-   * @param pageSize the page size
-   */
-  public void setPageSize(int pageSize) {
-    if (this.pageSize == pageSize) {
-      return;
-    }
-    this.pageSize = pageSize;
-    curPage = -1;
-    setPage(curPage);
-  }
-
-  /**
-   * Update the text that shows the current page.
-   * 
-   * @param page the current page
-   */
-  private void updatePageText(int page) {
-    if (table.getRowCount() > 0) {
-      int row = table.getRowCount() - 1;
-      table.setText(row, 1, "Page " + (page + 1) + " of " + numPages);
-    }
-  }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java
index 1b0663e..813d2dc 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java
@@ -1,3 +1,18 @@
+/*
+ * 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.sample.datawidgets.client;
 
 import com.google.gwt.cells.client.Cell;
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java
index d6ddd3b..b0437c0 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java
@@ -15,16 +15,20 @@
  */
 package com.google.gwt.sample.datawidgets.client;
 
-import com.google.gwt.list.shared.Range;
+import com.google.gwt.sample.datawidgets.shared.StockRequest;
 import com.google.gwt.sample.datawidgets.shared.StockResponse;
 import com.google.gwt.user.client.rpc.RemoteService;
 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
 
+import java.util.List;
+
 /**
  * The client side stub for the RPC service.
  */
 @RemoteServiceRelativePath("stock")
 public interface StockService extends RemoteService {
-  StockResponse getStockQuotes(String query, Range[] ranges)
+  
+  List<StockResponse> getStockQuotes(List<StockRequest> requests)
       throws IllegalArgumentException;
+  
 }
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java
index ef5ce39..b6d5c6e 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java
@@ -15,14 +15,16 @@
  */
 package com.google.gwt.sample.datawidgets.client;
 
-import com.google.gwt.list.shared.Range;
+import com.google.gwt.sample.datawidgets.shared.StockRequest;
 import com.google.gwt.sample.datawidgets.shared.StockResponse;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
+import java.util.List;
+
 /**
  * The async counterpart of <code>DataService</code>.
  */
 public interface StockServiceAsync {
-  void getStockQuotes(String query, Range[] ranges,
-      AsyncCallback<StockResponse> callback);
+  void getStockQuotes(List<StockRequest> requests,
+      AsyncCallback<List<StockResponse>> callback);
 }
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java b/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java
index 53f2eb6..85e4240 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java
@@ -19,6 +19,7 @@
 import com.google.gwt.sample.datawidgets.client.StockService;
 import com.google.gwt.sample.datawidgets.shared.StockQuote;
 import com.google.gwt.sample.datawidgets.shared.StockQuoteList;
+import com.google.gwt.sample.datawidgets.shared.StockRequest;
 import com.google.gwt.sample.datawidgets.shared.StockResponse;
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
@@ -57,11 +58,11 @@
     }
   }
 
+  static HashMap<String, String> companyNamesBySymbol = new HashMap<String, String>();
+
   // TODO(rice) - use a smarter data structure
   static TreeSet<MyStockQuote> quotes = new TreeSet<MyStockQuote>();
 
-  static HashMap<String, String> companyNamesBySymbol = new HashMap<String, String>();
-
   private static final int MAX_RESULTS_TO_RETURN = 10000;
 
   static {
@@ -75,27 +76,55 @@
       companyNamesBySymbol.put(symbol, companyName);
     }
   }
+  
+  public List<StockResponse> getStockQuotes(List<StockRequest> requests) {
+    List<StockResponse> responses = new ArrayList<StockResponse>();
+    for (StockRequest request : requests) {
+      responses.add(getStockQuotes(request));
+    }
+    
+    // TODO (rice) add favorites response
+    return responses;
+  }
 
-  public StockResponse getStockQuotes(String query, Range[] ranges)
+  public List<String> getSymbols(String query) {
+    List<String> symbols = new ArrayList<String>();
+    if (query.length() > 0) {
+      query = query.toUpperCase();
+      int count = 0;
+      for (MyStockQuote stock : quotes) {
+        String symbol = stock.getTicker();
+        if (match(symbol, query)) {
+          symbols.add(symbol);
+          count++;
+          if (count > MAX_RESULTS_TO_RETURN) {
+            break;
+          }
+        }
+      }
+    }
+    return symbols;
+  }
+
+  private StockResponse getStockQuotes(StockRequest request)
       throws IllegalArgumentException {
+    String query = request.getQuery();
+    Range range = request.getRange();
 
     // Get all symbols for the query.
     List<String> symbols = getSymbols(query);
-    List<StockQuoteList> results = new ArrayList<StockQuoteList>();
+    
     if (symbols.size() == 0) {
-      return new StockResponse(0, results);
+      return new StockResponse.Search(new StockQuoteList(0), 0);
     }
 
+    int start = range.getStart();
+    int end = Math.min(start + range.getLength(), symbols.size());
+    
     // Get the symbols that are in range.
     Set<String> symbolsInRange = new HashSet<String>();
-    for (Range range : ranges) {
-      int start = range.getStart();
-      int end = start + range.getLength();
-      start = Math.max(start, 0);
-      end = Math.min(end, symbols.size());
-      if (end > start) {
-        symbolsInRange.addAll(symbols.subList(start, end));
-      }
+    if (end > start) {
+      symbolsInRange.addAll(symbols.subList(start, end));
     }
 
     // Build the URL string.
@@ -112,7 +141,7 @@
 
     if (first) {
       // No symbols
-      return new StockResponse(0, results);
+      return new StockResponse.Search(new StockQuoteList(0), 0);
     }
 
     // Send the request.
@@ -169,45 +198,19 @@
       }
     }
 
-    // Convert the price map to a list of StockQuoteList.
-    List<StockQuoteList> toRet = new ArrayList<StockQuoteList>();
-    for (Range range : ranges) {
-      int start = range.getStart();
-      int end = Math.min(start + range.getLength(), symbols.size());
-      StockQuoteList curList = new StockQuoteList(start);
-      toRet.add(curList);
-      for (int i = start; i < end; i++) {
-        String symbol = symbols.get(i);
-        StockQuote quote = priceMap.get(symbol);
-//        System.out.println("i = " + i + ", add symbol " + symbol + " to range " + start + ", " + end);
-        if (quote == null) {
-          quote = new StockQuote(symbol, "<NO SUCH TICKER SYMBOL>", 0);
-          System.out.println("Bad symbol " + symbol);
-        }
-        curList.add(quote);
+    // Convert the price map to a StockQuoteList.
+    StockQuoteList toRet = new StockQuoteList(start);
+    for (int i = start; i < end; i++) {
+      String symbol = symbols.get(i);
+      StockQuote quote = priceMap.get(symbol);
+      if (quote == null) {
+        quote = new StockQuote(symbol, "<NO SUCH TICKER SYMBOL>", 0);
+        System.out.println("Bad symbol " + symbol);
       }
+      toRet.add(quote);
     }
 
-    return new StockResponse(symbols.size(), toRet);
-  }
-
-  public List<String> getSymbols(String query) {
-    List<String> symbols = new ArrayList<String>();
-    if (query.length() > 0) {
-      query = query.toUpperCase();
-      int count = 0;
-      for (MyStockQuote stock : quotes) {
-        String symbol = stock.getTicker();
-        if (match(symbol, query)) {
-          symbols.add(symbol);
-          count++;
-          if (count > MAX_RESULTS_TO_RETURN) {
-            break;
-          }
-        }
-      }
-    }
-    return symbols;
+    return new StockResponse.Search(toRet, symbols.size());
   }
 
   private boolean match(String symbol, String query) {
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockRequest.java b/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockRequest.java
new file mode 100644
index 0000000..3778b70
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockRequest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.sample.datawidgets.shared;
+
+import com.google.gwt.list.shared.Range;
+
+import java.io.Serializable;
+
+public class StockRequest implements Serializable {
+  
+  String query;
+  Range range;
+  
+  // Used by RPC
+  public StockRequest() {
+  }
+  
+  public StockRequest(String query, Range range) {
+    this.query = query;
+    this.range = range;
+  }
+  
+  public String getQuery() {
+    return query;
+  }
+  
+  public Range getRange() {
+    return range;
+  }
+}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java b/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java
index efa873a..4a33071 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java
@@ -16,19 +16,38 @@
 package com.google.gwt.sample.datawidgets.shared;
 
 import java.io.Serializable;
-import java.util.List;
 
 /**
  * A response to a request for stock data.
  */
 public class StockResponse implements Serializable {
-
-  private int numRows;
-  private List<StockQuoteList> lists;
-
-  public StockResponse(int numRows, List<StockQuoteList> lists) {
-    this.numRows = numRows;
-    this.lists = lists;
+  
+  protected StockQuoteList stocks;
+  
+  public static class Search extends StockResponse {
+    private int numRows;
+    
+    Search() {
+    }
+    
+    public Search(StockQuoteList stocks, int numRows) {
+      super(stocks);
+      this.numRows = numRows;
+    }
+    
+    public int getNumRows() {
+      return numRows;
+    }
+  }
+  
+  public static class Favorites extends StockResponse {
+    
+    Favorites() {
+    }
+    
+    public Favorites(StockQuoteList stocks) {
+      super(stocks);
+    }
   }
 
   /**
@@ -36,21 +55,13 @@
    */
   StockResponse() {
   }
-
-  /**
-   * Get the data for specific ranges.
-   * 
-   * @return the data
-   */
-  public List<StockQuoteList> getLists() {
-    return lists;
+  
+  public StockResponse(StockQuoteList stocks) {
+    this.stocks = stocks;
   }
-
-  /**
-   * @return the total number of rows available
-   */
-  public int getNumRows() {
-    return numRows;
+  
+  public StockQuoteList getStocks() {
+    return stocks;
   }
 }