Revision: 7890
Author: [email protected]
Date: Wed Apr  7 11:20:20 2010
Log: Clean up mail sample app; simplify table refresh for selection changes

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

Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=7890

Added:
/trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/DefaultSelectionModel.java
Modified:
/trunk/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
 /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/ProvidesKey.java
 /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
 /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/mail/MailSample.gwt.xml
/trunk/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java
 /trunk/bikeshed/war/Mail.html

=======================================
--- /dev/null
+++ /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/DefaultSelectionModel.java Wed Apr 7 11:20:20 2010
@@ -0,0 +1,90 @@
+/*
+ * 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.list.shared;
+
+import com.google.gwt.bikeshed.list.shared.SelectionModel.AbstractSelectionModel;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {...@link SelectionModel} that allows records to be selected according to
+ * a subclass-defined rule, plus a list of positive or negative exceptions.
+ *
+ * @param <T> the data type of records in the list
+ */
+public abstract class DefaultSelectionModel<T> extends
+    AbstractSelectionModel<T> {
+
+  private final Map<T, Boolean> exceptions = new HashMap<T, Boolean>();
+
+  /**
+   * Removes all exceptions.
+   */
+  public void clearExceptions() {
+    exceptions.clear();
+    scheduleSelectionChangeEvent();
+  }
+
+  /**
+   * Returns true if the given object should be selected by default.
+   * Subclasses implement this method in order to define the default
+   * selection behavior.
+   */
+  public abstract boolean isDefaultSelected(T object);
+
+  /**
+   * If the given object is marked as an exception, return the exception
+   * value.  Otherwise, return the value of isDefaultSelected for the given
+   * object.
+   */
+  public boolean isSelected(T object) {
+    // Check exceptions first
+    Boolean exception = exceptions.get(object);
+    if (exception != null) {
+      return exception.booleanValue();
+    }
+    // If not in exceptions, return the default
+    return isDefaultSelected(object);
+  }
+
+  /**
+   * Sets an object's selection state.  If the object is currently marked
+   * as an exception, and the new selected state differs from the previous
+   * selected state, the object is removed from the list of exceptions.
+ * Otherwise, the object is added to the list of exceptions with the given
+   * selected state.
+   */
+  public void setSelected(T object, boolean selected) {
+    Boolean currentlySelected = exceptions.get(object);
+    if (currentlySelected != null
+        && currentlySelected.booleanValue() != selected) {
+      exceptions.remove(object);
+    } else {
+      exceptions.put(object, selected);
+    }
+
+    scheduleSelectionChangeEvent();
+  }
+
+  /**
+   * Copies the exceptions map into a user-supplied map.
+   */
+  protected void getExceptions(Map<T, Boolean> output) {
+    output.clear();
+    output.putAll(exceptions);
+  }
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java Wed Apr 7 05:51:51 2010 +++ /trunk/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java Wed Apr 7 11:20:20 2010
@@ -50,7 +50,7 @@

   private class TableSelectionHandler implements SelectionChangeHandler {
     public void onSelectionChange(SelectionChangeEvent event) {
-      refresh();
+      refreshSelection();
     }
   }

@@ -184,6 +184,20 @@
     listReg.setRangeOfInterest(curPage * pageSize, pageSize);
     updateRowVisibility();
   }
+
+  public void refreshSelection() {
+    NodeList<TableRowElement> rows = tbody.getRows();
+    for (int indexOnPage = 0; indexOnPage < pageSize; indexOnPage++) {
+      TableRowElement row = rows.getItem(indexOnPage);
+      T q = data.get(indexOnPage);
+ if (q != null && selectionModel != null && selectionModel.isSelected(q)) {
+        row.setClassName("pagingTableListView selected");
+      } else {
+        row.setClassName("pagingTableListView "
+            + ((indexOnPage & 0x1) == 0 ? "evenRow" : "oddRow"));
+      }
+    }
+  }

   /**
    * Set the current visible page.
=======================================
--- /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/ProvidesKey.java Fri Apr 2 11:25:19 2010 +++ /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/ProvidesKey.java Wed Apr 7 11:20:20 2010
@@ -22,7 +22,7 @@
  * <p>
  * The key must implement a coherent set of {...@link #equals(Object)} and
* {...@link #hashCode()} methods. If the item type is a not uniquely identifiable,
- * such as a list of {...@link String}, the index can be used a the key.
+ * such as a list of {...@link String}, the index can be used as the key.
  * </p>
  *
  * @param <T> the data type of records in the list
=======================================
--- /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java Wed Apr 7 05:28:43 2010 +++ /trunk/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java Wed Apr 7 11:20:20 2010
@@ -119,10 +119,6 @@
     public void fireEvent(GwtEvent<?> event) {
       handlerManager.fireEvent(event);
     }
-
-    public void setSelected(T object, boolean selected) {
-      scheduleSelectionChangeEvent();
-    }

     /**
* Schedules a {...@link SelectionModel.SelectionChangeEvent} to fire at the
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/mail/MailSample.gwt.xml Mon Mar 29 05:42:31 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/mail/MailSample.gwt.xml Wed Apr 7 11:20:20 2010
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 0.0.999//EN" "http://google-web-toolkit.googlecode.com/svn/tags/0.0.999/distro-source/core/src/gwt-module.dtd";>
-<module rename-to='mail'>
+<module rename-to='mailsample'>
   <!-- Inherit the core Web Toolkit stuff.                        -->
   <inherits name='com.google.gwt.user.User'/>
   <inherits name='com.google.gwt.bikeshed.list.List'/>
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java Wed Apr 7 05:28:43 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java Wed Apr 7 11:20:20 2010
@@ -15,13 +15,14 @@
  */
 package com.google.gwt.sample.bikeshed.mail.client;

