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