Revision: 8044
Author: [email protected]
Date: Tue May  4 10:22:18 2010
Log: Adding Departments to Employees and updating the ExpenseTree so it loads employees correctly. Open icon now turns to a loading icon while loading.

http://code.google.com/p/google-web-toolkit/source/detail?r=8044

Added:
/branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/cellTreeLoading.gif
Modified:
/branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/impl/CellListImpl.java
 /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTree.css
/branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTree.java /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTreeNodeView.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseTree.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Expenses.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java

=======================================
--- /dev/null
+++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/cellTreeLoading.gif Tue May 4 10:22:18 2010
@@ -0,0 +1,75 @@
+GIF89a    ö  ÿÿÿ  ÌÔÔõ””é``Þ@@ØDDÙnnᢢëÜÜ÷¤¤ì$$Ó((Ó00Õ66Ö>>Øjj຺ð
+
+ÑrrâììúîîúÂÂòŠŠçPPÛ^^Þ¾¾ñÐÐô::×  ÐŒŒçªªí\\Þ||äââø††æ  Îhhàœœêffà¶¶ïFFÙ
+
+Ͱ°î––é  Ð  
Íèèùôôüxxã„„åööü‚‚娨íúúýüüýÀÀñÊÊóøøüÖÖö®®îððûÒÒõääøÞÞ÷ÎÎôÆÆò¼¼ñààøØØöòòûÚÚözzä´´ï²²ïLLÚRRÜZZÝbbßBBØ<<×ÄÄòvvã22Õêêú**ÔŽŽçXXÝ,,Ô
+
+Ñ  ëNNÛ Ïˆˆæddß44ÖÈÈóÌÌôææù¸¸ð~~ä’’èžžëVVÜ èJJÚHHÚ""Ò¦¦ì Ï Î¬¬í Ìššê&&Ó Ï88×ttâ..Õ Íppâ ÒTTÜllကå !ÿ NETSCAPE2.0 !þ Created with ajaxload.info !ù + , € ‚ƒ„
+
+ #)-†
+ +$*.‚6:7
+
+
+%+/‚70=6ƒ +†6 – Œ =?=‚ �...@?‚ + ± ;E‚ ±7A>‚ ± B;‚ ±>CD‚ †;+ Fƒ & 03 8 <Ý„ !' 14 +,< Œ"P° á¡FŽ` !ù + , Œ€ ‚ƒ„
+
+; WX [] † < U Y-\$_&
+JOSH  TJ^*\„G P›
+2
+
+ # ‚7ƒHKQ³ :6‚ L=ºFF‚'Mij ‚ NA³:bË I �...@Á ? R †/ D½‚924I ::/E` ÓƒA&V,Z JCaT T9Ij¬ ’ †®ƒ !ù + , ‰€ ‚ƒ„
+
+
+ i ^, † C K + jl
+8^ghQ  CRk  ƒ"dN<†h-P ‚c f   m]‚, >·Jnj‚2H0·T.-‚
+43·
+5 GЄ`eC·6:ƒ  f¿
+:=σG5 AT770 / 6
+...@c(CBA;@/܆F rø ¯Zµ@ !ù + , € ‚ƒ„
+
+?(
+ N!lG† 9  ' g[jNJ
+A#Hl F=B] O<ƒT&28†f_L;‚9V(  H  ‚J,      · kW‚lf:·  U‚olÇ T
+r‚ <b·JmK‚G(>·N*#‚F`8" p
+Aƒ/`9"0‚7ô q
+ E     "T=36né  F    o)
+ !ù + , Œ€ ‚ƒ„
+
+ aoe
+& ?† +qe 4H 2 +D< + @33 +% ƒF8<;†J @�...@c e[l‚>8T·Bju‚aB7· K¼AÇ ³ 0·Cv^‚ ¿ ur ‚6b?Õ
+
+Y) ƒ3T 3ƒ+'s
+
+6F0:‚.nt ·n830ÂÖ­ƒ‚ !ù + , ‰€ ‚ƒ„
+
+...@9(+ A † TA8cJ  q
+>
+  `GT6:" VVAƒ6@>/†B4eb‚=       ­ oR ‚TD3  > h»²¿F^ ‚  ÆM'‚0F7¿BLd‚63Ó ]jlƒ7Û
+& NG† +TTc1SOJ†vwn\-Yr < A#i ΔÙñ«à @ !ù + , ˆ€ ‚ƒ„
+
+7=?;>;"0† :TD   AB8Q"
+6 / 3 7 ;(i...@„0=:†G J ‚73¬ QfA²6 ‚D,<¼
+3
+eÁ„34
+‚ .À  1&‚2*^¼Z  ‚  \ †<N|›‚Zk-xc   Vz^8
+ Oyrv j{CÏe H`ÌX !ù + , ‹€ ‚ƒ„
+†7:F  F6†ƒ63F bDD=
+&†: E; ƒA
+. TA>3‚#*N D‚K$+® C9‚ \T®6Jc‚ -¾ : ¶ gY(®DZ ‚e u® ,A‚ KXq† Æ oPt2Q=FAl! ++ OjLM^ +a G 1 n...@ñÕÁa ; ======================================= --- /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/impl/CellListImpl.java Tue May 4 08:08:32 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/impl/CellListImpl.java Tue May 4 10:22:18 2010
@@ -53,6 +53,14 @@
   private final List<T> data = new ArrayList<T>();

   private int dataSize;
+
+  /**
+ * A boolean indicating whether or not the data size has ever been set. If the
+   * data size has never been set, then we will always pass it along to the
+   * view.
+   */
+  private boolean dataSizeInitialized;
+
   private Delegate<T> delegate;
   private final PagingListView<T> listView;
   private Pager<T> pager;
@@ -276,9 +284,10 @@
    * @param size the overall size
    */
   public void setDataSize(int size) {
-    if (size == this.dataSize) {
+    if (dataSizeInitialized && size == this.dataSize) {
       return;
     }
+    dataSizeInitialized = true;
     this.dataSize = size;
     updateDataAndView();
     onSizeChanged();
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTree.css Fri Apr 30 09:33:31 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTree.css Tue May 4 10:22:18 2010
@@ -1,3 +1,8 @@
+.emptyMessage {
+  padding-left: 16px;
+  font-style: italic;
+}
+
 .item {
   padding: 4px 3px;
 }
@@ -7,7 +12,6 @@
 }

 .openItem {
-  font-weight: bold;
 }

 @sprite .selectedItem {
@@ -17,3 +21,8 @@
   height: auto;
   overflow: auto;
 }
+
+.showMoreButton {
+  padding-left: 16px;
+  outline: none;
+}
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTree.java Fri Apr 30 09:33:31 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTree.java Tue May 4 10:22:18 2010
@@ -38,11 +38,21 @@
  */
 public class CellTree extends Composite implements HasAnimation {

+  /**
+   * The default number of children to show under a tree node.
+   */
+  private static final int DEFAULT_LIST_SIZE = 25;
+
   /**
    * Styles used by this widget.
    */
   public static interface Style extends CssResource {

+    /**
+     * Applied to the empty message.
+     */
+    String emptyMessage();
+
     /**
      * Applied to tree items.
      */
@@ -62,6 +72,11 @@
      * Applied to selected tree items.
      */
     String selectedItem();
+
+    /**
+     * Applied to the show more button.
+     */
+    String showMoreButton();
   }

   /**
@@ -74,6 +89,11 @@
      */
     ImageResource cellTreeClosedItem();

+    /**
+     * An image indicating that a node is loading.
+     */
+    ImageResource cellTreeLoading();
+
     /**
      * An image indicating an open branch.
      */
@@ -311,6 +331,11 @@
    */
   private final String closedImageHtml;

+  /**
+   * The default number of children to display under each node.
+   */
+  private int defaultNodeSize = DEFAULT_LIST_SIZE;
+
   /**
    * The maximum width of the open and closed images.
    */
@@ -322,10 +347,9 @@
   private boolean isAnimationEnabled;

   /**
-   * The message displayed while child nodes are loading.
+   * The HTML used to generate the loading image.
    */
-  // TODO(jlabanca): I18N loading HTML, or remove the text.
-  private String loadingHtml = "Loading...";
+  private final String loadingImageHtml;

   /**
    * The HTML used to generate the open image.
@@ -377,9 +401,12 @@
     // Initialize the open and close images strings.
     ImageResource treeOpen = resources.cellTreeOpenItem();
     ImageResource treeClosed = resources.cellTreeClosedItem();
+    ImageResource treeLoading = resources.cellTreeLoading();
     openImageHtml = getImageHtml(treeOpen);
     closedImageHtml = getImageHtml(treeClosed);
-    imageWidth = Math.max(treeOpen.getWidth(), treeClosed.getWidth());
+    loadingImageHtml = getImageHtml(treeLoading);
+ imageWidth = Math.max(Math.max(treeOpen.getWidth(), treeClosed.getWidth()),
+        treeLoading.getWidth());

     // We use one animation for the entire tree.
     setAnimation(SlideAnimation.create());
@@ -406,13 +433,12 @@
   }

   /**
- * Get the HTML string that is displayed while nodes wait for their children
-   * to load.
+ * Get the default maximum number of children to display under each tree node.
    *
-   * @return the loading HTML string
+   * @return the default node size
    */
-  public String getLoadingHtml() {
-    return loadingHtml;
+  public int getDefaultNodeSize() {
+    return defaultNodeSize;
   }

   public CellTreeViewModel getTreeViewModel() {
@@ -436,14 +462,10 @@
     if (nodeView != null && nodeView != rootNode) {
       if ("click".equals(event.getType())) {
         // Open the node when the open image is clicked.
-        Element showFewerElem = nodeView.getShowFewerElement();
         Element showMoreElem = nodeView.getShowMoreElement();
         if (nodeView.getImageElement().isOrHasChild(target)) {
           nodeView.setOpen(!nodeView.isOpen());
           return;
- } else if (showFewerElem != null && showFewerElem.isOrHasChild(target)) {
-          nodeView.showFewer();
-          return;
} else if (showMoreElem != null && showMoreElem.isOrHasChild(target)) {
           nodeView.showMore();
           return;
@@ -480,13 +502,15 @@
   }

   /**
- * Set the HTML string that will be displayed when a node is waiting for its
-   * child nodes to load.
+ * Set the default number of children to display beneath each child node. If
+   * more nodes are available, a button will appear at the end of the list
+ * allowing the user to show more items. Changing this value will not affect
+   * tree nodes that are already open.
    *
-   * @param loadingHtml the HTML string
+   * @param defaultNodeSize the max
    */
-  public void setLoadingHtml(String loadingHtml) {
-    this.loadingHtml = loadingHtml;
+  public void setDefaultNodeSize(int defaultNodeSize) {
+    this.defaultNodeSize = defaultNodeSize;
   }

   /**
@@ -504,6 +528,13 @@
   int getImageWidth() {
     return imageWidth;
   }
+
+  /**
+   * @return the HTML to render the loading image.
+   */
+  String getLoadingImageHtml() {
+    return loadingImageHtml;
+  }

   /**
    * @return the HTML to render the open image.
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTreeNodeView.java Fri Apr 30 09:33:31 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/tree/client/CellTreeNodeView.java Tue May 4 10:22:18 2010
@@ -22,6 +22,7 @@
 import com.google.gwt.bikeshed.list.shared.Range;
 import com.google.gwt.bikeshed.list.shared.SelectionModel;
 import com.google.gwt.bikeshed.tree.client.CellTreeViewModel.NodeInfo;
+import com.google.gwt.dom.client.AnchorElement;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
@@ -42,11 +43,6 @@
  */
 class CellTreeNodeView<T> extends UIObject {

-  /**
-   * The default number of children to show under a tree node.
-   */
-  private static final int DEFAULT_LIST_SIZE = 100;
-
   /**
    * The element used in place of an image when a node has no children.
    */
@@ -84,21 +80,23 @@
    */
   private static class NodeListView<C> implements PagingListView<C> {

+    private final int defaultPageSize;
     private final CellListImpl<C> impl;
     private CellTreeNodeView<?> nodeView;
     private Map<Object, CellTreeNodeView<?>> savedViews;

     public NodeListView(final NodeInfo<C> nodeInfo,
-        final CellTreeNodeView<?> nodeView) {
+        final CellTreeNodeView<?> nodeView, int pageSize) {
+      this.defaultPageSize = pageSize;
       this.nodeView = nodeView;

       final Cell<C> cell = nodeInfo.getCell();
-      impl = new CellListImpl<C>(this, DEFAULT_LIST_SIZE,
+      impl = new CellListImpl<C>(this, pageSize,
           nodeView.ensureChildContainer()) {

         @Override
         public void setData(List<C> values, int start) {
-          showOrHide(nodeView.loadingMessageElem, false);
+          nodeView.updateImage(false);

           // Ensure that we have a children array.
           if (nodeView.children == null) {
@@ -157,7 +155,6 @@
               child.nodeInfo = savedChild.nodeInfo;
               child.nodeInfoLoaded = savedChild.nodeInfoLoaded;
               child.open = savedChild.open;
-              child.showFewerElem = savedChild.showFewerElem;
               child.showMoreElem = savedChild.showMoreElem;

// Swap the node view in the child. We reuse the same NodeListView
@@ -248,11 +245,14 @@
       impl.setPager(new Pager<C>() {
         public void onRangeOrSizeChanged(PagingListView<C> listView) {
           // Assumes a page start of 0.
-          showOrHide(nodeView.showMoreElem,
-              impl.getDataSize() > impl.getPageSize());
-          showOrHide(nodeView.showFewerElem,
-              impl.getPageSize() > DEFAULT_LIST_SIZE);
-          showOrHide(nodeView.emptyMessageElem, impl.getDataSize() == 0);
+          int dataSize = impl.getDataSize();
+          showOrHide(nodeView.showMoreElem, dataSize > impl.getPageSize());
+          if (dataSize == 0) {
+            showOrHide(nodeView.emptyMessageElem, true);
+            nodeView.updateImage(false);
+          } else {
+            showOrHide(nodeView.emptyMessageElem, false);
+          }
         }
       });
     }
@@ -267,6 +267,10 @@
     public int getDataSize() {
       return impl.getDataSize();
     }
+
+    public int getDefaultPageSize() {
+      return defaultPageSize;
+    }

     public int getPageSize() {
       return impl.getPageSize();
@@ -358,11 +362,6 @@
    */
   private NodeListView<?> listView;

-  /**
-   * The element used when the children are loading.
-   */
-  private Element loadingMessageElem;
-
   /**
    * The info about children of this node.
    */
@@ -388,15 +387,10 @@
    */
   private final NodeInfo<T> parentNodeInfo;

-  /**
-   * The element used to display less children.
-   */
-  private Element showFewerElem;
-
   /**
    * The element used to display more children.
    */
-  private Element showMoreElem;
+  private AnchorElement showMoreElem;

   /**
    * The {...@link CellTree} that this node belongs to.
@@ -476,20 +470,23 @@
       if (nodeInfo != null) {
         // Add a loading message.
         ensureChildContainer();
-        showOrHide(loadingMessageElem, true);
-        showOrHide(showFewerElem, false);
         showOrHide(showMoreElem, false);
         showOrHide(emptyMessageElem, false);
+        if (!isRootNode()) {
+          setStyleName(getCellParent(), tree.getStyle().openItem(), true);
+        }
         ensureAnimationFrame().getStyle().setProperty("display", "");
+        updateImage(true);
         onOpen(nodeInfo);
       }
     } else {
+      if (!isRootNode()) {
+        setStyleName(getCellParent(), tree.getStyle().openItem(), false);
+      }
       cleanup();
       tree.maybeAnimateTreeNode(this);
-    }
-
-    // Update the image.
-    updateImage();
+      updateImage(false);
+    }
   }

   /**
@@ -583,33 +580,12 @@
    * @param <C> the child data type of the node
    */
   protected <C> void onOpen(final NodeInfo<C> nodeInfo) {
-    NodeListView<C> view = new NodeListView<C>(nodeInfo, this);
+    NodeListView<C> view = new NodeListView<C>(nodeInfo, this,
+        tree.getDefaultNodeSize());
     listView = view;
     view.setSelectionModel(nodeInfo.getSelectionModel());
     nodeInfo.setView(view);
   }
-
-  /**
-   * Update the image based on the current state.
-   */
-  protected void updateImage() {
-    // Early out if this is a root node.
-    if (parentNode == null) {
-      return;
-    }
-
-    // Replace the image element with a new one.
- String html = open ? tree.getOpenImageHtml() : tree.getClosedImageHtml();
-    if (nodeInfoLoaded && nodeInfo == null) {
-      html = LEAF_IMAGE;
-    }
-    Element tmp = Document.get().createDivElement();
-    tmp.setInnerHTML(html);
-    Element imageElem = tmp.getFirstChildElement();
-
-    Element oldImg = getImageElement();
-    oldImg.getParentElement().replaceChild(imageElem, oldImg);
-  }

   /**
    * Ensure that the animation frame exists and return it.
@@ -650,45 +626,72 @@
       contentContainer = Document.get().createDivElement();
       ensureAnimationFrame().appendChild(contentContainer);

-      loadingMessageElem = Document.get().createDivElement();
-      loadingMessageElem.setInnerHTML(tree.getLoadingHtml());
-      showOrHide(loadingMessageElem, false);
-      contentContainer.appendChild(loadingMessageElem);
-
       // TODO(jlabanca): I18N no data string.
       emptyMessageElem = Document.get().createDivElement();
-      emptyMessageElem.setInnerHTML("<i>no data</i>");
+      emptyMessageElem.setInnerHTML("no data");
+      setStyleName(emptyMessageElem, tree.getStyle().emptyMessage(), true);
       showOrHide(emptyMessageElem, false);
       contentContainer.appendChild(emptyMessageElem);

-      showMoreElem = Document.get().createPushButtonElement();
+      showMoreElem = Document.get().createAnchorElement();
+      showMoreElem.setHref("javascript:;");
       showMoreElem.setInnerText("Show more");
+      setStyleName(showMoreElem, tree.getStyle().showMoreButton(), true);
       showOrHide(showMoreElem, false);
       contentContainer.appendChild(showMoreElem);
-
-      showFewerElem = Document.get().createPushButtonElement();
-      showFewerElem.setInnerText("Show fewer");
-      showOrHide(showFewerElem, false);
-      contentContainer.appendChild(showFewerElem);
     }
     return contentContainer;
   }
-
-  Element getShowFewerElement() {
-    return showFewerElem;
-  }

   Element getShowMoreElement() {
     return showMoreElem;
   }

   void showFewer() {
-    int maxSize = Math.max(DEFAULT_LIST_SIZE, listView.impl.getPageSize()
-        - DEFAULT_LIST_SIZE);
+    int defaultPageSize = listView.getDefaultPageSize();
+    int maxSize = Math.max(defaultPageSize, listView.impl.getPageSize()
+        - defaultPageSize);
     listView.impl.setPageSize(maxSize);
   }

   void showMore() {
- listView.impl.setPageSize(listView.impl.getPageSize() + DEFAULT_LIST_SIZE);
+    listView.impl.setPageSize(listView.impl.getPageSize()
+        + listView.getDefaultPageSize());
+  }
+
+  /**
+   * Check if this node is a root node.
+   *
+   * @return true if a root node
+   */
+  private boolean isRootNode() {
+    return parentNode == null;
+  }
+
+  /**
+   * Update the image based on the current state.
+   *
+   * @param isLoading true if still loading data
+   */
+  private void updateImage(boolean isLoading) {
+    // Early out if this is a root node.
+    if (isRootNode()) {
+      return;
+    }
+
+    // Replace the image element with a new one.
+    String html = tree.getClosedImageHtml();
+    if (open) {
+ html = isLoading ? tree.getLoadingImageHtml() : tree.getOpenImageHtml();
+    }
+    if (nodeInfoLoaded && nodeInfo == null) {
+      html = LEAF_IMAGE;
+    }
+    Element tmp = Document.get().createDivElement();
+    tmp.setInnerHTML(html);
+    Element imageElem = tmp.getFirstChildElement();
+
+    Element oldImg = getImageElement();
+    oldImg.getParentElement().replaceChild(imageElem, oldImg);
   }
 }
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseTree.java Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseTree.java Tue May 4 10:22:18 2010
@@ -18,26 +18,32 @@
 import com.google.gwt.bikeshed.cells.client.Cell;
 import com.google.gwt.bikeshed.cells.client.IconCellDecorator;
 import com.google.gwt.bikeshed.cells.client.TextCell;
+import com.google.gwt.bikeshed.list.client.ListView;
+import com.google.gwt.bikeshed.list.shared.AsyncListViewAdapter;
 import com.google.gwt.bikeshed.list.shared.ListViewAdapter;
 import com.google.gwt.bikeshed.list.shared.ProvidesKey;
+import com.google.gwt.bikeshed.list.shared.Range;
 import com.google.gwt.bikeshed.list.shared.SingleSelectionModel;
import com.google.gwt.bikeshed.list.shared.SelectionModel.SelectionChangeEvent; import com.google.gwt.bikeshed.list.shared.SelectionModel.SelectionChangeHandler;
 import com.google.gwt.bikeshed.tree.client.CellTree;
 import com.google.gwt.bikeshed.tree.client.CellTreeViewModel;
+import com.google.gwt.dom.client.Style.Overflow;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.sample.bikeshed.style.client.Styles;
 import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
-import com.google.gwt.sample.expenses.gwt.request.EmployeeRecordChanged;
+import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
 import com.google.gwt.user.client.ui.Composite;
-
+import com.google.gwt.valuestore.shared.Property;
+
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;

 /**
  * The employee tree located on the left of the app.
  */
-public class ExpenseTree extends Composite implements
-    Receiver<List<EmployeeRecord>>, EmployeeRecordChanged.Handler {
+public class ExpenseTree extends Composite {

   /**
    * Custom listener for this widget.
@@ -47,10 +53,10 @@
     /**
      * Called when the user selects a tree item.
      *
-     * @param category the selected category name
+     * @param department the selected department name
      * @param employee the selected employee
      */
-    void onSelection(String category, EmployeeRecord employee);
+    void onSelection(String department, EmployeeRecord employee);
   }

   /**
@@ -71,6 +77,45 @@
       });
     }
   }
+
+  /**
+   * The {...@link ListViewAdapter} used for Employee lists.
+   */
+  private class EmployeeListViewAdapter extends
+      AsyncListViewAdapter<EmployeeRecord> implements
+      Receiver<List<EmployeeRecord>> {
+
+    private final String department;
+
+    public EmployeeListViewAdapter(String department) {
+      this.department = department;
+    }
+
+    @Override
+    public void addView(ListView<EmployeeRecord> view) {
+      super.addView(view);
+
+      // Request the count anytime a view is added.
+ requestFactory.employeeRequest().countEmployeesByDepartment(department).to(
+          new Receiver<Long>() {
+            public void onSuccess(Long response) {
+              updateDataSize(response.intValue(), true);
+            }
+          }).fire();
+    }
+
+    public void onSuccess(List<EmployeeRecord> response) {
+      updateViewData(0, response.size(), response);
+    }
+
+    @Override
+    protected void onRangeChanged(ListView<EmployeeRecord> view) {
+      Range range = view.getRange();
+      requestFactory.employeeRequest().findEmployeeEntriesByDepartment(
+          department, range.getStart(), range.getLength()).forProperties(
+          getEmployeeMenuProperties()).to(this).fire();
+    }
+  }

   /**
    * The {...@link CellTreeViewModel} used to browse expense reports.
@@ -78,9 +123,9 @@
   private class ExpensesTreeViewModel implements CellTreeViewModel {

     /**
-     * The category cell singleton.
+     * The department cell singleton.
      */
- private final Cell<String> categoryCell = new IconCellDecorator<String>( + private final Cell<String> departmentCell = new IconCellDecorator<String>(
         Styles.resources().groupIcon(), TextCell.getInstance());

     /**
@@ -91,11 +136,16 @@
     public <T> NodeInfo<?> getNodeInfo(T value) {
       if (value == null) {
         // Top level.
-        return new DefaultNodeInfo<String>(categories, categoryCell,
+        return new DefaultNodeInfo<String>(departments, departmentCell,
             selectionModel, null);
+      } else if (isAllDepartment(value)) {
+        // Employees are not displayed under the 'All' Department.
+        return null;
       } else if (value instanceof String) {
         // Second level.
-        return new DefaultNodeInfo<EmployeeRecord>(employees, employeeCell,
+        EmployeeListViewAdapter adapter = new EmployeeListViewAdapter(
+            (String) value);
+        return new DefaultNodeInfo<EmployeeRecord>(adapter, employeeCell,
             selectionModel, null);
       }

@@ -103,31 +153,36 @@
     }

     public boolean isLeaf(Object value) {
-      return !isCategory(value);
+      return !isDepartment(value) || isAllDepartment(value);
     }

-    private boolean isCategory(Object value) {
-      return categories.getList().contains(value.toString());
+    /**
+     * @return true if the object is the All department
+     */
+    private boolean isAllDepartment(Object value) {
+      return departments.getList().get(0).equals(value);
+    }
+
+    /**
+     * @return true if the object is a department
+     */
+    private boolean isDepartment(Object value) {
+      return departments.getList().contains(value.toString());
     }
   }

   /**
-   * The adapter that provides categories.
+   * The adapter that provides departments.
    */
- private ListViewAdapter<String> categories = new ListViewAdapter<String>(); + private ListViewAdapter<String> departments = new ListViewAdapter<String>();

   /**
-   * The adapter that provides employees.
+   * The last selected department.
    */
- private ListViewAdapter<EmployeeRecord> employees = new ListViewAdapter<EmployeeRecord>();
+  private String lastDepartment;

   /**
-   * The last selected category.
-   */
-  private String lastCategory;
-
-  /**
-   * The last selected category.
+   * The last selected employee.
    */
   private EmployeeRecord lastEmployee;

@@ -136,6 +191,11 @@
    */
   private Listener listener;

+  /**
+   * The factory used to send requests.
+   */
+  private ExpensesRequestFactory requestFactory;
+
   /**
    * The shared {...@link SingleSelectionModel}.
    */
@@ -149,26 +209,26 @@
   public ExpenseTree() {
     createTree();
     initWidget(tree);
-
-    // Initialize the categories.
-    List<String> categoriesList = categories.getList();
-    categoriesList.add("All");
-    categoriesList.add("Sales");
-    categoriesList.add("Marketing");
-    categoriesList.add("Engineering");
-  }
-
-  public void onEmployeeChanged(EmployeeRecordChanged event) {
-    employees.refresh();
-  }
-
-  public void onSuccess(List<EmployeeRecord> response) {
-    employees.setList(response);
+    getElement().getStyle().setOverflow(Overflow.AUTO);
+
+    // Initialize the departments.
+    List<String> departmentList = departments.getList();
+    departmentList.add("All");
+    departmentList.add("Engineering");
+    // The Finance department is empty.
+    departmentList.add("Finance");
+    departmentList.add("Marketing");
+    departmentList.add("Operations");
+    departmentList.add("Sales");
   }

   public void setListener(Listener listener) {
     this.listener = listener;
   }
+
+  public void setRequestFactory(ExpensesRequestFactory factory) {
+    this.requestFactory = factory;
+  }

   /**
    * Create the {...@link CellTree}.
@@ -181,16 +241,16 @@
         Object selected = selectionModel.getSelectedObject();
         if (selected == null) {
           lastEmployee = null;
-          lastCategory = null;
+          lastDepartment = null;
         } else if (selected instanceof EmployeeRecord) {
           lastEmployee = (EmployeeRecord) selected;
         } else if (selected instanceof String) {
           lastEmployee = null;
-          lastCategory = (String) selected;
+          lastDepartment = (String) selected;
         }

         if (listener != null) {
-          listener.onSelection(lastCategory, lastEmployee);
+          listener.onSelection(lastDepartment, lastEmployee);
         }
       }
     });
@@ -207,4 +267,11 @@
     tree = new CellTree(new ExpensesTreeViewModel(), null);
     tree.setAnimationEnabled(true);
   }
-}
+
+  private Collection<Property<?>> getEmployeeMenuProperties() {
+    List<Property<?>> columns = new ArrayList<Property<?>>();
+    columns.add(EmployeeRecord.displayName);
+    columns.add(EmployeeRecord.userName);
+    return columns;
+  }
+}
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Expenses.java Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Expenses.java Tue May 4 10:22:18 2010
@@ -48,7 +48,6 @@
   };

   private ExpensesRequestFactory requestFactory;
-  private String searchCategory;
   private ExpensesShell shell;

   public void onModuleLoad() {
@@ -67,17 +66,12 @@

     // Listen for requests from ExpenseTree.
     expenseTree.setListener(new ExpenseTree.Listener() {
-      public void onSelection(String category, EmployeeRecord employee) {
-        if (category != null && !category.equals(searchCategory)) {
-          // TODO(jlabanca): Limit employees using category.
- requestFactory.employeeRequest().findAllEmployees().forProperties(
-              getEmployeeMenuProperties()).to(expenseTree).fire();
-        }
-        searchCategory = category;
+      public void onSelection(String department, EmployeeRecord employee) {
         expenseList.setEmployee(employee);
         shell.showExpenseDetails(false);
       }
     });
+    expenseTree.setRequestFactory(requestFactory);

     // Listen for requests from the ExpenseList.
     expenseList.setListener(new ExpenseList.Listener() {
@@ -96,13 +90,6 @@

     eventBus.addHandler(ExpenseRecordChanged.TYPE, expenseDetails);
   }
-
-  private Collection<Property<?>> getEmployeeMenuProperties() {
-    List<Property<?>> columns = new ArrayList<Property<?>>();
-    columns.add(EmployeeRecord.displayName);
-    columns.add(EmployeeRecord.userName);
-    return columns;
-  }

   private Collection<Property<?>> getExpenseColumns() {
     List<Property<?>> columns = new ArrayList<Property<?>>();
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java Sat May 1 07:18:49 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/EmployeeRequest.java Tue May 4 10:22:18 2010
@@ -38,6 +38,24 @@
       public String getDomainMethodName() {
         return "countEmployees";
       }
+
+      public Class<?> getReturnType() {
+        return Long.class;
+      }
+
+      public boolean isReturnTypeList() {
+        return false;
+      }
+    },
+
+    COUNT_EMPLOYEES_BY_DEPARTMENT {
+      public String getDomainMethodName() {
+        return "countEmployeesByDepartment";
+      }
+
+      public Class<?>[] getParameterTypes() {
+        return new Class[]{java.lang.String.class};
+      }

       public Class<?> getReturnType() {
         return Long.class;
@@ -74,7 +92,17 @@
       }

       public Class<?>[] getParameterTypes() {
-        return new Class[] {int.class, int.class};
+        return new Class[]{int.class, int.class};
+      }
+    },
+
+    FIND_EMPLOYEE_ENTRIES_BY_DEPARTMENT {
+      public String getDomainMethodName() {
+        return "findEmployeeEntriesByDepartment";
+      }
+
+      public Class<?>[] getParameterTypes() {
+        return new Class[]{java.lang.String.class, int.class, int.class};
       }
     };

@@ -101,6 +129,13 @@
   @ServerOperation("COUNT_EMPLOYEES")
   RequestFactory.RequestObject<Long> countEmployees();

+  /**
+   * @return a request object
+   */
+  @ServerOperation("COUNT_EMPLOYEES_BY_DEPARTMENT")
+  RequestFactory.RequestObject<Long> countEmployeesByDepartment(
+      String department);
+
   /**
    * @return a request object
    */
@@ -119,4 +154,11 @@
   @ServerOperation("FIND_EMPLOYEE_ENTRIES")
   RecordListRequest<EmployeeRecord> findEmployeeEntries(int firstResult,
       int maxResults);
-}
+
+  /**
+   * @return a request object
+   */
+  @ServerOperation("FIND_EMPLOYEE_ENTRIES_BY_DEPARTMENT")
+  RecordListRequest<EmployeeRecord> findEmployeeEntriesByDepartment(
+      String department, int firstResult, int maxResults);
+}
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java Tue May 4 10:22:18 2010
@@ -32,121 +32,108 @@

   // Must be in sync with DESCRIPTIONS
   private static final String[] CATEGORIES = {
-    "Dining", "Dining", "Dining",
-    "Lodging", "Lodging",
-    "Local Transportation", "Local Transportation", "Local Transportation",
-    "Air Travel", "Air Travel",
- "Office Supplies", "Office Supplies", "Office Supplies", "Office Supplies",
-  };
+      "Dining", "Dining", "Dining", "Lodging", "Lodging",
+ "Local Transportation", "Local Transportation", "Local Transportation",
+      "Air Travel", "Air Travel", "Office Supplies", "Office Supplies",
+      "Office Supplies", "Office Supplies",};
+
+  private static final String[] DEPARTMENTS = {
+      "Sales", "Marketing", "Engineering", "Operations"};

   // Must be in sync with CATEGORIES
   private static final String[] DESCRIPTIONS = {
-    "Breakfast", "Lunch", "Dinner",
-    "Hotel", "Bed & Breakfast",
-    "Train fare", "Taxi fare", "Bus ticket",
-    "Flight from ATL to SFO", "Flight from SFO to ATL",
-    "Paperclips", "Stapler", "Scissors", "Paste",
-  };
-
+ "Breakfast", "Lunch", "Dinner", "Hotel", "Bed & Breakfast", "Train fare",
+      "Taxi fare", "Bus ticket", "Flight from ATL to SFO",
+ "Flight from SFO to ATL", "Paperclips", "Stapler", "Scissors", "Paste",};
+
+  private static final String[] FIRST_NAMES = {
+      "Amy", "Bob", "Catherine", "Dave", "Earl", "Flin", "George", "Harriot",
+      "Ingrid", "John", "Katy", "Leo", "Mike", "Nancy", "Owen", "Paul",
+      "Reece", "Sally", "Terry", "Val", "Wes", "Xavier", "Zack"};
+
+  private static final String[] LAST_NAMES = {
+      "Awesome", "Bravo", "Cool", "Fantastic", "Great", "Happy",
+      "Ignoranomous", "Krazy", "Luminous", "Magnanimous", "Outstanding",
+      "Perfect", "Radical", "Stellar", "Terrific", "Wonderful"};
+
   private static final String[] NOTES = {
-    "Need approval by Monday", "Show me the money",
-    "Please bill to the Widgets project",
-    "High priority",
-    "Review A.S.A.P."
-  };
+      // Some entries do not have notes.
+      "", "Need approval by Monday", "Show me the money",
+ "Please bill to the Widgets project", "High priority", "Review A.S.A.P."};
+
+  private static final String[] PURPOSES = {
+      "Spending lots of money", "Team building diamond cutting offsite",
+      "Visit to Istanbul", "ISDN modem for telecommuting", "Sushi offsite",
+      "Baseball card research", "Potato chip cooking offsite",
+      "Money laundering", "Donut day"};

   Random rand = new Random();
-
+
   @Override
   protected void initDb() {
     long size = Employee.countEmployees();
     if (size > 1) {
       return;
     }
-    // initialize
-    Employee abc = new Employee();
-    abc.setUserName("abc");
-    abc.setDisplayName("Able B. Charlie");
-    abc.persist();
-
-    Employee def = new Employee();
-    def.setUserName("def");
-    def.setDisplayName("Delta E. Foxtrot");
-    def.setSupervisorKey(abc.getId());
-    def.persist();
-
-    Employee ghi = new Employee();
-    ghi.setUserName("ghi");
-    ghi.setDisplayName("George H. Indigo");
-    ghi.setSupervisorKey(abc.getId());
-    ghi.persist();
-
-    for (String purpose : new String[] {
-        "Spending lots of money", "Team building diamond cutting offsite",
-        "Visit to Istanbul"}) {
-      Report report = new Report();
-      report.setReporterKey(abc.getId());
-      report.setCreated(getDate());
-      report.setPurpose(purpose);
-      report.setNotes(getNote());
-      report.persist();
-
-      addExpenses(report.getId());
-    }
-
-    for (String purpose : new String[] {"Money laundering", "Donut day"}) {
-      Report report = new Report();
-      report.setCreated(getDate());
-      report.setReporterKey(def.getId());
-      report.setPurpose(purpose);
-      report.setNotes(getNote());
-      report.persist();
-
-      addExpenses(report.getId());
-    }
-
-    for (String purpose : new String[] {
-        "ISDN modem for telecommuting", "Sushi offsite",
-        "Baseball card research", "Potato chip cooking offsite"}) {
-      Report report = new Report();
-      report.setCreated(getDate());
-      report.setReporterKey(ghi.getId());
-      report.setPurpose(purpose);
-      report.setNotes(getNote());
-      report.persist();
-
-      addExpenses(report.getId());
-    }
-
-    for (int i = 0; i < 1000; i++) {
-      Report report = new Report();
-      report.setCreated(getDate());
-      report.setReporterKey(ghi.getId());
-      report.setPurpose("Report " + i);
-      report.setNotes(getNote());
-      report.persist();
-
-      addExpenses(report.getId());
-    }
-  }
-
+
+    // Initialize the database.
+    for (int i = 0; i < 100; i++) {
+      addEmployee();
+    }
+  }
+
+  /**
+   * Add a randomly generated employee.
+   */
+  private void addEmployee() {
+    Employee abc = new Employee();
+    String firstName = nextValue(FIRST_NAMES);
+    String lastName = nextValue(LAST_NAMES);
+    String username = (firstName.charAt(0) + lastName).toLowerCase();
+    abc.setUserName(username);
+    abc.setDisplayName(firstName + " " + lastName);
+    abc.setDepartment(nextValue(DEPARTMENTS));
+    abc.persist();
+
+    addReports(abc.getId());
+  }
+
   private void addExpenses(Long reportId) {
     int num = rand.nextInt(5) + 1;
     for (int i = 0; i < num; i++) {
-      String[] descCat = getDescriptionAndCategory();
-
+      int index = rand.nextInt(DESCRIPTIONS.length);
       Expense detail = new Expense();
       detail.setReportId(reportId);
-      detail.setDescription(descCat[0]);
+      detail.setDescription(DESCRIPTIONS[index]);
       detail.setDate(getDate());
       detail.setAmount(rand.nextInt(25000) / 100.0);
-      detail.setCategory(descCat[1]);
+      detail.setCategory(CATEGORIES[index]);
       detail.setApproval("");
       detail.setReasonDenied("");
       detail.persist();
     }
   }
-
+
+  /**
+   * Add a randomly generated report.
+   *
+   * @param employeeId the id of the employee who created the report
+   */
+  private void addReports(Long employeeId) {
+    // Add 1-20 expense reports.
+    int reportCount = 1 + rand.nextInt(20);
+    for (int i = 0; i < reportCount; i++) {
+      Report report = new Report();
+      report.setCreated(getDate());
+      report.setReporterKey(employeeId);
+      report.setPurpose(nextValue(PURPOSES));
+      report.setNotes(nextValue(NOTES));
+      report.persist();
+
+      addExpenses(report.getId());
+    }
+  }
+
   private Date getDate() {
     long now = new Date().getTime();
     // Go back up to 90 days from the current date
@@ -154,15 +141,13 @@
     return new Date(now - dateOffset);
   }

-  private String[] getDescriptionAndCategory() {
-    String[] dc = new String[2];
-    int index = rand.nextInt(DESCRIPTIONS.length);
-    dc[0] = DESCRIPTIONS[index];
-    dc[1] = CATEGORIES[index];
-    return dc;
-  }
-
-  private String getNote() {
-    return NOTES[rand.nextInt(NOTES.length)];
+  /**
+   * Get the next random value from an array.
+   *
+   * @param array the array
+   * @return a random value from the array
+   */
+  private String nextValue(String[] array) {
+    return array[rand.nextInt(array.length)];
   }
 }
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java Mon May 3 13:28:30 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java Tue May 4 10:22:18 2010
@@ -23,6 +23,7 @@
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.Query;
 import javax.persistence.Version;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
@@ -41,6 +42,17 @@
       em.close();
     }
   }
+
+  public static long countEmployeesByDepartment(String department) {
+    EntityManager em = entityManager();
+    try {
+ Query query = em.createQuery("select count(o) from Employee o where o.department=:department");
+      query.setParameter("department", department);
+      return ((Number) query.getSingleResult()).longValue();
+    } finally {
+      em.close();
+    }
+  }

   public static final EntityManager entityManager() {
     return EMF.get().createEntityManager();
@@ -85,10 +97,30 @@
       em.close();
     }
   }
+
+  @SuppressWarnings("unchecked")
+  public static List<Employee> findEmployeeEntriesByDepartment(
+      String department, int firstResult, int maxResults) {
+    EntityManager em = entityManager();
+    try {
+ Query query = em.createQuery("select o from Employee o WHERE o.department =:department");
+      query.setFirstResult(firstResult);
+      query.setMaxResults(maxResults);
+      query.setParameter("department", department);
+      List resultList = query.getResultList();
+      // force it to materialize
+      resultList.size();
+      return resultList;
+    } finally {
+      em.close();
+    }
+  }

   @Size(min = 3, max = 30)
   private String userName;

+  private String department;
+
   @NotNull
   private String displayName;

@@ -106,6 +138,10 @@
   @Column(name = "version")
   private Integer version;

+  public String getDepartment() {
+    return department;
+  }
+
   public String getDisplayName() {
     return this.displayName;
   }
@@ -148,6 +184,10 @@
       em.close();
     }
   }
+
+  public void setDepartment(String department) {
+    this.department = department;
+  }

   public void setDisplayName(String displayName) {
     this.displayName = displayName;

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to