+import com.google.gwt.bikeshed.cells.client.ButtonCell;
 import com.google.gwt.bikeshed.cells.client.CheckboxCell;
 import com.google.gwt.bikeshed.cells.client.FieldUpdater;
 import com.google.gwt.bikeshed.list.client.PagingTableListView;
 import com.google.gwt.bikeshed.list.client.SimpleColumn;
 import com.google.gwt.bikeshed.list.client.TextColumn;
+import com.google.gwt.bikeshed.list.shared.DefaultSelectionModel;
 import com.google.gwt.bikeshed.list.shared.ListListModel;
-import com.google.gwt.bikeshed.list.shared.SelectionModel.AbstractSelectionModel;
 import com.google.gwt.core.client.EntryPoint;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -34,6 +35,7 @@
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.TextBox;

+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
@@ -44,124 +46,25 @@
  */
 public class MailSample implements EntryPoint, ClickHandler {

-  class MailSelectionModel extends AbstractSelectionModel<Message> {
-    private static final int ALL = 0;
-    private static final int NONE = 1;
-    private static final int READ = 2;
-    private static final int SENDER = 3;
-    private static final int SUBJECT = 4;
-    private static final int UNREAD = 5;
-
-    // Use a TreeMap in order to get sorted diagnostic output
- private Map<Integer, Boolean> exceptions = new TreeMap<Integer, Boolean>();
-
-    private String search;
-    private int type = NONE;
-
-    public boolean isSelected(Message object) {
-      // Check exceptions
-      int id = object.id;
-      Boolean exception = exceptions.get(id);
-      if (exception != null) {
-        return exception.booleanValue();
-      }
-      // If not in exceptions, return the default
-      return isDefaultSelected(object);
+  static class MailSelectionModel extends DefaultSelectionModel<Message> {
+    enum Type {
+      ALL(), NONE(), READ(), SENDER(), SUBJECT(), UNREAD();
+
+      Type() {
+        typeMap.put(this.toString(), this);
+      }
     }

-    public void setSearch(String search) {
-      this.search = canonicalize(search);
-      updateListeners();
-    }
-
-    @Override
-    public void setSelected(Message object, boolean selected) {
-      addException(object.id, selected);
-      updateListeners();
-    }
-
-    public void setType(int type) {
-      this.type = type;
-      exceptions.clear();
-      updateListeners();
-    }
+    // A map from enum names to their values
+    private static Map<String, Type> typeMap = new HashMap<String, Type>();
+
+    private String search;
+    private Type type = Type.NONE;

     @Override
-    public String toString() {
-      StringBuilder sb = new StringBuilder();
+    public boolean isDefaultSelected(Message object) {
       switch (type) {
         case NONE:
-          sb.append("NONE ");
-          break;
-        case ALL:
-          sb.append("ALL ");
-          break;
-        case READ:
-          sb.append("READ ");
-          break;
-        case UNREAD:
-          sb.append("UNREAD ");
-          break;
-        case SENDER:
-          sb.append("SENDER ");
-          sb.append(search);
-          sb.append(' ');
-          break;
-        case SUBJECT:
-          sb.append("SUBJECT ");
-          sb.append(search);
-          sb.append(' ');
-          break;
-      }
-
-      boolean first = true;
-      for (int i : exceptions.keySet()) {
-        if (exceptions.get(i) != Boolean.TRUE) {
-          continue;
-        }
-
-        if (first) {
-          first = false;
-          sb.append("+msg(s) ");
-        }
-        sb.append(i);
-        sb.append(' ');
-      }
-
-      first = true;
-      for (int i : exceptions.keySet()) {
-        if (exceptions.get(i) != Boolean.FALSE) {
-          continue;
-        }
-
-        if (first) {
-          first = false;
-          sb.append("-msg(s) ");
-        }
-        sb.append(i);
-        sb.append(' ');
-      }
-
-      return sb.toString();
-    }
-
-    private void addException(int id, boolean selected) {
-      Boolean currentlySelected = exceptions.get(id);
-      if (currentlySelected != null
-          && currentlySelected.booleanValue() != selected) {
-        exceptions.remove(id);
-      } else {
-        exceptions.put(id, selected);
-      }
-    }
-
-    private String canonicalize(String input) {
-      return input.toUpperCase();
-    }
-
-    private boolean isDefaultSelected(Message object) {
-      switch (type) {
-        case NONE:
           return false;
         case ALL:
           return true;
@@ -184,13 +87,67 @@
       }
     }

-    private void updateListeners() {
-      selectionLabel.setText("Selected " + this.toString());
+    public void setSearch(String search) {
+      this.search = canonicalize(search);
       scheduleSelectionChangeEvent();
     }
+
+    public void setType(String type) {
+      this.type = typeMap.get(type);
+      clearExceptions();
+      scheduleSelectionChangeEvent();
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append(type.name());
+      sb.append(' ');
+      if (type == Type.SENDER || type == Type.SUBJECT) {
+        sb.append(search);
+        sb.append(' ');
+      }
+
+      // Copy the exceptions into a TreeMap in order to sort by message id
+ TreeMap<Message, Boolean> exceptions = new TreeMap<Message, Boolean>();
+      getExceptions(exceptions);
+
+      appendExceptions(sb, exceptions, true);
+      appendExceptions(sb, exceptions, false);
+
+      return sb.toString();
+    }
+
+    protected void scheduleSelectionChangeEvent() {
+      selectionLabel.setText("Selected " + this.toString());
+      super.scheduleSelectionChangeEvent();
+    }
+
+    private void appendExceptions(StringBuilder sb,
+        Map<Message, Boolean> exceptions, boolean selected) {
+      boolean first = true;
+      for (Message message : exceptions.keySet()) {
+        if (exceptions.get(message) != selected) {
+          continue;
+        }
+
+        if (first) {
+          first = false;
+          sb.append(selected ? '+' : '-');
+          sb.append("msg(s) ");
+        }
+        sb.append(message.id);
+        sb.append(' ');
+      }
+    }
+
+    private String canonicalize(String input) {
+      return input.toUpperCase();
+    }
   }

-  class Message {
+  // Hashing, comparison, and equality are based on the message id
+  class Message implements Comparable<Message> {
     int id;
     boolean isRead;
     String sender;
@@ -202,6 +159,10 @@
       this.sender = sender;
       this.subject = subject;
     }
+
+    public int compareTo(Message o) {
+      return id - o.id;
+    }

     @Override
     public boolean equals(Object obj) {
@@ -238,6 +199,8 @@
           + subject + ", read=" + isRead + "]";
     }
   }
+
+  private static Label selectionLabel = new Label("Selected NONE");

   private static final String[] senders = {
       "[email protected]", "[email protected]", "[email protected]", "Mai Oleta",
@@ -247,48 +210,31 @@
       "Kaan Boulier", "Emilee Naoma", "Atino Alice", "Debby Renay",
       "Versie Nereida", "Ramon Erikson", "Karole Crissy", "Nelda Olsen",
       "Mariana Dann", "Reda Cheyenne", "Edelmira Jody", "Agueda Shante",
-      "Marla Dorris"};
+      "Marla Dorris"
+  };

   private static final String[] subjects = {
       "GWT rocks", "What's a widget?", "Money in Nigeria",
       "Impress your colleagues with bling-bling", "Degree available",
-      "Rolex Watches", "Re: Re: yo bud", "Important notice"};
-
-  private Button allButton = new Button("Select All");
-  private Button allOnPageButton = new Button("Select All On This Page");
-  private Button noneButton = new Button("Select None");
-  private Button readButton = new Button("Select Read");
-  private Label selectionLabel = new Label();
+      "Rolex Watches", "Re: Re: yo bud", "Important notice"
+  };
+
   private MailSelectionModel selectionModel = new MailSelectionModel();
-  private Button senderButton = new Button("Search Senders");
-  private Button subjectButton = new Button("Search Subject");

   private PagingTableListView<Message> table;

-  private Button unreadButton = new Button("Select Unread");
-
   // Handle events for all buttons here in order to avoid creating multiple
   // ClickHandlers
   public void onClick(ClickEvent event) {
-    Button source = (Button) event.getSource();
-    if (source == noneButton) {
-      selectionModel.setType(MailSelectionModel.NONE);
-    } else if (source == allOnPageButton) {
-      selectionModel.setType(MailSelectionModel.NONE);
+    String id = ((Button) event.getSource()).getElement().getId();
+    if ("PAGE".equals(id)) {
+      // selectionModel.setType(MailSelectionModel.NONE);
       List<Message> selectedItems = table.getDisplayedItems();
       for (Message item : selectedItems) {
         selectionModel.setSelected(item, true);
       }
-    } else if (source == allButton) {
-      selectionModel.setType(MailSelectionModel.ALL);
-    } else if (source == readButton) {
-      selectionModel.setType(MailSelectionModel.READ);
-    } else if (source == unreadButton) {
-      selectionModel.setType(MailSelectionModel.UNREAD);
-    } else if (source == senderButton) {
-      selectionModel.setType(MailSelectionModel.SENDER);
-    } else if (source == subjectButton) {
-      selectionModel.setType(MailSelectionModel.SUBJECT);
+    } else {
+      selectionModel.setType(id);
     }
   }

@@ -347,6 +293,21 @@
     };
     table.addColumn(subjectColumn, "Subject");

+    SimpleColumn<Message, String> toggleColumn =
+      new SimpleColumn<Message, String>(ButtonCell.getInstance()) {
+      @Override
+      public String getValue(Message object) {
+        return object.isRead ? "Mark Unread" : "Mark Read";
+      }
+    };
+ toggleColumn.setFieldUpdater(new FieldUpdater<Message, String, Void>() { + public void update(int index, Message object, String value, Void viewData) {
+        object.isRead = !object.isRead;
+        table.refresh();
+      }
+    });
+    table.addColumn(toggleColumn, "Toggle Read/Unread");
+
     Label searchLabel = new Label("Search Sender or Subject:");
     final TextBox searchBox = new TextBox();
     searchBox.addKeyUpHandler(new KeyUpHandler() {
@@ -355,30 +316,29 @@
       }
     });

-    noneButton.addClickHandler(this);
-    allOnPageButton.addClickHandler(this);
-    allButton.addClickHandler(this);
-    readButton.addClickHandler(this);
-    unreadButton.addClickHandler(this);
-    senderButton.addClickHandler(this);
-    subjectButton.addClickHandler(this);
-
     HorizontalPanel panel = new HorizontalPanel();
     panel.add(searchLabel);
     panel.add(searchBox);

     RootPanel.get().add(panel);
+    RootPanel.get().add(makeButton("Search Subject", "SUBJECT"));
+    RootPanel.get().add(makeButton("Search Senders", "SENDER"));
     RootPanel.get().add(new HTML("<br>"));
     RootPanel.get().add(table);
     RootPanel.get().add(new HTML("<br>"));
-    RootPanel.get().add(noneButton);
-    RootPanel.get().add(allOnPageButton);
-    RootPanel.get().add(allButton);
-    RootPanel.get().add(readButton);
-    RootPanel.get().add(unreadButton);
-    RootPanel.get().add(subjectButton);
-    RootPanel.get().add(senderButton);
+    RootPanel.get().add(makeButton("Select None", "NONE"));
+    RootPanel.get().add(makeButton("Select All On This Page", "PAGE"));
+    RootPanel.get().add(makeButton("Select All", "ALL"));
+    RootPanel.get().add(makeButton("Select Read", "READ"));
+    RootPanel.get().add(makeButton("Select Unread", "UNREAD"));
     RootPanel.get().add(new HTML("<hr>"));
     RootPanel.get().add(selectionLabel);
   }
-}
+
+  private Button makeButton(String label, String id) {
+    Button button = new Button(label);
+    button.getElement().setId(id);
+    button.addClickHandler(this);
+    return button;
+  }
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java Fri Apr 2 11:25:19 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java Wed Apr 7 11:20:20 2010
@@ -44,7 +44,6 @@
       return selectedSet.contains(object);
     }

-    @Override
     public void setSelected(Object object, boolean selected) {
       if (selected) {
         selectedSet.add(object);
@@ -52,7 +51,7 @@
         selectedSet.remove(object);
       }
       label.setText("Selected " + selectedSet.toString());
-      super.setSelected(object, selected);
+      scheduleSelectionChangeEvent();
     }
   }

=======================================
--- /trunk/bikeshed/war/Mail.html       Mon Mar 29 05:42:31 2010
+++ /trunk/bikeshed/war/Mail.html       Wed Apr  7 11:20:20 2010
@@ -4,7 +4,7 @@
     <meta http-equiv="content-type" content="text/html; charset=UTF-8">
     <link type="text/css" rel="stylesheet" href="Mail.css">
     <title>Mail Sample</title>
- <script type="text/javascript" language="javascript" src="mail/mail.nocache.js"></script> + <script type="text/javascript" language="javascript" src="mailsample/mailsample.nocache.js"></script>
   </head>

   <body>

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

To unsubscribe, reply using "remove me" as the subject.

Reply via email to