Replaced HasKey with ProvidesKey and moved it from Column/NodeInfo to ListModel. Updating SelectionModel to use handlers and have only one setSelected method, firing events in a finally command to avoid multiple view updates.
http://gwt-code-reviews.appspot.com/307801/show
Review by: jgw@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7866 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
index 976d8a1..f296f4f 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/Column.java
@@ -18,6 +18,7 @@
import com.google.gwt.bikeshed.cells.client.Cell;
import com.google.gwt.bikeshed.cells.client.FieldUpdater;
import com.google.gwt.bikeshed.cells.client.ValueUpdater;
+import com.google.gwt.bikeshed.list.shared.ProvidesKey;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
@@ -37,28 +38,16 @@
// TODO - when can we get rid of a view data object?
// TODO - should viewData implement some interface? (e.g., with commit/rollback/dispose)
// TODO - have a ViewDataColumn superclass / SimpleColumn subclass
-public abstract class Column<T, C, V> implements HasCell<T, C, V> {
+public abstract class Column<T, C, V> {
protected final Cell<C, V> cell;
protected FieldUpdater<T, C, V> fieldUpdater;
- /**
- * An instance of HasKey<T> that is used to provide a key for a row object
- * for the purpose of retrieving view data. A value of null means that
- * the row object itself will be used as the key.
- */
- protected HasKey<T> hasKey;
-
protected Map<Object, V> viewDataMap = new HashMap<Object, V>();
- public Column(Cell<C, V> cell, HasKey<T> hasKey) {
- this.cell = cell;
- this.hasKey = hasKey;
- }
-
public Column(Cell<C, V> cell) {
- this(cell, null);
+ this.cell = cell;
}
public boolean consumesEvents() {
@@ -76,8 +65,8 @@
public abstract C getValue(T object);
public void onBrowserEvent(Element elem, final int index, final T object,
- NativeEvent event) {
- Object key = hasKey == null ? object : hasKey.getKey(object);
+ NativeEvent event, ProvidesKey<T> providesKey) {
+ Object key = providesKey.getKey(object);
V viewData = viewDataMap.get(key);
V newViewData = cell.onBrowserEvent(elem,
getValue(object), viewData, event, fieldUpdater == null ? null
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java
deleted file mode 100644
index d910a52..0000000
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/HasKey.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.bikeshed.list.client;
-
-/**
- * An interface for extracting a key from a value. The extracted key must
- * contain suitable implementations of hashCode() and equals().
- *
- * @param <C> the value type for which keys are to be returned
- */
-public interface HasKey<C> {
-
- /**
- * Return a key that may be used to identify values that should be treated as
- * the same in UI views.
- *
- * @param value a value of type C.
- * @return an Object that implements appropriate hashCode() and equals()
- * methods.
- */
- Object getKey(C value);
-}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
index 599c9ec..339845b 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/PagingTableListView.java
@@ -21,7 +21,8 @@
import com.google.gwt.bikeshed.list.shared.ListRegistration;
import com.google.gwt.bikeshed.list.shared.SelectionModel;
import com.google.gwt.bikeshed.list.shared.SizeChangeEvent;
-import com.google.gwt.bikeshed.list.shared.SelectionModel.SelectionListener;
+import com.google.gwt.bikeshed.list.shared.SelectionModel.SelectionChangeEvent;
+import com.google.gwt.bikeshed.list.shared.SelectionModel.SelectionChangeHandler;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
@@ -33,6 +34,7 @@
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
@@ -46,15 +48,23 @@
*/
public class PagingTableListView<T> extends Widget {
+ private class TableSelectionHandler implements SelectionChangeHandler {
+ public void onSelectionChange(SelectionChangeEvent event) {
+ refresh();
+ }
+ }
+
protected int curPage;
private List<Column<T, ?, ?>> columns = new ArrayList<Column<T, ?, ?>>();
private ArrayList<T> data = new ArrayList<T>();
private List<Header<?>> footers = new ArrayList<Header<?>>();
private List<Header<?>> headers = new ArrayList<Header<?>>();
private ListRegistration listReg;
+ private ListModel<T> listModel;
private int numPages;
private int pageSize;
+ private HandlerRegistration selectionHandler;
private SelectionModel<T> selectionModel;
private TableElement table;
@@ -64,13 +74,8 @@
private TableSectionElement thead;
private int totalSize;
- private SelectionListener listener = new SelectionListener() {
- public void selectionChanged() {
- refresh();
- }
- };
-
public PagingTableListView(ListModel<T> listModel, final int pageSize) {
+ this.listModel = listModel;
this.pageSize = pageSize;
setElement(table = Document.get().createTableElement());
thead = table.createTHead();
@@ -82,24 +87,6 @@
// those events actually needed by cells.
sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.KEYEVENTS
| Event.ONCHANGE);
-
- // Attach to the list model.
- listReg = listModel.addListHandler(new ListHandler<T>() {
- public void onDataChanged(ListEvent<T> event) {
- render(event.getStart(), event.getLength(), event.getValues());
- }
-
- public void onSizeChanged(SizeChangeEvent event) {
- totalSize = event.getSize();
- if (totalSize <= 0) {
- numPages = 0;
- } else {
- numPages = 1 + (totalSize - 1) / pageSize;
- }
- setPage(curPage);
- }
- });
- listReg.setRangeOfInterest(0, pageSize);
}
public void addColumn(Column<T, ?, ?> col) {
@@ -118,7 +105,7 @@
public void addColumn(Column<T, ?, ?> col, Header<?> header, Header<?> footer) {
headers.add(header);
footers.add(footer);
- createHeadersAndFooters(); // TODO: defer header recreation
+ createHeadersAndFooters(); // TODO: defer header recreation
columns.add(col);
createRows();
setPage(curPage); // TODO: better way to refresh?
@@ -131,11 +118,11 @@
}
return data.get(indexOnPage);
}
-
+
public List<T> getDisplayedItems() {
return new ArrayList<T>(data);
}
-
+
public int getNumDisplayedItems() {
return Math.min(getPageSize(), totalSize - curPage * pageSize);
}
@@ -148,11 +135,11 @@
public int getPage() {
return curPage;
}
-
+
public int getPageSize() {
return pageSize;
}
-
+
public void nextPage() {
setPage(curPage + 1);
}
@@ -184,14 +171,15 @@
T value = data.get(row);
Column<T, ?, ?> column = columns.get(col);
- column.onBrowserEvent(cell, curPage * pageSize + row, value, event);
+ column.onBrowserEvent(cell, curPage * pageSize + row, value, event,
+ listModel);
}
}
public void previousPage() {
setPage(curPage - 1);
}
-
+
public void refresh() {
listReg.setRangeOfInterest(curPage * pageSize, pageSize);
updateRowVisibility();
@@ -233,11 +221,55 @@
}
public void setSelectionModel(SelectionModel<T> selectionModel) {
- if (this.selectionModel != null) {
- this.selectionModel.removeListener(listener);
+ if (selectionHandler != null) {
+ selectionHandler.removeHandler();
+ selectionHandler = null;
}
this.selectionModel = selectionModel;
- selectionModel.addListener(listener);
+ if (selectionModel != null && isAttached()) {
+ selectionHandler = selectionModel.addSelectionChangeHandler(new TableSelectionHandler());
+ }
+ }
+
+ @Override
+ protected void onLoad() {
+ // Attach a selection handler.
+ if (selectionModel != null) {
+ selectionHandler = selectionModel.addSelectionChangeHandler(new TableSelectionHandler());
+ }
+
+ // Attach to the list model.
+ listReg = listModel.addListHandler(new ListHandler<T>() {
+ public void onDataChanged(ListEvent<T> event) {
+ render(event.getStart(), event.getLength(), event.getValues());
+ }
+
+ public void onSizeChanged(SizeChangeEvent event) {
+ totalSize = event.getSize();
+ if (totalSize <= 0) {
+ numPages = 0;
+ } else {
+ numPages = 1 + (totalSize - 1) / pageSize;
+ }
+ setPage(curPage);
+ }
+ });
+ listReg.setRangeOfInterest(curPage * pageSize, pageSize);
+ }
+
+ @Override
+ protected void onUnload() {
+ // Detach the selection handler.
+ if (selectionHandler != null) {
+ selectionHandler.removeHandler();
+ selectionHandler = null;
+ }
+
+ // Detach from the list model.
+ if (listReg != null) {
+ listReg.removeHandler();
+ listReg = null;
+ }
}
protected void render(int start, int length, List<T> values) {
@@ -252,8 +284,8 @@
if (selectionModel != null && selectionModel.isSelected(q)) {
row.setClassName("pagingTableListView selected");
} else {
- row.setClassName("pagingTableListView " +
- ((indexOnPage & 0x1) == 0 ? "evenRow" : "oddRow"));
+ row.setClassName("pagingTableListView "
+ + ((indexOnPage & 0x1) == 0 ? "evenRow" : "oddRow"));
}
data.set(indexOnPage, q);
@@ -272,7 +304,8 @@
}
}
- private void createHeaders(List<Header<?>> headers, TableSectionElement section) {
+ private void createHeaders(List<Header<?>> headers,
+ TableSectionElement section) {
StringBuilder sb = new StringBuilder();
sb.append("<tr>");
for (Header<?> header : headers) {
@@ -303,7 +336,8 @@
for (int r = 0; r < pageSize; ++r) {
TableRowElement row = tbody.insertRow(0);
- row.setClassName("pagingTableListView " + ((r & 0x1) == 0 ? "evenRow" : "oddRow"));
+ row.setClassName("pagingTableListView "
+ + ((r & 0x1) == 0 ? "evenRow" : "oddRow"));
// TODO: use cloneNode() to make this even faster.
for (int c = 0; c < numCols; ++c) {
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleColumn.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleColumn.java
index 9ba3632..b750820 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleColumn.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/SimpleColumn.java
@@ -19,15 +19,11 @@
/**
* A column that does not make use of view data.
- *
+ *
* @param <T> the row type
* @param <C> the column type
*/
-public abstract class SimpleColumn<T, C> extends Column<T, C, Void> {
-
- public SimpleColumn(Cell<C, Void> cell, HasKey<T> hasKey) {
- super(cell, hasKey);
- }
+public abstract class SimpleColumn<T, C> extends Column<T, C, Void> {
public SimpleColumn(Cell<C, Void> cell) {
super(cell);
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/TextColumn.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/TextColumn.java
index f053998..d3dd015 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/TextColumn.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/TextColumn.java
@@ -20,15 +20,11 @@
/**
* A column that displays its contents with a {@link TextCell} and does not make
* use of view data.
-
+ *
* @param <T> the row type
*/
public abstract class TextColumn<T> extends Column<T, String, Void> {
- public TextColumn(HasKey<T> hasKey) {
- super(TextCell.getInstance(), hasKey);
- }
-
public TextColumn() {
super(TextCell.getInstance());
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java
index b66db76..66e4a49 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/AbstractListModel.java
@@ -99,6 +99,11 @@
}
/**
+ * The provider of keys for list items.
+ */
+ private ProvidesKey<T> keyProvider;
+
+ /**
* The handlers that are listening to this model.
*/
private List<DefaultListRegistration> registrations = new ArrayList<DefaultListRegistration>();
@@ -110,6 +115,26 @@
}
/**
+ * Get the key for a list item. The default implementation returns the item
+ * itself.
+ *
+ * @param item the list item
+ * @return the key that represents the item
+ */
+ public Object getKey(T item) {
+ return keyProvider == null ? item : keyProvider.getKey(item);
+ }
+
+ /**
+ * Get the {@link ProvidesKey} that provides keys for list items.
+ *
+ * @return the {@link ProvidesKey}
+ */
+ public ProvidesKey<T> getKeyProvider() {
+ return keyProvider;
+ }
+
+ /**
* Get the current ranges of all views.
*
* @return the ranges
@@ -124,6 +149,15 @@
}
/**
+ * Set the {@link ProvidesKey} that provides keys for list items.
+ *
+ * @param keyProvider the {@link ProvidesKey}
+ */
+ public void setKeyProvider(ProvidesKey<T> keyProvider) {
+ this.keyProvider = keyProvider;
+ }
+
+ /**
* Called when a view changes its range of interest.
*/
protected abstract void onRangeChanged(int start, int length);
@@ -159,8 +193,8 @@
int realStart = curStart < start ? start : curStart;
int realEnd = curEnd > end ? end : curEnd;
int realLength = realEnd - realStart;
- List<T> realValues = values.subList(realStart - start,
- realStart - start + realLength);
+ List<T> realValues = values.subList(realStart - start, realStart
+ - start + realLength);
ListEvent<T> event = new ListEvent<T>(realStart, realLength, realValues);
reg.getHandler().onDataChanged(event);
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListModel.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListModel.java
index 6bf9ee5..d16f659 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/ListModel.java
@@ -20,7 +20,7 @@
*
* @param <T> the data type of records in the list
*/
-public interface ListModel<T> {
+public interface ListModel<T> extends ProvidesKey<T> {
/**
* Add a {@link ListHandler} to the model.
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/ProvidesKey.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/ProvidesKey.java
new file mode 100644
index 0000000..19cda3c
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/ProvidesKey.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * <p>
+ * Implementors of {@link ProvidesKey} provide a key for list items.
+ * </p>
+ * <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.
+ * </p>
+ *
+ * @param <T> the data type of records in the list
+ */
+public interface ProvidesKey<T> {
+
+ /**
+ * Get the key for a list item.
+ *
+ * @param item the list item
+ * @return the key that represents the item
+ */
+ Object getKey(T item);
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java b/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
index 491ac00..e68e925 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/shared/SelectionModel.java
@@ -15,60 +15,153 @@
*/
package com.google.gwt.bikeshed.list.shared;
-import java.util.ArrayList;
-import java.util.List;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.event.shared.HandlerManager;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.event.shared.HasHandlers;
/**
* A model for selection within a list.
*
* @param <T> the data type of records in the list
*/
-public interface SelectionModel<T> {
-
+public interface SelectionModel<T> extends HasHandlers {
+
/**
- * A listener who will be updated when the selection changes.
+ * Handler interface for {@link SelectionChangeEvent} events.
*/
- interface SelectionListener {
- void selectionChanged();
+ public interface SelectionChangeHandler extends EventHandler {
+
+ /**
+ * Called when {@link SelectionChangeEvent} is fired.
+ *
+ * @param event the {@link SelectionChangeEvent} that was fired
+ */
+ void onSelectionChange(SelectionChangeEvent event);
}
-
+
/**
- * A default implementation of SelectionModel that provides listener
- * addition and removal.
- *
+ * Represents a selection change event.
+ */
+ public static class SelectionChangeEvent extends
+ GwtEvent<SelectionChangeHandler> {
+
+ /**
+ * Handler type.
+ */
+ private static Type<SelectionChangeHandler> TYPE;
+
+ /**
+ * Fires a selection change event on all registered handlers in the handler
+ * manager. If no such handlers exist, this method will do nothing.
+ *
+ * @param source the source of the handlers
+ */
+ static void fire(SelectionModel<?> source) {
+ if (TYPE != null) {
+ SelectionChangeEvent event = new SelectionChangeEvent();
+ source.fireEvent(event);
+ }
+ }
+
+ /**
+ * Gets the type associated with this event.
+ *
+ * @return returns the handler type
+ */
+ public static Type<SelectionChangeHandler> getType() {
+ if (TYPE == null) {
+ TYPE = new Type<SelectionChangeHandler>();
+ }
+ return TYPE;
+ }
+
+ /**
+ * Creates a selection change event.
+ */
+ SelectionChangeEvent() {
+ }
+
+ @Override
+ public final Type<SelectionChangeHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(SelectionChangeHandler handler) {
+ handler.onSelectionChange(this);
+ }
+ }
+
+ /**
+ * A default implementation of SelectionModel that provides listener addition
+ * and removal.
+ *
* @param <T> the data type of records in the list
*/
- abstract class DefaultSelectionModel<T> implements SelectionModel<T> {
+ abstract class AbstractSelectionModel<T> implements SelectionModel<T> {
- protected List<SelectionListener> listeners = new ArrayList<SelectionListener>();
+ private final HandlerManager handlerManager = new HandlerManager(this);
- public void addListener(SelectionListener listener) {
- if (!listeners.contains(listener)) {
- listeners.add(listener);
- }
+ /**
+ * Set to true if an event is scheduled to be fired.
+ */
+ private boolean isEventScheduled;
+
+ public HandlerRegistration addSelectionChangeHandler(
+ SelectionChangeHandler handler) {
+ return handlerManager.addHandler(SelectionChangeEvent.getType(), handler);
}
-
- public void removeListener(SelectionListener listener) {
- if (listeners.contains(listener)) {
- listeners.remove(listener);
- }
+
+ public void fireEvent(GwtEvent<?> event) {
+ handlerManager.fireEvent(event);
}
-
- public void updateListeners() {
- // Inform the listeners
- for (SelectionListener listener : listeners) {
- listener.selectionChanged();
+
+ public void setSelected(T object, boolean selected) {
+ scheduleSelectionChangeEvent();
+ }
+
+ /**
+ * Schedules a {@link SelectionModel.SelectionChangeEvent} to fire at the
+ * end of the current event loop.
+ */
+ protected void scheduleSelectionChangeEvent() {
+ if (!isEventScheduled) {
+ isEventScheduled = true;
+ Scheduler.get().scheduleFinally(new ScheduledCommand() {
+ public void execute() {
+ isEventScheduled = false;
+ SelectionChangeEvent.fire(AbstractSelectionModel.this);
+ }
+ });
}
}
}
-
- void addListener(SelectionListener listener);
+ /**
+ * Adds a {@link SelectionChangeEvent} handler.
+ *
+ * @param handler the handler
+ * @return the registration for the event
+ */
+ HandlerRegistration addSelectionChangeHandler(SelectionChangeHandler handler);
+
+ /**
+ * Check if an object is selected.
+ *
+ * @param object the object
+ * @return true if selected, false if not
+ */
boolean isSelected(T object);
-
- void removeListener(SelectionListener listener);
+ /**
+ * Set the selected state of an object.
+ *
+ * @param object the object to select or deselect
+ * @param selected true to select, false to deselect
+ */
void setSelected(T object, boolean selected);
-
- void setSelected(List<T> objects, boolean selected);
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java
index 9f4aae9..3b3ceb3 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java
@@ -336,7 +336,7 @@
* implementation of NodeInfo.getKey().
*/
protected Object getValueKey() {
- return getParentNodeInfo().getKey(getValue());
+ return getParentNodeInfo().getListModel().getKey(getValue());
}
/**
@@ -353,7 +353,7 @@
ensureAnimationFrame().getStyle().setProperty("display", "");
// Get the node info.
- ListModel<C> listModel = nodeInfo.getListModel();
+ final ListModel<C> listModel = nodeInfo.getListModel();
listReg = listModel.addListHandler(new ListHandler<C>() {
public void onDataChanged(ListEvent<C> event) {
// TODO - handle event start and length
@@ -364,8 +364,7 @@
for (TreeNodeView<?> child : children) {
// Ignore child nodes that are closed
if (child.getState()) {
- Object key = child.getValueKey();
- map.put(key, child);
+ map.put(child.getValueKey(), child);
}
}
}
@@ -379,7 +378,7 @@
for (C childValue : event.getValues()) {
// Remove any child elements that correspond to prior children
// so the call to setInnerHtml will not destroy them
- TreeNodeView<?> savedView = map.get(nodeInfo.getKey(childValue));
+ TreeNodeView<?> savedView = map.get(listModel.getKey(childValue));
if (savedView != null) {
savedView.getContainer().getFirstChild().removeFromParent();
}
@@ -398,7 +397,7 @@
for (C childValue : event.getValues()) {
TreeNodeView<C> child = createTreeNodeView(nodeInfo, childElem,
childValue, null, idx);
- TreeNodeView<?> savedChild = map.get(nodeInfo.getKey(childValue));
+ TreeNodeView<?> savedChild = map.get(listModel.getKey(childValue));
// Copy the saved child's state into the new child
if (savedChild != null) {
child.children = savedChild.children;
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeViewModel.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeViewModel.java
index ce54371..3699a1b 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeViewModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeViewModel.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -19,7 +19,6 @@
import com.google.gwt.bikeshed.cells.client.FieldUpdater;
import com.google.gwt.bikeshed.cells.client.ValueUpdater;
import com.google.gwt.bikeshed.list.client.HasCell;
-import com.google.gwt.bikeshed.list.client.HasKey;
import com.google.gwt.bikeshed.list.shared.ListModel;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
@@ -35,20 +34,20 @@
/**
* The info needed to create a {@link TreeNodeView}.
*/
- interface NodeInfo<T> extends HasKey<T> {
+ interface NodeInfo<T> {
List<HasCell<T, ?, Void>> getHasCells();
/**
* Get the {@link ListModel} used to retrieve child node values.
- *
+ *
* @return the list model
*/
ListModel<T> getListModel();
/**
* Handle an event that is fired on one of the children of this item.
- *
+ *
* @param elem the parent element of the item
* @param object the data value of the item
* @param event the event that was fired
@@ -66,7 +65,7 @@
/**
* Construct a new {@link DefaultNodeInfo} with a single cell and a
* {@link ValueUpdater}.
- *
+ *
* @param listModel the {@link ListModel} that provides the child values
* @param cell the {@link Cell} used to render the child values
* @param valueUpdater the {@link ValueUpdater}
@@ -95,7 +94,7 @@
/**
* Construct a new {@link DefaultNodeInfo}.
- *
+ *
* @param listModel the {@link ListModel} that provides the child values
* @param cell the {@link Cell} used to render the child values
*/
@@ -103,7 +102,8 @@
this(listModel, cell, null);
}
- public DefaultNodeInfo(ListModel<T> listModel, List<HasCell<T, ?, Void>> hasCells) {
+ public DefaultNodeInfo(ListModel<T> listModel,
+ List<HasCell<T, ?, Void>> hasCells) {
this.hasCells.addAll(hasCells);
this.listModel = listModel;
}
@@ -112,10 +112,6 @@
return hasCells;
}
- public Object getKey(T value) {
- return value;
- }
-
public ListModel<T> getListModel() {
return listModel;
}
@@ -134,23 +130,22 @@
}
}
- private <X> void dispatch(Element target, final T object, NativeEvent event,
- HasCell<T, X, Void> hc) {
+ private <X> void dispatch(Element target, final T object,
+ NativeEvent event, HasCell<T, X, Void> hc) {
final FieldUpdater<T, X, Void> fieldUpdater = hc.getFieldUpdater();
hc.getCell().onBrowserEvent(target, hc.getValue(object), null, event,
- fieldUpdater == null ? null
- : new ValueUpdater<X, Void>() {
- public void update(X value, Void viewData) {
- fieldUpdater.update(0, object, value, viewData);
- }
- });
+ fieldUpdater == null ? null : new ValueUpdater<X, Void>() {
+ public void update(X value, Void viewData) {
+ fieldUpdater.update(0, object, value, viewData);
+ }
+ });
}
}
/**
* Get the {@link NodeInfo} that will provide the {@link ListModel} and
* {@link Cell} to retrieve the children of the specified value.
- *
+ *
* @param value the value in the parent node
* @param treeNode the {@link TreeNode} that contains the value
* @return the {@link NodeInfo}
@@ -159,10 +154,10 @@
/**
* Check if the value is known to be a leaf node.
- *
+ *
* @param value the value at the node
* @param treeNode the {@link TreeNode} that contains the value
- *
+ *
* @return true if the node is known to be a leaf node, false otherwise
*/
boolean isLeaf(Object value, TreeNode<?> treeNode);
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java b/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java
index 8e90367..d35798b 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/mail/client/MailSample.java
@@ -21,7 +21,7 @@
import com.google.gwt.bikeshed.list.client.SimpleColumn;
import com.google.gwt.bikeshed.list.client.TextColumn;
import com.google.gwt.bikeshed.list.shared.ListListModel;
-import com.google.gwt.bikeshed.list.shared.SelectionModel.DefaultSelectionModel;
+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;
@@ -44,7 +44,7 @@
*/
public class MailSample implements EntryPoint, ClickHandler {
- class MailSelectionModel extends DefaultSelectionModel<Message> {
+ class MailSelectionModel extends AbstractSelectionModel<Message> {
private static final int ALL = 0;
private static final int NONE = 1;
private static final int READ = 2;
@@ -73,14 +73,8 @@
this.search = canonicalize(search);
updateListeners();
}
-
- public void setSelected(List<Message> objects, boolean selected) {
- for (Message object : objects) {
- addException(object.id, selected);
- }
- updateListeners();
- }
+ @Override
public void setSelected(Message object, boolean selected) {
addException(object.id, selected);
updateListeners();
@@ -91,7 +85,8 @@
exceptions.clear();
updateListeners();
}
-
+
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder();
switch (type) {
@@ -124,6 +119,7 @@
if (exceptions.get(i) != Boolean.TRUE) {
continue;
}
+
if (first) {
first = false;
sb.append("+msg(s) ");
@@ -137,6 +133,7 @@
if (exceptions.get(i) != Boolean.FALSE) {
continue;
}
+
if (first) {
first = false;
sb.append("-msg(s) ");
@@ -148,18 +145,14 @@
return sb.toString();
}
- public void updateListeners() {
- super.updateListeners();
- selectionLabel.setText("Selected " + this.toString());
- }
-
private void addException(int id, boolean selected) {
Boolean currentlySelected = exceptions.get(id);
- if (currentlySelected != null && currentlySelected.booleanValue() != selected) {
+ if (currentlySelected != null
+ && currentlySelected.booleanValue() != selected) {
exceptions.remove(id);
} else {
exceptions.put(id, selected);
- }
+ }
}
private String canonicalize(String input) {
@@ -190,6 +183,11 @@
throw new IllegalStateException("type = " + type);
}
}
+
+ private void updateListeners() {
+ selectionLabel.setText("Selected " + this.toString());
+ scheduleSelectionChangeEvent();
+ }
}
class Message {
@@ -249,14 +247,12 @@
"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"
- };
+ "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");
@@ -278,7 +274,11 @@
if (source == noneButton) {
selectionModel.setType(MailSelectionModel.NONE);
} else if (source == allOnPageButton) {
- selectionModel.setSelected(table.getDisplayedItems(), true);
+ 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) {
@@ -346,7 +346,7 @@
}
};
table.addColumn(subjectColumn, "Subject");
-
+
Label searchLabel = new Label("Search Sender or Subject:");
final TextBox searchBox = new TextBox();
searchBox.addKeyUpHandler(new KeyUpHandler() {
@@ -362,11 +362,11 @@
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(new HTML("<br>"));
RootPanel.get().add(table);
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.java b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.java
index 6739ad4..006d166 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksDesktop.java
@@ -117,6 +117,7 @@
update();
}
};
+ searchListModel.setKeyProvider(StockQuote.KEY_PROVIDER);
favoritesListModel = new AsyncListModel<StockQuote>() {
@Override
@@ -124,6 +125,7 @@
update();
}
};
+ favoritesListModel.setKeyProvider(StockQuote.KEY_PROVIDER);
playerScoresListModel = new AsyncListModel<PlayerInfo>() {
@Override
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.java b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.java
index 2242c84..01ca04e 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/StocksMobile.java
@@ -78,6 +78,7 @@
update();
}
};
+ favoritesListModel.setKeyProvider(StockQuote.KEY_PROVIDER);
// Now create the UI.
RootPanel.get().add(binder.createAndBindUi(this));
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/TransactionTreeViewModel.java b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/TransactionTreeViewModel.java
index 5536256..d1d151e 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/TransactionTreeViewModel.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/client/TransactionTreeViewModel.java
@@ -1,12 +1,12 @@
/*
* 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
@@ -32,9 +32,8 @@
import java.util.Map;
/**
- * A TreeViewModel for a tree with a hidden root node of null,
- * a first level containing ticker symbol Strings, and a second
- * level containing Transactions.
+ * A TreeViewModel for a tree with a hidden root node of null, a first level
+ * containing ticker symbol Strings, and a second level containing Transactions.
*/
class TransactionTreeViewModel implements TreeViewModel {
@@ -44,6 +43,7 @@
public SectorListModel(String sector) {
this.sector = sector;
+ setKeyProvider(StockQuote.KEY_PROVIDER);
}
public String getSector() {
@@ -70,19 +70,18 @@
}
};
- private static final Cell<Transaction, Void> TRANSACTION_CELL =
- new TransactionCell();
+ private static final Cell<Transaction, Void> TRANSACTION_CELL = new TransactionCell();
private Map<String, SectorListModel> sectorListModels = new HashMap<String, SectorListModel>();
private ListModel<StockQuote> stockQuoteListModel;
- private ListListModel<String> topLevelListListModel =
- new ListListModel<String>();
+ private ListListModel<String> topLevelListListModel = new ListListModel<String>();
private Map<String, ListListModel<Transaction>> transactionListListModelsByTicker;
private Updater updater;
- public TransactionTreeViewModel(Updater updater, ListModel<StockQuote> stockQuoteListModel,
+ public TransactionTreeViewModel(Updater updater,
+ ListModel<StockQuote> stockQuoteListModel,
Map<String, ListListModel<Transaction>> transactionListListModelsByTicker) {
this.updater = updater;
this.stockQuoteListModel = stockQuoteListModel;
@@ -92,19 +91,14 @@
topLevelList.add("S&P 500");
this.transactionListListModelsByTicker = transactionListListModelsByTicker;
}
-
+
public <T> NodeInfo<?> getNodeInfo(T value, final TreeNode<T> treeNode) {
if (value == null) {
return new TreeViewModel.DefaultNodeInfo<String>(topLevelListListModel,
TextCell.getInstance());
} else if ("Favorites".equals(value)) {
return new TreeViewModel.DefaultNodeInfo<StockQuote>(stockQuoteListModel,
- STOCK_QUOTE_CELL) {
- @Override
- public Object getKey(StockQuote value) {
- return value.getTicker();
- }
- };
+ STOCK_QUOTE_CELL);
} else if ("History".equals(value)) {
String ticker = ((StockQuote) treeNode.getParentNode().getValue()).getTicker();
ListListModel<Transaction> listModel = transactionListListModelsByTicker.get(ticker);
@@ -119,8 +113,8 @@
List<String> list = listModel.getList();
list.add("Buy");
list.add("Sell");
- return new TreeViewModel.DefaultNodeInfo<String>(listModel, ButtonCell.getInstance(),
- new ValueUpdater<String, Void>() {
+ return new TreeViewModel.DefaultNodeInfo<String>(listModel,
+ ButtonCell.getInstance(), new ValueUpdater<String, Void>() {
public void update(String value, Void viewData) {
StockQuote stockQuote = (StockQuote) treeNode.getParentNode().getValue();
if ("Buy".equals(value)) {
@@ -133,18 +127,15 @@
} else if (value instanceof String) {
SectorListModel listModel = new SectorListModel((String) value);
sectorListModels.put((String) value, listModel);
- return new TreeViewModel.DefaultNodeInfo<StockQuote>(listModel, STOCK_QUOTE_CELL) {
- @Override
- public Object getKey(StockQuote value) {
- return value.getTicker();
- }
- };
+ return new TreeViewModel.DefaultNodeInfo<StockQuote>(listModel,
+ STOCK_QUOTE_CELL);
} else if (value instanceof StockQuote) {
ListListModel<String> listModel = new ListListModel<String>();
List<String> list = listModel.getList();
list.add("Actions");
list.add("History");
- return new TreeViewModel.DefaultNodeInfo<String>(listModel, TextCell.getInstance());
+ return new TreeViewModel.DefaultNodeInfo<String>(listModel,
+ TextCell.getInstance());
}
throw new IllegalArgumentException(value.toString());
@@ -155,11 +146,11 @@
}
public boolean isLeaf(Object value, final TreeNode<?> parentNode) {
- if (value instanceof Transaction ||
- "Buy".equals(value) || "Sell".equals(value)) {
+ if (value instanceof Transaction || "Buy".equals(value)
+ || "Sell".equals(value)) {
return true;
}
-
+
return false;
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/shared/StockQuote.java b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/shared/StockQuote.java
index c279d09..7f2605a 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/shared/StockQuote.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/stocks/shared/StockQuote.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.sample.bikeshed.stocks.shared;
+import com.google.gwt.bikeshed.list.shared.ProvidesKey;
+
import java.io.Serializable;
/**
@@ -22,6 +24,15 @@
*/
public class StockQuote implements Serializable {
+ /**
+ * Provides the key for {@link StockQuote}.
+ */
+ public static final ProvidesKey<StockQuote> KEY_PROVIDER = new ProvidesKey<StockQuote>() {
+ public Object getKey(StockQuote item) {
+ return item.getTicker();
+ }
+ };
+
private boolean favorite;
private String name;
private int price;
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java b/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java
index af14b87..63c1fef 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/tree/client/TreeSample.java
@@ -15,7 +15,7 @@
*/
package com.google.gwt.sample.bikeshed.tree.client;
-import com.google.gwt.bikeshed.list.shared.SelectionModel.DefaultSelectionModel;
+import com.google.gwt.bikeshed.list.shared.SelectionModel.AbstractSelectionModel;
import com.google.gwt.bikeshed.tree.client.SideBySideTreeView;
import com.google.gwt.bikeshed.tree.client.StandardTreeView;
import com.google.gwt.core.client.EntryPoint;
@@ -23,7 +23,6 @@
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
-import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@@ -32,7 +31,7 @@
*/
public class TreeSample implements EntryPoint {
- class MySelectionModel extends DefaultSelectionModel<Object> {
+ class MySelectionModel extends AbstractSelectionModel<Object> {
private Label label;
private Set<Object> selectedSet = new TreeSet<Object>();
@@ -45,24 +44,15 @@
return selectedSet.contains(object);
}
+ @Override
public void setSelected(Object object, boolean selected) {
- setSelectedHelper(object, selected);
- label.setText("Selected " + selectedSet.toString());
- }
-
- public void setSelected(List<Object> objects, boolean selected) {
- for (Object object : objects) {
- setSelectedHelper(object, selected);
- }
- label.setText("Selected " + selectedSet.toString());
- }
-
- public void setSelectedHelper(Object object, boolean selected) {
if (selected) {
selectedSet.add(object);
} else {
selectedSet.remove(object);
}
+ label.setText("Selected " + selectedSet.toString());
+ super.setSelected(object, selected);
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java b/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java
index 2635a48..dbe519c 100644
--- a/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java
+++ b/bikeshed/src/com/google/gwt/sample/bikeshed/validation/client/Validation.java
@@ -17,10 +17,10 @@
import com.google.gwt.bikeshed.cells.client.FieldUpdater;
import com.google.gwt.bikeshed.list.client.Column;
-import com.google.gwt.bikeshed.list.client.HasKey;
import com.google.gwt.bikeshed.list.client.PagingTableListView;
import com.google.gwt.bikeshed.list.client.TextColumn;
import com.google.gwt.bikeshed.list.shared.ListListModel;
+import com.google.gwt.bikeshed.list.shared.ProvidesKey;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;
@@ -80,6 +80,11 @@
String zip = "300" + i;
list.add(new Address("GA", zip));
}
+ listModel.setKeyProvider(new ProvidesKey<Address>() {
+ public Object getKey(Address object) {
+ return object.key;
+ }
+ });
PagingTableListView<Address> table = new PagingTableListView<Address>(
listModel, 10);
@@ -90,14 +95,9 @@
}
};
- HasKey<Address> key = new HasKey<Address>() {
- public Object getKey(Address object) {
- return object.key;
- }
- };
Column<Address, String, ValidatableField<String>> zipColumn =
new Column<Address, String, ValidatableField<String>>(
- new ValidatableInputCell(), key) {
+ new ValidatableInputCell()) {
@Override
public String getValue(Address object) {
return object.zip;