Revision: 8037
Author: jlaba...@google.com
Date: Tue May  4 08:08:32 2010
Log: Adding option to highlight a search term in text cells.

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

Added:
/branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/highlightIcon.png
Modified:
/branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/CellTable.java /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/impl/CellListImpl.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.ui.xml /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java

=======================================
--- /dev/null
+++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/highlightIcon.png Tue May 4 08:08:32 2010
@@ -0,0 +1,12 @@
+‰PNG
+ + +IHDR 醟 sRGB ®Î
+é      pHYs          šœ     tIME Ú    3<RQ>   ÀIDAT8ËÝ“±
+Â0 EŸq‘ h(¢¤u ºÌ‚â
+²NF €:i˜€0 )è˜ÀP £0 "@@@ |é ÷åûº;ÉJD„“v;E_
+ø’~ dŒç£ å ´ö4ÍãwêÙ±‹  D`³ ²ì ´ßC Ájåë,ƒ¶
+ xsµª‚ñ F#OšB]÷¸Ñt
+Ö^jk½÷ÖjÛ-Ä1
+ Ý ­a½†áðʼnf3˜LÀ¹.y óù  ¹’sœ1 Y,èxÎy/In}õ¿ í »
+{ Ž±ù?    IEND®B`‚
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/CellTable.java Mon May 3 12:44:17 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/CellTable.java Tue May 4 08:08:32 2010
@@ -453,6 +453,13 @@
       }
     }
   }
+
+  /**
+   * Redraw the table using the existing data.
+   */
+  public void redraw() {
+    impl.redraw();
+  }

   /**
    * Redraw the table, requesting data from the delegate.
@@ -594,4 +601,3 @@
     return element.clientHeight;
   }-*/;
 }
-
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/impl/CellListImpl.java Tue Apr 27 10:56:01 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/bikeshed/list/client/impl/CellListImpl.java Tue May 4 08:08:32 2010
@@ -34,9 +34,9 @@
 import java.util.Set;

 /**
- * Implementation of {...@link CellList}. This class is subject to change or
- * deletion. Do not rely on this class.
- *
+ * Implementation of {...@link com.google.gwt.bikeshed.list.client.CellList}. This
+ * class is subject to change or deletion. Do not rely on this class.
+ *
  * @param <T> the data type of items in the list
  */
 public abstract class CellListImpl<T> {
@@ -115,7 +115,7 @@
   /**
    * Get the list of data within the current range. The data may not be
    * complete.
-   *
+   *
    * @return the list of data
    */
   public List<T> getData() {
@@ -124,7 +124,7 @@

   /**
    * Get the overall data size.
-   *
+   *
    * @return the data size
    */
   public int getDataSize() {
@@ -133,7 +133,7 @@

   /**
* Get the number of items that are within the current page and data range.
-   *
+   *
    * @return the number of displayed items
    */
   public int getDisplayedItemCount() {
@@ -164,22 +164,27 @@
   public SelectionModel<? super T> getSelectionModel() {
     return selectionModel;
   }
+
+  /**
+   * Redraw the list with the current data.
+   */
+  public void redraw() {
+    setData(data, pageStart);
+  }

   /**
    * Request data from the delegate.
    */
   public void refresh() {
-    if (delegate != null) {
-      if (!refreshScheduled) {
-        refreshScheduled = true;
-        Scheduler.get().scheduleFinally(refreshCommand);
-      }
+    if (delegate != null && !refreshScheduled) {
+      refreshScheduled = true;
+      Scheduler.get().scheduleFinally(refreshCommand);
     }
   }

   /**
    * Set the data in the list.
-   *
+   *
    * @param values the new data
    * @param valuesStart the start index of the values
    */
@@ -267,7 +272,7 @@

   /**
    * Set the overall size of the list.
-   *
+   *
    * @param size the overall size
    */
   public void setDataSize(int size) {
@@ -289,7 +294,7 @@

   /**
    * Set the number of items to show on each page.
-   *
+   *
    * @param pageSize the page size
    */
   public void setPageSize(int pageSize) {
@@ -304,7 +309,7 @@

   /**
    * Set the start index of the range.
-   *
+   *
    * @param pageStart the start index
    */
   public void setPageStart(int pageStart) {
@@ -347,7 +352,7 @@

   /**
    * Set the {...@link SelectionModel}, optionally triggering an update.
-   *
+   *
    * @param selectionModel the new {...@link SelectionModel}
    * @param updateSelection true to update selection
    */
@@ -378,7 +383,7 @@
   /**
* Convert the specified HTML into DOM elements and return the parent of the
    * DOM elements.
-   *
+   *
    * @param html the HTML to convert
    * @return the parent element
    */
@@ -389,14 +394,14 @@

   /**
* Check whether or not the cells in the list depend on the selection state.
-   *
+   *
    * @return true if cells depend on selection, false if not
    */
   protected abstract boolean dependsOnSelection();

   /**
    * Construct the HTML that represents the list of items.
-   *
+   *
    * @param sb the {...@link StringBuilder} to build into
    * @param values the values to render
    * @param start the start index
@@ -425,7 +430,7 @@
   /**
    * Mark an element as selected or unselected. This is called when a cells
    * selection state changes, but the cell does not depend on selection.
-   *
+   *
    * @param elem the element to modify
    * @param selected true if selected, false if not
    */
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.java Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.java Tue May 4 08:08:32 2010
@@ -17,7 +17,6 @@

 import com.google.gwt.bikeshed.cells.client.Cell;
 import com.google.gwt.bikeshed.cells.client.DateCell;
-import com.google.gwt.bikeshed.cells.client.TextCell;
 import com.google.gwt.bikeshed.cells.client.ValueUpdater;
 import com.google.gwt.bikeshed.list.client.CellTable;
 import com.google.gwt.bikeshed.list.client.Column;
@@ -35,6 +34,7 @@
 import com.google.gwt.event.dom.client.FocusHandler;
 import com.google.gwt.event.dom.client.KeyUpEvent;
 import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.regexp.shared.RegExp;
 import com.google.gwt.requestfactory.shared.Receiver;
 import com.google.gwt.sample.expenses.gwt.request.EmployeeRecord;
 import com.google.gwt.sample.expenses.gwt.request.ExpensesRequestFactory;
@@ -59,9 +59,52 @@
 public class ExpenseList extends Composite implements
     Receiver<List<ReportRecord>>, ReportRecordChanged.Handler {

-  private static final String TEXTBOX_DEFAULT_TEXT = "search";
-
-  private static final String TEXTBOX_DISABLED_COLOR = "#aaaaaa";
+  /**
+   * A text box that displays default text.
+   */
+  private static class DefaultTextBox extends TextBox {
+
+    /**
+     * The text color used when the box is disabled and empty.
+     */
+    private static final String TEXTBOX_DISABLED_COLOR = "#aaaaaa";
+
+    private final String defaultText;
+
+    public DefaultTextBox(final String defaultText) {
+      this.defaultText = defaultText;
+      resetDefaultText();
+
+      // Add focus and blur handlers.
+      addFocusHandler(new FocusHandler() {
+        public void onFocus(FocusEvent event) {
+          getElement().getStyle().clearColor();
+          if (defaultText.equals(getText())) {
+            setText("");
+          }
+        }
+      });
+      addBlurHandler(new BlurHandler() {
+        public void onBlur(BlurEvent event) {
+          if ("".equals(getText())) {
+            resetDefaultText();
+          }
+        }
+      });
+    }
+
+    public String getDefaultText() {
+      return defaultText;
+    }
+
+    /**
+     * Reset the text box to the default text.
+     */
+    public void resetDefaultText() {
+      setText(defaultText);
+      getElement().getStyle().setColor(TEXTBOX_DISABLED_COLOR);
+    }
+  }

   interface ExpenseListUiBinder extends UiBinder<Widget, ExpenseList> {
   }
@@ -77,6 +120,24 @@
      */
     void onReportSelected(ReportRecord report);
   }
+
+  /**
+   * A cell used to highlight search text.
+   */
+  private class HighlightCell extends Cell<String> {
+
+ private static final String replaceString = "<span style='color:red;font-weight:bold;'>$1</span>";
+
+    @Override
+    public void render(String value, Object viewData, StringBuilder sb) {
+      if (value != null) {
+        if (searchRegExp != null) {
+          value = searchRegExp.replace(value, replaceString);
+        }
+        sb.append(value);
+      }
+    }
+  }

   /**
    * The adapter used to retrieve reports.
@@ -90,10 +151,12 @@

private static ExpenseListUiBinder uiBinder = GWT.create(ExpenseListUiBinder.class);

+  @UiField(provided = true)
+  DefaultTextBox highlightBox;
   @UiField
   SimplePager<ReportRecord> pager;
-  @UiField
-  TextBox searchBox;
+  @UiField(provided = true)
+  DefaultTextBox searchBox;

   /**
    * The main table. We provide this in the constructor before calling
@@ -119,11 +182,6 @@
    */
   private String orderBy = ReportRecord.purpose.getName();

-  /**
-   * True to sort in descending order.
-   */
-  private boolean orderByDesc = false;
-
   private Listener listener;

   /**
@@ -141,6 +199,11 @@
    */
   private ExpensesRequestFactory requestFactory;

+  /**
+   * The string that the user searched for.
+   */
+  private RegExp searchRegExp;
+
   /**
    * The timer used to delay searches until the user stops typing.
    */
@@ -160,33 +223,28 @@

     // Initialize the widget.
     createTable();
+    highlightBox = new DefaultTextBox("highlight");
+    searchBox = new DefaultTextBox("search");
     initWidget(uiBinder.createAndBindUi(this));

     // Add the view to the adapter.
     reports.addView(table);

-    // Setup the search box.
-    searchBox.setText(TEXTBOX_DEFAULT_TEXT);
-    searchBox.getElement().getStyle().setColor(TEXTBOX_DISABLED_COLOR);
+    // Listen for key events from the text boxes.
     searchBox.addKeyUpHandler(new KeyUpHandler() {
       public void onKeyUp(KeyUpEvent event) {
         searchTimer.schedule(500);
       }
     });
-    searchBox.addFocusHandler(new FocusHandler() {
-      public void onFocus(FocusEvent event) {
-        searchBox.getElement().getStyle().clearColor();
-        if (TEXTBOX_DEFAULT_TEXT.equals(searchBox.getText())) {
-          searchBox.setText("");
-        }
-      }
-    });
-    searchBox.addBlurHandler(new BlurHandler() {
-      public void onBlur(BlurEvent event) {
-        if ("".equals(searchBox.getText())) {
-          searchBox.setText(TEXTBOX_DEFAULT_TEXT);
- searchBox.getElement().getStyle().setColor(TEXTBOX_DISABLED_COLOR);
-        }
+    highlightBox.addKeyUpHandler(new KeyUpHandler() {
+      public void onKeyUp(KeyUpEvent event) {
+        String text = highlightBox.getText();
+        if (text.length() > 0) {
+          searchRegExp = RegExp.compile("(" + text + ")", "i");
+        } else {
+          searchRegExp = null;
+        }
+        table.redraw();
       }
     });
   }
@@ -270,7 +328,10 @@

         // Request sorted rows.
         orderBy = property.getName();
-        orderByDesc = header.getReverseSort();
+        if (header.getReverseSort()) {
+          orderBy += " DESC";
+        }
+        searchBox.resetDefaultText();
         requestReports();
       }
     });
@@ -298,7 +359,7 @@
     });

     // Purpose column.
-    addColumn("Purpose", TextCell.getInstance(),
+    addColumn("Purpose", new HighlightCell(),
         new GetValue<ReportRecord, String>() {
           public String getValue(ReportRecord object) {
             return object.getPurpose();
@@ -313,7 +374,7 @@
     }, ReportRecord.created);

     // Notes column.
-    addColumn("Notes", TextCell.getInstance(),
+    addColumn("Notes", new HighlightCell(),
         new GetValue<ReportRecord, String>() {
           public String getValue(ReportRecord object) {
             return object.getNotes();
@@ -331,11 +392,20 @@

     // Get the parameters.
     String startsWith = searchBox.getText();
-    if (TEXTBOX_DEFAULT_TEXT.equals(startsWith)) {
+    if (searchBox.getDefaultText().equals(startsWith)) {
       startsWith = "";
     }
     Range range = table.getRange();
     Long employeeId = employee == null ? -1 : new Long(employee.getId());
+
+    // If a search string is specified, the results will not be sorted.
+    if (startsWith.length() > 0) {
+      for (SortableHeader header : allHeaders) {
+        header.setSorted(false);
+        header.setReverseSort(false);
+      }
+      table.refreshHeaders();
+    }

     // Request the total data size.
     if (isCountStale) {
@@ -350,7 +420,7 @@

     // Request reports in the current range.
     requestFactory.reportRequest().findReportEntriesBySearch(employeeId,
-        startsWith, orderBy, orderByDesc ? 1 : 0, range.getStart(),
-        range.getLength()).forProperties(reportColumns).to(this).fire();
+ startsWith, orderBy, range.getStart(), range.getLength()).forProperties(
+        reportColumns).to(this).fire();
   }
 }
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.ui.xml Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.ui.xml Tue May 4 08:08:32 2010
@@ -4,6 +4,8 @@
   xmlns:g="urn:import:com.google.gwt.user.client.ui"
   xmlns:l='urn:import:com.google.gwt.bikeshed.list.client'>

+  <ui:image
+    field='highlightIcon' />
   <ui:image
     field='searchIcon' />

@@ -19,8 +21,13 @@
     .textBox {
       width: 250px;
       border: 1px solid #5478af;
+      padding-left: 5px;
     }

+    @sprite .highlightIcon {
+      gwt-image: 'highlightIcon';
+    }
+
     @sprite .searchIcon {
       gwt-image: 'searchIcon';
     }
@@ -34,12 +41,13 @@
         width='100%'
         cellspacing='0'
         cellpadding='0'>
-        <tr>
-          <td>
+        <tr rowspan='2'>
+          <td rowspan='2'>
             <h1>Reports</h1>
           </td>
           <td
             align='right'>
+            <i>Search:</i>
             <g:TextBox
               styleName='{style.textBox}'
               ui:field='searchBox'></g:TextBox>
@@ -52,6 +60,20 @@
         </tr>
         <tr>
           <td
+            align='right'>
+            <i>Highlight:</i>
+            <g:TextBox
+              styleName='{style.textBox}'
+              ui:field='highlightBox'></g:TextBox>
+          </td>
+          <td
+            width='60'>
+            <div
+              class='{style.highlightIcon}' />
+          </td>
+        </tr>
+        <tr>
+          <td
             align='center'
             colspan='3'>
             <l:SimplePager
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java Tue May 4 08:08:32 2010
@@ -114,7 +114,7 @@
       public Class<?>[] getParameterTypes() {
         return new Class[]{
             java.lang.Long.class, java.lang.String.class,
-            java.lang.String.class, int.class, int.class, int.class};
+            java.lang.String.class, int.class, int.class};
       }
     };

@@ -145,7 +145,8 @@
    * @return a request object
    */
   @ServerOperation("COUNT_REPORTS_BY_SEARCH")
- RequestFactory.RequestObject<Long> countReportsBySearch(Long employeeId, String startsWith);
+  RequestFactory.RequestObject<Long> countReportsBySearch(Long employeeId,
+      String startsWith);

   /**
    * @return a request object
@@ -170,8 +171,7 @@
    */
   @ServerOperation("FIND_REPORT_ENTRIES_BY_SEARCH")
RecordListRequest<ReportRecord> findReportEntriesBySearch(Long employeeId,
-      String startsWith, String orderBy, int descending, int firstResult,
-      int maxResults);
+      String startsWith, String orderBy, int firstResult, int maxResults);

   /**
    * @return a request object
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java Mon May 3 11:15:42 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java Tue May 4 08:08:32 2010
@@ -15,8 +15,6 @@
  */
 package com.google.gwt.sample.expenses.server.domain;

-import java.util.Collections;
-import java.util.Comparator;
 import java.util.Date;
 import java.util.List;

@@ -34,102 +32,6 @@
  */
 @Entity
 public class Report {
-
-  /**
-   * Comparator for the created field.
-   */
- private static final FieldComparator<Date> CREATED_COMPARATOR_ASC = new FieldComparator<Date>(false) {
-    @Override
-    Date getField(Report r) {
-      return r.getCreated();
-    }
-  };
-
-  /**
-   * Comparator for the created field.
-   */
- private static final FieldComparator<Date> CREATED_COMPARATOR_DESC = new FieldComparator<Date>(true) {
-    @Override
-    Date getField(Report r) {
-      return r.getCreated();
-    }
-  };
-
-  /**
-   * Comparator for the notes field.
-   */
- private static final FieldComparator<String> NOTES_COMPARATOR_ASC = new FieldComparator<String>(false) {
-    @Override
-    String getField(Report r) {
-      return r.getNotes();
-    }
-  };
-
-  /**
-   * Comparator for the notes field.
-   */
- private static final FieldComparator<String> NOTES_COMPARATOR_DESC = new FieldComparator<String>(true) {
-    @Override
-    String getField(Report r) {
-      return r.getNotes();
-    }
-  };
-
-  /**
-   * Comparator for the purpose field.
-   */
- private static final FieldComparator<String> PURPOSE_COMPARATOR_ASC = new FieldComparator<String>(false) {
-    @Override
-    String getField(Report r) {
-      return r.getPurpose();
-    }
-  };
-
-  /**
-   * Comparator for the purpose field.
-   */
- private static final FieldComparator<String> PURPOSE_COMPARATOR_DESC = new FieldComparator<String>(true) {
-    @Override
-    String getField(Report r) {
-      return r.getPurpose();
-    }
-  };
-
-  /**
-   * An {...@link Comparator} used to compare fields in this class.
-   *
-   * @param <C> the comparable within the report to compare
-   */
- private abstract static class FieldComparator<C extends Comparable<C>> implements Comparator<Report> {
-
-    private static int NEXT = 0;
-    private final int id = NEXT++;
-    private final boolean descending;
-
-    FieldComparator(boolean descending) {
-      this.descending = descending;
-    }
-
-    public final int compare(Report o1, Report o2) {
-      // Compare the fields.
-      C c1 = (o1 == null) ? null : getField(o1);
-      C c2 = (o2 == null) ? null : getField(o2);
-      if (c1 == null && c2 == null) {
-        return 0;
-      } else if (c1 == null) {
-        return descending ? -1 : 1;
-      } else if (c2 == null) {
-        return descending ? 1 : -1;
-      }
-      return descending ? c2.compareTo(c1) : c1.compareTo(c2);
-    }
-
-    public final int getId() {
-      return id;
-    }
-
-    abstract C getField(Report r);
-  }

   public static long countReports() {
     EntityManager em = entityManager();
@@ -143,7 +45,7 @@
public static long countReportsBySearch(Long employeeId, String startsWith) {
     EntityManager em = entityManager();
     try {
-      Query query = queryReportsBySearch(em, employeeId, startsWith, true);
+ Query query = queryReportsBySearch(em, employeeId, startsWith, null, true);
       return ((Number) query.getSingleResult()).longValue();
     } finally {
       em.close();
@@ -195,34 +97,16 @@

   @SuppressWarnings("unchecked")
   public static List<Report> findReportEntriesBySearch(Long employeeId,
-      String startsWith, String orderBy, int descending, int firstResult,
-      int maxResults) {
-    // TODO(jlabanca): Switch descending to a boolean when it works.
+      String startsWith, String orderBy, int firstResult, int maxResults) {
     EntityManager em = entityManager();
     try {
- Query query = queryReportsBySearch(em, employeeId, startsWith, false); + Query query = queryReportsBySearch(em, employeeId, startsWith, orderBy,
+          false);
       query.setFirstResult(firstResult);
       query.setMaxResults(maxResults);
       List<Report> reportList = query.getResultList();
       // force it to materialize
       reportList.size();
-
-      // Order the results.
- // We have to sort manually because app engine only supports an inequality
-      // check on one field, and we already use it for startsWith.
-      if (orderBy != null) {
-        // TODO(jlabanca): Can we do full data ordering and search?
-        if (orderBy.equals("purpose")) {
- Collections.sort(reportList, descending == 0 ? PURPOSE_COMPARATOR_ASC
-              : PURPOSE_COMPARATOR_DESC);
-        } else if (orderBy.equals("notes")) {
- Collections.sort(reportList, descending == 0 ? NOTES_COMPARATOR_ASC
-              : NOTES_COMPARATOR_DESC);
-        } else if (orderBy.equals("created")) {
- Collections.sort(reportList, descending == 0 ? CREATED_COMPARATOR_ASC
-              : CREATED_COMPARATOR_DESC);
-        }
-      }

       return reportList;
     } finally {
@@ -246,15 +130,18 @@
   }

   /**
-   * Query for reports based on the search parameters.
+   * Query for reports based on the search parameters. If startsWith is
+   * specified, the results will not be ordered.
    *
+   * @param em the {...@link EntityManager} to use
    * @param employeeId the employee id
-   * @param startsWith the start substring
-   * @param isCount true to query on the count
+   * @param startsWith the starting string
+   * @param orderBy the order of the results
+   * @param isCount true to query on the count only
    * @return the query
    */
private static Query queryReportsBySearch(EntityManager em, Long employeeId,
-      String startsWith, boolean isCount) {
+      String startsWith, String orderBy, boolean isCount) {
     // Construct a query string.
     boolean isFirstStatement = true;
     boolean hasEmployee = employeeId != null && employeeId >= 0;
@@ -272,6 +159,9 @@
       queryString += " o.purposeLowerCase >=:startsWith";
       queryString += " AND o.purposeLowerCase <=:startsWithZ";
     }
+    if (!hasStartsWith && orderBy != null && orderBy.length() >= 0) {
+      queryString += " ORDER BY " + orderBy;
+    }

     // Construct the query;
     Query query = em.createQuery(queryString);

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

Reply via email to