From 9b510d35a7ec6b1a9504343b2620110b77d4e5c3 Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Tue, 18 Mar 2014 16:36:10 +0200 Subject: Adds setHeightByRow support to Grid (#13297) Change-Id: I67f1bfb476a8af28c0ea1a03758684ca42d1ba48 --- .../src/com/vaadin/shared/ui/grid/GridState.java | 24 +++++++++++++ .../src/com/vaadin/shared/ui/grid/HeightMode.java | 42 ++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 shared/src/com/vaadin/shared/ui/grid/HeightMode.java (limited to 'shared/src/com') diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 93e602a539..8fdd8c8ec5 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -28,6 +28,14 @@ import com.vaadin.shared.AbstractComponentState; * @author Vaadin Ltd */ public class GridState extends AbstractComponentState { + + /** + * The default value for height-by-rows for both GWT widgets + * {@link com.vaadin.ui.components.grid Grid} and + * {@link com.vaadin.client.ui.grid.Escalator Escalator} + */ + public static final double DEFAULT_HEIGHT_BY_ROWS = 10.0d; + { // FIXME Grid currently does not support undefined size width = "400px"; @@ -61,4 +69,20 @@ public class GridState extends AbstractComponentState { */ public String lastFrozenColumnId = null; + /** The height of the Grid in terms of body rows. */ + // @DelegateToWidget + /* + * Annotation doesn't work because of http://dev.vaadin.com/ticket/12900. + * Remove manual code from Connector once fixed + */ + public double heightByRows = DEFAULT_HEIGHT_BY_ROWS; + + /** The mode by which Grid defines its height. */ + // @DelegateToWidget + /* + * Annotation doesn't work because of http://dev.vaadin.com/ticket/12900. + * Remove manual code from Connector once fixed + */ + public HeightMode heightMode = HeightMode.CSS; + } diff --git a/shared/src/com/vaadin/shared/ui/grid/HeightMode.java b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java new file mode 100644 index 0000000000..0146e53e73 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.grid; + +/** + * The modes for height calculation that are supported by Grid ( + * {@link com.vaadin.client.ui.grid.Grid client} and + * {@link com.vaadin.ui.components.grid.Grid server}) / + * {@link com.vaadin.client.ui.grid.Escalator Escalator}. + * + * @since 7.2 + * @author Vaadin Ltd + * @see com.vaadin.client.ui.grid.Grid#setHeightMode(HeightMode) + * @see com.vaadin.ui.components.grid.Grid#setHeightMode(HeightMode) + * @see com.vaadin.client.ui.grid.Escalator#setHeightMode(HeightMode) + */ +public enum HeightMode { + /** + * The height of the Component or Widget is defined by a CSS-like value. + * (e.g. "100px", "50em" or "25%") + */ + CSS, + + /** + * The height of the Component or Widget in question is defined by a number + * of rows. + */ + ROW; +} -- cgit v1.2.3 From 300c94c4b39e100cb506b476e08aa5e6626653b5 Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Thu, 20 Mar 2014 16:16:33 +0200 Subject: Made some Grid classes Serializable. Change-Id: If784192dbfd9b8d23597fce493f93a51bb7d4907 --- server/src/com/vaadin/ui/components/grid/Grid.java | 5 +++-- shared/src/com/vaadin/shared/ui/grid/GridConstants.java | 4 +++- shared/src/com/vaadin/shared/ui/grid/Range.java | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'shared/src/com') diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 7544f2b497..503ef0a1c4 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -16,6 +16,7 @@ package com.vaadin.ui.components.grid; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -46,9 +47,9 @@ import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; +import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.ui.grid.ScrollDestination; -import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.Component; @@ -86,7 +87,7 @@ public class Grid extends AbstractComponent { * Component hierarchy. * */ - private final class ActiveRowHandler { + private final class ActiveRowHandler implements Serializable { /** * A map from itemId to the value change listener used for all of its * properties diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java index 5b88fad5a8..d5fdd40120 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -15,6 +15,8 @@ */ package com.vaadin.shared.ui.grid; +import java.io.Serializable; + /** * Container class for common constants and default values used by the Grid * component. @@ -22,7 +24,7 @@ package com.vaadin.shared.ui.grid; * @since 7.2 * @author Vaadin Ltd */ -public final class GridConstants { +public final class GridConstants implements Serializable { /** * Default padding in pixels when scrolling programmatically, without an diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java index 3114a79c82..2593f7afd9 100644 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -16,6 +16,8 @@ package com.vaadin.shared.ui.grid; +import java.io.Serializable; + /** * An immutable representation of a range, marked by start and end points. *

@@ -28,7 +30,7 @@ package com.vaadin.shared.ui.grid; * @since 7.2 * @author Vaadin Ltd */ -public final class Range { +public final class Range implements Serializable { private final int start; private final int end; -- cgit v1.2.3 From 964ebc8a2c4024f2a4fe60e9eec140e351c724bb Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Fri, 30 May 2014 09:50:40 +0300 Subject: Move ActiveRowHandler to the data provider extension (#13334) This makes GridConnector include information about what rows are cached when more data is requested instead of the previous way of synchronizing this separately every time a scroll event occurs. This new approach makes it possible to have rows cached even if they are not in view. It also improves performance since there's no need to do an RPC every time scrolling changes what is visible. Change-Id: Ibfe8a69586dfc397591f56efa8ef351e274f0116 --- .../client/data/AbstractRemoteDataSource.java | 9 + .../vaadin/client/data/RpcDataSourceConnector.java | 5 +- .../com/vaadin/client/ui/grid/GridConnector.java | 11 - .../com/vaadin/data/RpcDataProviderExtension.java | 396 ++++++++++++++++++++- server/src/com/vaadin/ui/components/grid/Grid.java | 355 +----------------- .../src/com/vaadin/shared/data/DataRequestRpc.java | 7 +- .../com/vaadin/shared/ui/grid/GridServerRpc.java | 39 -- 7 files changed, 412 insertions(+), 410 deletions(-) delete mode 100644 shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index 48026abb49..40f5111f8a 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -320,4 +320,13 @@ public abstract class AbstractRemoteDataSource implements DataSource { Profiler.leave("AbstractRemoteDataSource.insertRowData"); } + + /** + * Gets the current range of cached rows + * + * @return the range of currently cached rows + */ + public Range getCachedRange() { + return cached; + } } diff --git a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java index f44a541083..e07d2297c9 100644 --- a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java @@ -25,6 +25,7 @@ import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataProviderState; import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.Range; /** * Connects a Vaadin server-side container data source to a Grid. This is @@ -41,8 +42,10 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { private final AbstractRemoteDataSource dataSource = new AbstractRemoteDataSource() { @Override protected void requestRows(int firstRowIndex, int numberOfRows) { + Range cached = getCachedRange(); + getRpcProxy(DataRequestRpc.class).requestRows(firstRowIndex, - numberOfRows); + numberOfRows, cached.getStart(), cached.length()); } }; diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 9f0585f2f9..8685180d38 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -32,7 +32,6 @@ import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.ColumnGroupState; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.ScrollDestination; @@ -95,16 +94,6 @@ public class GridConnector extends AbstractComponentConnector { @Override protected void init() { super.init(); - getWidget().addRowVisibilityChangeHandler( - new RowVisibilityChangeHandler() { - @Override - public void onRowVisibilityChange( - RowVisibilityChangeEvent event) { - getRpcProxy(GridServerRpc.class).setVisibleRows( - event.getFirstVisibleRow(), - event.getVisibleRowCount()); - } - }); registerRpc(GridClientRpc.class, new GridClientRpc() { @Override diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index c7cb4a27e2..507b2d0f22 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -16,16 +16,32 @@ package com.vaadin.data; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.Indexed.ItemAddEvent; +import com.vaadin.data.Container.Indexed.ItemRemoveEvent; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeListener; +import com.vaadin.data.Container.ItemSetChangeNotifier; +import com.vaadin.data.Container.PropertySetChangeListener; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.Property.ValueChangeNotifier; import com.vaadin.server.AbstractExtension; +import com.vaadin.server.ClientConnector; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataProviderState; import com.vaadin.shared.data.DataRequestRpc; +import com.vaadin.shared.ui.grid.Range; +import com.vaadin.ui.Component; import com.vaadin.ui.components.grid.Grid; /** @@ -40,8 +56,296 @@ import com.vaadin.ui.components.grid.Grid; */ public class RpcDataProviderExtension extends AbstractExtension { + /** + * A helper class that handles the client-side Escalator logic relating to + * making sure that whatever is currently visible to the user, is properly + * initialized and otherwise handled on the server side (as far as + * required). + *

+ * This bookeeping includes, but is not limited to: + *

+ */ + private class ActiveRowHandler implements Serializable { + /** + * A map from itemId to the value change listener used for all of its + * properties + */ + private final Map valueChangeListeners = new HashMap(); + + /** + * The currently active range. Practically, it's the range of row + * indices being cached currently. + */ + private Range activeRange = Range.withLength(0, 0); + + /** + * A hook for making sure that appropriate data is "active". All other + * rows should be "inactive". + *

+ * "Active" can mean different things in different contexts. For + * example, only the Properties in the active range need + * ValueChangeListeners. Also, whenever a row with a Component becomes + * active, it needs to be attached (and conversely, when inactive, it + * needs to be detached). + * + * @param firstActiveRow + * the first active row + * @param activeRowCount + * the number of active rows + */ + public void setActiveRows(int firstActiveRow, int activeRowCount) { + + final Range newActiveRange = Range.withLength(firstActiveRow, + activeRowCount); + + // TODO [[Components]] attach and detach components + + /*- + * Example + * + * New Range: [3, 4, 5, 6, 7] + * Old Range: [1, 2, 3, 4, 5] + * Result: [1, 2][3, 4, 5] [] + */ + final Range[] depractionPartition = activeRange + .partitionWith(newActiveRange); + removeValueChangeListeners(depractionPartition[0]); + removeValueChangeListeners(depractionPartition[2]); + + /*- + * Example + * + * Old Range: [1, 2, 3, 4, 5] + * New Range: [3, 4, 5, 6, 7] + * Result: [] [3, 4, 5][6, 7] + */ + final Range[] activationPartition = newActiveRange + .partitionWith(activeRange); + addValueChangeListeners(activationPartition[0]); + addValueChangeListeners(activationPartition[2]); + + activeRange = newActiveRange; + } + + private void addValueChangeListeners(Range range) { + for (int i = range.getStart(); i < range.getEnd(); i++) { + + final Object itemId = container.getIdByIndex(i); + final Item item = container.getItem(itemId); + + if (valueChangeListeners.containsKey(itemId)) { + /* + * This might occur when items are removed from above the + * viewport, the escalator scrolls up to compensate, but the + * same items remain in the view: It looks as if one row was + * scrolled, when in fact the whole viewport was shifted up. + */ + continue; + } + + GridValueChangeListener listener = new GridValueChangeListener( + itemId); + valueChangeListeners.put(itemId, listener); + + for (final Object propertyId : item.getItemPropertyIds()) { + final Property property = item + .getItemProperty(propertyId); + if (property instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) property) + .addValueChangeListener(listener); + } + } + } + } + + private void removeValueChangeListeners(Range range) { + for (int i = range.getStart(); i < range.getEnd(); i++) { + final Object itemId = container.getIdByIndex(i); + final Item item = container.getItem(itemId); + final GridValueChangeListener listener = valueChangeListeners + .remove(itemId); + + if (listener != null) { + for (final Object propertyId : item.getItemPropertyIds()) { + final Property property = item + .getItemProperty(propertyId); + if (property instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) property) + .removeValueChangeListener(listener); + } + } + } + } + } + + /** + * Manages removed properties in active rows. + * + * @param removedPropertyIds + * the property ids that have been removed from the container + */ + public void propertiesRemoved(Collection removedPropertyIds) { + /* + * no-op, for now. + * + * The Container should be responsible for cleaning out any + * ValueChangeListeners from removed Properties. Components will + * benefit from this, however. + */ + } + + /** + * Manages added properties in active rows. + * + * @param addedPropertyIds + * the property ids that have been added to the container + */ + public void propertiesAdded(Collection addedPropertyIds) { + for (int i = activeRange.getStart(); i < activeRange.getEnd(); i++) { + final Object itemId = container.getIdByIndex(i); + final Item item = container.getItem(itemId); + final GridValueChangeListener listener = valueChangeListeners + .get(itemId); + assert (listener != null) : "a listener should've been pre-made by addValueChangeListeners"; + + for (final Object propertyId : addedPropertyIds) { + final Property property = item + .getItemProperty(propertyId); + if (property instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) property) + .addValueChangeListener(listener); + } + } + } + } + + /** + * Handles the insertion of rows. + *

+ * This method's responsibilities are to: + *

    + *
  • shift the internal bookkeeping by count if the + * insertion happens above currently active range + *
  • ignore rows inserted below the currently active range + *
  • shift (and deactivate) rows pushed out of view + *
  • activate rows that are inserted in the current viewport + *
+ * + * @param firstIndex + * the index of the first inserted rows + * @param count + * the number of rows inserted at firstIndex + */ + public void insertRows(int firstIndex, int count) { + if (firstIndex < activeRange.getStart()) { + activeRange = activeRange.offsetBy(count); + } else if (firstIndex < activeRange.getEnd()) { + final Range deprecatedRange = Range.withLength( + activeRange.getEnd(), count); + removeValueChangeListeners(deprecatedRange); + + final Range freshRange = Range.between(firstIndex, count); + addValueChangeListeners(freshRange); + } else { + // out of view, noop + } + } + + /** + * Removes a single item by its id. + * + * @param itemId + * the id of the removed id. Note: this item does + * not exist anymore in the datasource + */ + public void removeItemId(Object itemId) { + final GridValueChangeListener removedListener = valueChangeListeners + .remove(itemId); + if (removedListener != null) { + /* + * We removed an item from somewhere in the visible range, so we + * make the active range shorter. The empty hole will be filled + * by the client-side code when it asks for more information. + */ + activeRange = Range.withLength(activeRange.getStart(), + activeRange.length() - 1); + } + } + } + + /** + * A class to listen to changes in property values in the Container added + * with {@link Grid#setContainerDatasource(Container.Indexed)}, and notifies + * the data source to update the client-side representation of the modified + * item. + *

+ * One instance of this class can (and should) be reused for all the + * properties in an item, since this class will inform that the entire row + * needs to be re-evaluated (in contrast to a property-based change + * management) + *

+ * Since there's no Container-wide possibility to listen to any kind of + * value changes, an instance of this class needs to be attached to each and + * every Item's Property in the container. + * + * @see Grid#addValueChangeListener(Container, Object, Object) + * @see Grid#valueChangeListeners + */ + private class GridValueChangeListener implements ValueChangeListener { + private final Object itemId; + + public GridValueChangeListener(Object itemId) { + /* + * Using an assert instead of an exception throw, just to optimize + * prematurely + */ + assert itemId != null : "null itemId not accepted"; + this.itemId = itemId; + } + + @Override + public void valueChange(ValueChangeEvent event) { + updateRowData(container.indexOfId(itemId)); + } + } + private final Indexed container; + private final ActiveRowHandler activeRowHandler = new ActiveRowHandler(); + + private final ItemSetChangeListener itemListener = new ItemSetChangeListener() { + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + + if (event instanceof ItemAddEvent) { + ItemAddEvent addEvent = (ItemAddEvent) event; + int firstIndex = addEvent.getFirstIndex(); + int count = addEvent.getAddedItemsCount(); + insertRowData(firstIndex, count); + } + + else if (event instanceof ItemRemoveEvent) { + ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; + int firstIndex = removeEvent.getFirstIndex(); + int count = removeEvent.getRemovedItemsCount(); + removeRowData(firstIndex, count, removeEvent.getFirstItemId()); + } + + else { + // TODO no diff info available, redraw everything + throw new UnsupportedOperationException("bare " + + "ItemSetChangeEvents are currently " + + "not supported, use a container that " + + "uses AddItemEvents and RemoveItemEvents."); + } + } + }; + /** * Creates a new data provider using the given container. * @@ -51,16 +355,31 @@ public class RpcDataProviderExtension extends AbstractExtension { public RpcDataProviderExtension(Indexed container) { this.container = container; - // TODO support for reacting to events from the container added later - registerRpc(new DataRequestRpc() { @Override - public void requestRows(int firstRow, int numberOfRows) { + public void requestRows(int firstRow, int numberOfRows, + int firstCachedRowIndex, int cacheSize) { pushRows(firstRow, numberOfRows); + + Range active = Range.withLength(firstRow, numberOfRows); + if (cacheSize != 0) { + Range cached = Range.withLength(firstCachedRowIndex, + cacheSize); + active = active.combineWith(cached); + } + + activeRowHandler.setActiveRows(active.getStart(), + active.length()); } }); getState().containerSize = container.size(); + + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container) + .addItemSetChangeListener(itemListener); + } + } private void pushRows(int firstRow, int numberOfRows) { @@ -110,9 +429,11 @@ public class RpcDataProviderExtension extends AbstractExtension { * @param count * the number of rows inserted at index */ - public void insertRowData(int index, int count) { + private void insertRowData(int index, int count) { getState().containerSize += count; getRpcProxy(DataProviderRpc.class).insertRowData(index, count); + + activeRowHandler.insertRows(index, count); } /** @@ -122,10 +443,26 @@ public class RpcDataProviderExtension extends AbstractExtension { * the index of the first row removed * @param count * the number of rows removed + * @param firstItemId + * the item id of the first removed item */ - public void removeRowData(int firstIndex, int count) { + private void removeRowData(int firstIndex, int count, Object firstItemId) { getState().containerSize -= count; getRpcProxy(DataProviderRpc.class).removeRowData(firstIndex, count); + + /* + * Unfortunately, there's no sane way of getting the rest of the removed + * itemIds unless we cache a mapping between index and itemId. + * + * Fortunately, the only time _currently_ an event with more than one + * removed item seems to be when calling + * AbstractInMemoryContainer.removeAllElements(). Otherwise, it's only + * removing one item at a time. + * + * We _could_ have a backup of all the itemIds, and compare to that one, + * but we really really don't want to go there. + */ + activeRowHandler.removeItemId(firstItemId); } /** @@ -145,4 +482,53 @@ public class RpcDataProviderExtension extends AbstractExtension { getRpcProxy(DataProviderRpc.class).setRowData(index, Collections.singletonList(row)); } + + @Override + public void setParent(ClientConnector parent) { + super.setParent(parent); + if (parent == null) { + // We're detached, release various listeners + + activeRowHandler + .removeValueChangeListeners(activeRowHandler.activeRange); + + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container) + .removeItemSetChangeListener(itemListener); + } + + } + } + + /** + * Informs this data provider that some of the properties have been removed + * from the container. + *

+ * Please note that we could add our own {@link PropertySetChangeListener} + * to the container, but then we'd need to implement the same bookeeping for + * finding what's added and removed that Grid already does in its own + * listener. + * + * @param removedColumns + * a list of property ids for the removed columns + */ + public void propertiesRemoved(List removedColumns) { + activeRowHandler.propertiesRemoved(removedColumns); + } + + /** + * Informs this data provider that some of the properties have been added to + * the container. + *

+ * Please note that we could add our own {@link PropertySetChangeListener} + * to the container, but then we'd need to implement the same bookeeping for + * finding what's added and removed that Grid already does in its own + * listener. + * + * @param addedPropertyIds + * a list of property ids for the added columns + */ + public void propertiesAdded(HashSet addedPropertyIds) { + activeRowHandler.propertiesAdded(addedPropertyIds); + } } diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 69beb260f4..49b3f1fa64 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -16,7 +16,6 @@ package com.vaadin.ui.components.grid; -import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; @@ -28,28 +27,16 @@ import java.util.List; import java.util.Map; import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeEvent; -import com.vaadin.data.Container.ItemSetChangeListener; -import com.vaadin.data.Container.ItemSetChangeNotifier; import com.vaadin.data.Container.PropertySetChangeEvent; import com.vaadin.data.Container.PropertySetChangeListener; import com.vaadin.data.Container.PropertySetChangeNotifier; -import com.vaadin.data.Item; -import com.vaadin.data.Property; -import com.vaadin.data.Property.ValueChangeEvent; -import com.vaadin.data.Property.ValueChangeListener; -import com.vaadin.data.Property.ValueChangeNotifier; import com.vaadin.data.RpcDataProviderExtension; import com.vaadin.server.KeyMapper; import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; -import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.HeightMode; -import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.components.grid.selection.MultiSelectionModel; @@ -81,282 +68,6 @@ import com.vaadin.util.ReflectTools; */ public class Grid extends AbstractComponent implements SelectionChangeNotifier { - /** - * A helper class that handles the client-side Escalator logic relating to - * making sure that whatever is currently visible to the user, is properly - * initialized and otherwise handled on the server side (as far as - * requried). - *

- * This bookeeping includes, but is not limited to: - *

    - *
  • listening to the currently visible {@link Property Properties'} value - * changes on the server side and sending those back to the client; and - *
  • attaching and detaching {@link Component Components} from the Vaadin - * Component hierarchy. - *
- */ - private final class ActiveRowHandler implements Serializable { - /** - * A map from itemId to the value change listener used for all of its - * properties - */ - private final Map valueChangeListeners = new HashMap(); - - /** - * The currently active range. Practically, it's the range of row - * indices being displayed currently. - */ - private Range activeRange = Range.withLength(0, 0); - - /** - * A hook for making sure that appropriate data is "active". All other - * rows should be "inactive". - *

- * "Active" can mean different things in different contexts. For - * example, only the Properties in the active range need - * ValueChangeListeners. Also, whenever a row with a Component becomes - * active, it needs to be attached (and conversely, when inactive, it - * needs to be detached). - * - * @param firstActiveRow - * the first active row - * @param activeRowCount - * the number of active rows - */ - public void setActiveRows(int firstActiveRow, int activeRowCount) { - - final Range newActiveRange = Range.withLength(firstActiveRow, - activeRowCount); - - // TODO [[Components]] attach and detach components - - /*- - * Example - * - * New Range: [3, 4, 5, 6, 7] - * Old Range: [1, 2, 3, 4, 5] - * Result: [1, 2][3, 4, 5] [] - */ - final Range[] depractionPartition = activeRange - .partitionWith(newActiveRange); - removeValueChangeListeners(depractionPartition[0]); - removeValueChangeListeners(depractionPartition[2]); - - /*- - * Example - * - * Old Range: [1, 2, 3, 4, 5] - * New Range: [3, 4, 5, 6, 7] - * Result: [] [3, 4, 5][6, 7] - */ - final Range[] activationPartition = newActiveRange - .partitionWith(activeRange); - addValueChangeListeners(activationPartition[0]); - addValueChangeListeners(activationPartition[2]); - - activeRange = newActiveRange; - } - - private void addValueChangeListeners(Range range) { - for (int i = range.getStart(); i < range.getEnd(); i++) { - - final Object itemId = datasource.getIdByIndex(i); - final Item item = datasource.getItem(itemId); - - if (valueChangeListeners.containsKey(itemId)) { - /* - * This might occur when items are removed from above the - * viewport, the escalator scrolls up to compensate, but the - * same items remain in the view: It looks as if one row was - * scrolled, when in fact the whole viewport was shifted up. - */ - continue; - } - - GridValueChangeListener listener = new GridValueChangeListener( - itemId); - valueChangeListeners.put(itemId, listener); - - for (final Object propertyId : item.getItemPropertyIds()) { - final Property property = item - .getItemProperty(propertyId); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property) - .addValueChangeListener(listener); - } - } - } - } - - private void removeValueChangeListeners(Range range) { - for (int i = range.getStart(); i < range.getEnd(); i++) { - final Object itemId = datasource.getIdByIndex(i); - final Item item = datasource.getItem(itemId); - final GridValueChangeListener listener = valueChangeListeners - .remove(itemId); - - if (listener != null) { - for (final Object propertyId : item.getItemPropertyIds()) { - final Property property = item - .getItemProperty(propertyId); - - /* - * Because listener != null, we can be certain that this - * property is a ValueChangeNotifier: It wouldn't be - * inserted in addValueChangeListeners if the property - * wasn't a suitable type. I.e. No need for "instanceof" - * check. - */ - ((ValueChangeNotifier) property) - .removeValueChangeListener(listener); - } - } - } - } - - public void clear() { - removeValueChangeListeners(activeRange); - /* - * we're doing an assert for emptiness there (instead of a - * carte-blanche ".clear()"), to be absolutely sure that everything - * is cleaned up properly, and that we have no dangling listeners. - */ - assert valueChangeListeners.isEmpty() : "GridValueChangeListeners are leaking"; - - activeRange = Range.withLength(0, 0); - } - - /** - * Manages removed properties in active rows. - * - * @param removedPropertyIds - * the property ids that have been removed from the container - */ - public void propertiesRemoved(Collection removedPropertyIds) { - /* - * no-op, for now. - * - * The Container should be responsible for cleaning out any - * ValueChangeListeners from removed Properties. Components will - * benefit from this, however. - */ - } - - /** - * Manages added properties in active rows. - * - * @param addedPropertyIds - * the property ids that have been added to the container - */ - public void propertiesAdded(Collection addedPropertyIds) { - for (int i = activeRange.getStart(); i < activeRange.getEnd(); i++) { - final Object itemId = datasource.getIdByIndex(i); - final Item item = datasource.getItem(itemId); - final GridValueChangeListener listener = valueChangeListeners - .get(itemId); - assert (listener != null) : "a listener should've been pre-made by addValueChangeListeners"; - - for (final Object propertyId : addedPropertyIds) { - final Property property = item - .getItemProperty(propertyId); - if (property instanceof ValueChangeNotifier) { - ((ValueChangeNotifier) property) - .addValueChangeListener(listener); - } - } - } - } - - /** - * Handles the insertion of rows. - *

- * This method's responsibilities are to: - *

    - *
  • shift the internal bookkeeping by count if the - * insertion happens above currently active range - *
  • ignore rows inserted below the currently active range - *
  • shift (and deactivate) rows pushed out of view - *
  • activate rows that are inserted in the current viewport - *
- * - * @param firstIndex - * the index of the first inserted rows - * @param count - * the number of rows inserted at firstIndex - */ - public void insertRows(int firstIndex, int count) { - if (firstIndex < activeRange.getStart()) { - activeRange = activeRange.offsetBy(count); - } else if (firstIndex < activeRange.getEnd()) { - final Range deprecatedRange = Range.withLength( - activeRange.getEnd(), count); - removeValueChangeListeners(deprecatedRange); - - final Range freshRange = Range.between(firstIndex, count); - addValueChangeListeners(freshRange); - } else { - // out of view, noop - } - } - - /** - * Removes a single item by its id. - * - * @param itemId - * the id of the removed id. Note: this item does - * not exist anymore in the datasource - */ - public void removeItemId(Object itemId) { - final GridValueChangeListener removedListener = valueChangeListeners - .remove(itemId); - if (removedListener != null) { - /* - * We removed an item from somewhere in the visible range, so we - * make the active range shorter. The empty hole will be filled - * by the client-side code when it asks for more information. - */ - activeRange = Range.withLength(activeRange.getStart(), - activeRange.length() - 1); - } - } - } - - /** - * A class to listen to changes in property values in the Container added - * with {@link Grid#setContainerDatasource(Container.Indexed)}, and notifies - * the data source to update the client-side representation of the modified - * item. - *

- * One instance of this class can (and should) be reused for all the - * properties in an item, since this class will inform that the entire row - * needs to be re-evaluated (in contrast to a property-based change - * management) - *

- * Since there's no Container-wide possibility to listen to any kind of - * value changes, an instance of this class needs to be attached to each and - * every Item's Property in the container. - * - * @see Grid#addValueChangeListener(Container, Object, Object) - * @see Grid#valueChangeListeners - */ - private class GridValueChangeListener implements ValueChangeListener { - private final Object itemId; - - public GridValueChangeListener(Object itemId) { - /* - * Using an assert instead of an exception throw, just to optimize - * prematurely - */ - assert itemId != null : "null itemId not accepted"; - this.itemId = itemId; - } - - @Override - public void valueChange(ValueChangeEvent event) { - datasourceExtension.updateRowData(datasource.indexOfId(itemId)); - } - } - /** * Selection modes representing built-in {@link SelectionModel * SelectionModels} that come bundled with {@link Grid}. @@ -440,7 +151,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { columnKeys.remove(columnId); getState().columns.remove(column.getState()); } - activeRowHandler.propertiesRemoved(removedColumns); + datasourceExtension.propertiesRemoved(removedColumns); // Add new columns HashSet addedPropertyIds = new HashSet(); @@ -450,7 +161,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { addedPropertyIds.add(propertyId); } } - activeRowHandler.propertiesAdded(addedPropertyIds); + datasourceExtension.propertiesAdded(addedPropertyIds); Object frozenPropertyId = columnKeys .get(getState(false).lastFrozenColumnId); @@ -460,53 +171,8 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { } }; - private ItemSetChangeListener itemListener = new ItemSetChangeListener() { - @Override - public void containerItemSetChange(ItemSetChangeEvent event) { - - if (event instanceof ItemAddEvent) { - ItemAddEvent addEvent = (ItemAddEvent) event; - int firstIndex = addEvent.getFirstIndex(); - int count = addEvent.getAddedItemsCount(); - datasourceExtension.insertRowData(firstIndex, count); - activeRowHandler.insertRows(firstIndex, count); - } - - else if (event instanceof ItemRemoveEvent) { - ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; - int firstIndex = removeEvent.getFirstIndex(); - int count = removeEvent.getRemovedItemsCount(); - datasourceExtension.removeRowData(firstIndex, count); - - /* - * Unfortunately, there's no sane way of getting the rest of the - * removed itemIds. - * - * Fortunately, the only time _currently_ an event with more - * than one removed item seems to be when calling - * AbstractInMemoryContainer.removeAllElements(). Otherwise, - * it's only removing one item at a time. - * - * We _could_ have a backup of all the itemIds, and compare to - * that one, but we really really don't want to go there. - */ - activeRowHandler.removeItemId(removeEvent.getFirstItemId()); - } - - else { - // TODO no diff info available, redraw everything - throw new UnsupportedOperationException("bare " - + "ItemSetChangeEvents are currently " - + "not supported, use a container that " - + "uses AddItemEvents and RemoveItemEvents."); - } - } - }; - private RpcDataProviderExtension datasourceExtension; - private final ActiveRowHandler activeRowHandler = new ActiveRowHandler(); - /** * The selection model that is currently in use. Never null * after the constructor has been run. @@ -526,14 +192,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { public Grid(Container.Indexed datasource) { setContainerDataSource(datasource); setSelectionMode(SelectionMode.MULTI); - - registerRpc(new GridServerRpc() { - @Override - public void setVisibleRows(int firstVisibleRow, int visibleRowCount) { - activeRowHandler - .setActiveRows(firstVisibleRow, visibleRowCount); - } - }); } /** @@ -558,11 +216,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { ((PropertySetChangeNotifier) datasource) .removePropertySetChangeListener(propertyListener); } - if (datasource instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) datasource) - .removeItemSetChangeListener(itemListener); - } - activeRowHandler.clear(); if (datasourceExtension != null) { removeExtension(datasourceExtension); @@ -585,10 +238,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { ((PropertySetChangeNotifier) datasource) .addPropertySetChangeListener(propertyListener); } - if (datasource instanceof ItemSetChangeNotifier) { - ((ItemSetChangeNotifier) datasource) - .addItemSetChangeListener(itemListener); - } /* * activeRowHandler will be updated by the client-side request that * occurs on container change - no need to actively re-insert any diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java index eaf17df8f6..b2a3e6d2ba 100644 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -33,6 +33,11 @@ public interface DataRequestRpc extends ServerRpc { * the index of the first requested row * @param numberOfRows * the number of requested rows + * @param firstCachedRowIndex + * the index of the first cached row + * @param cacheSize + * the number of cached rows */ - public void requestRows(int firstRowIndex, int numberOfRows); + public void requestRows(int firstRowIndex, int numberOfRows, + int firstCachedRowIndex, int cacheSize); } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java deleted file mode 100644 index db0a31ed2c..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.shared.ui.grid; - -import com.vaadin.shared.communication.ServerRpc; - -/** - * TODO - * - * @since 7.2 - * @author Vaadin Ltd - */ -public interface GridServerRpc extends ServerRpc { - - /** - * TODO - * - * @param firstVisibleRow - * the index of the first visible row - * @param visibleRowCount - * the number of rows visible, counted from - * firstVisibleRow - */ - void setVisibleRows(int firstVisibleRow, int visibleRowCount); - -} -- cgit v1.2.3 From d2d803e4aaafe642ea490db7fb4f6c3615103382 Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Thu, 17 Apr 2014 16:37:19 +0300 Subject: Add expansion and limiting for Range (#13334) The added functionality is used by the upcoming caching logic. Change-Id: Ibc4e7103241b2199b85bf7727339d1f6a4b5fc9b --- shared/src/com/vaadin/shared/ui/grid/Range.java | 51 +++++++++++++ .../src/com/vaadin/shared/ui/grid/RangeTest.java | 88 ++++++++++++++++++++++ 2 files changed, 139 insertions(+) (limited to 'shared/src/com') diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java index 2593f7afd9..c28502256c 100644 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -377,4 +377,55 @@ public final class Range implements Serializable { return Range.between(Math.min(getStart(), other.getStart()), Math.max(getEnd(), other.getEnd())); } + + /** + * Creates a range that is expanded the given amounts in both ends. + * + * @param startDelta + * the amount to expand by in the beginning of the range + * @param endDelta + * the amount to expand by in the end of the range + * + * @return an expanded range + * + * @throws IllegalArgumentException + * if the new range would have start > end + */ + public Range expand(int startDelta, int endDelta) + throws IllegalArgumentException { + return Range.between(getStart() - startDelta, getEnd() + endDelta); + } + + /** + * Limits this range to be within the bounds of the provided range. + *

+ * This is basically an optimized way of calculating + * {@link #partitionWith(Range)}[1] without the overhead of + * defining the parts that do not overlap. + *

+ * If the two ranges do not intersect, an empty range is returned. There are + * no guarantees about the position of that range. + * + * @param bounds + * the bounds that the returned range should be limited to + * @return a bounded range + */ + public Range restrictTo(Range bounds) { + boolean startWithin = getStart() >= bounds.getStart(); + boolean endWithin = getEnd() <= bounds.getEnd(); + + if (startWithin) { + if (endWithin) { + return this; + } else { + return Range.between(getStart(), bounds.getEnd()); + } + } else { + if (endWithin) { + return Range.between(bounds.getStart(), getEnd()); + } else { + return bounds; + } + } + } } diff --git a/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java b/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java index b042cee509..ab67b22d0b 100644 --- a/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java +++ b/shared/tests/src/com/vaadin/shared/ui/grid/RangeTest.java @@ -315,4 +315,92 @@ public class RangeTest { assertEquals(r1, combined1); } + @Test + public void expand_basic() { + Range r1 = Range.between(5, 10); + Range r2 = r1.expand(2, 3); + + assertEquals(Range.between(3, 13), r2); + } + + @Test + public void expand_negativeLegal() { + Range r1 = Range.between(5, 10); + + Range r2 = r1.expand(-2, -2); + assertEquals(Range.between(7, 8), r2); + + Range r3 = r1.expand(-3, -2); + assertEquals(Range.between(8, 8), r3); + + Range r4 = r1.expand(3, -8); + assertEquals(Range.between(2, 2), r4); + } + + @Test(expected = IllegalArgumentException.class) + public void expand_negativeIllegal1() { + Range r1 = Range.between(5, 10); + + // Should throw because the start would contract beyond the end + r1.expand(-3, -3); + + } + + @Test(expected = IllegalArgumentException.class) + public void expand_negativeIllegal2() { + Range r1 = Range.between(5, 10); + + // Should throw because the end would contract beyond the start + r1.expand(3, -9); + } + + @Test + public void restrictTo_fullyInside() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(4, 11); + + Range r3 = r1.restrictTo(r2); + assertTrue(r1 == r3); + } + + @Test + public void restrictTo_fullyOutside() { + Range r1 = Range.between(4, 11); + Range r2 = Range.between(5, 10); + + Range r3 = r1.restrictTo(r2); + assertTrue(r2 == r3); + } + + public void restrictTo_notInterstecting() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(15, 20); + + Range r3 = r1.restrictTo(r2); + assertTrue("Non-intersecting ranges should produce an empty result", + r3.isEmpty()); + + Range r4 = r2.restrictTo(r1); + assertTrue("Non-intersecting ranges should produce an empty result", + r4.isEmpty()); + } + + public void restrictTo_startOutside() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(7, 15); + + Range r3 = r1.restrictTo(r2); + + assertEquals(Range.between(7, 10), r3); + } + + public void restrictTo_endOutside() { + Range r1 = Range.between(5, 10); + Range r2 = Range.between(4, 7); + + Range r3 = r1.restrictTo(r2); + + assertEquals(Range.between(5, 7), r3); + } + } -- cgit v1.2.3 From 37436ce6448c8bc20bb866a5c259f58e8f50c0c3 Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Thu, 22 May 2014 15:54:36 +0300 Subject: Client-side selection checkbox renderer (#13334) Change-Id: I7b6a5c4ca1d78a97c34b1f7b95d1488edeb8551e --- client/src/com/vaadin/client/ui/grid/Grid.java | 168 ++++++++++++++++++++- .../ui/grid/selection/MultiSelectionRenderer.java | 34 +++++ server/src/com/vaadin/ui/components/grid/Grid.java | 5 + .../src/com/vaadin/shared/ui/grid/GridState.java | 5 + .../tests/components/grid/GridBasicFeatures.java | 8 + 5 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 44e0edce8e..0d8c8f9ba9 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -38,6 +38,7 @@ import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.SubPartAware; import com.vaadin.client.ui.grid.renderers.ComplexRenderer; import com.vaadin.client.ui.grid.renderers.TextRenderer; +import com.vaadin.client.ui.grid.selection.MultiSelectionRenderer; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.Range; @@ -78,6 +79,92 @@ import com.vaadin.shared.util.SharedUtil; */ public class Grid extends Composite implements SubPartAware { + private class SelectionColumn extends GridColumn { + private boolean initDone = false; + + public SelectionColumn(final Renderer selectColumnRenderer) { + super(selectColumnRenderer); + + setHeaderRenderer(new Renderer() { + @Override + public void render(FlyweightCell cell, String data) { + if (cell.getRow() == escalator.getHeader().getRowCount() - 1) { + selectColumnRenderer.render(cell, Boolean.FALSE); + } + } + }); + } + + public void initDone() { + initDone = true; + } + + @Override + public void setFooterCaption(String caption) { + if (initDone) { + throw new UnsupportedOperationException( + "The selection column is read only"); + } else { + super.setFooterCaption(caption); + } + } + + @Override + public void setFooterRenderer(Renderer renderer) { + if (initDone) { + throw new UnsupportedOperationException( + "The selection column is read only"); + } else { + super.setFooterRenderer(renderer); + } + } + + @Override + public void setHeaderCaption(String caption) { + if (initDone) { + throw new UnsupportedOperationException( + "The selection column is read only"); + } else { + super.setHeaderCaption(caption); + } + } + + @Override + public void setHeaderRenderer(Renderer renderer) { + if (initDone) { + throw new UnsupportedOperationException( + "The selection column is read only"); + } else { + super.setHeaderRenderer(renderer); + } + } + + @Override + public void setVisible(boolean visible) { + if (initDone) { + throw new UnsupportedOperationException( + "The selection column is read only"); + } else { + super.setVisible(visible); + } + } + + @Override + public void setWidth(int pixels) { + if (initDone) { + throw new UnsupportedOperationException( + "The selection column is read only"); + } else { + super.setWidth(pixels); + } + } + + @Override + public Boolean getValue(T row) { + return Boolean.valueOf(isSelected(row)); + } + } + /** * Escalator used internally by grid to render the rows */ @@ -114,6 +201,10 @@ public class Grid extends Composite implements SubPartAware { */ private GridColumn lastFrozenColumn; + private Renderer selectColumnRenderer = null; + + private SelectionColumn selectionColumn; + /** * Base class for grid columns internally used by the Grid. The user should * use {@link GridColumn} when creating new columns. @@ -215,10 +306,12 @@ public class Grid extends Composite implements SubPartAware { this.grid = grid; - setVisible(this.visible); - setWidth(this.width); - setHeaderCaption(this.header); - setFooterCaption(this.footer); + if (grid != null) { + setVisible(this.visible); + setWidth(this.width); + setHeaderCaption(this.header); + setFooterCaption(this.footer); + } } /** @@ -796,8 +889,7 @@ public class Grid extends Composite implements SubPartAware { * the column to add */ public void addColumn(GridColumn column) { - ColumnConfiguration conf = escalator.getColumnConfiguration(); - addColumn(column, conf.getColumnCount()); + addColumn(column, getColumnCount()); } /** @@ -807,9 +899,24 @@ public class Grid extends Composite implements SubPartAware { * the index where the column should be inserted into * @param column * the column to add + * @throws IllegalStateException + * if Grid's current selection model renders a selection column, + * and {@code index} is 0. */ public void addColumn(GridColumn column, int index) { + if (column == selectionColumn) { + throw new IllegalArgumentException("The selection column many " + + "not be added manually"); + } else if (selectionColumn != null && index == 0) { + throw new IllegalStateException("A column cannot be inserted " + + "before the selection column"); + } + addColumnSkipSelectionColumnCheck(column, index); + } + + private void addColumnSkipSelectionColumnCheck(GridColumn column, + int index) { // Register column with grid columns.add(index, column); @@ -889,7 +996,15 @@ public class Grid extends Composite implements SubPartAware { * the column to remove */ public void removeColumn(GridColumn column) { + if (column != null && column.equals(selectionColumn)) { + throw new IllegalArgumentException( + "The selection column may not be removed manually."); + } + + removeColumnSkipSelectionColumnCheck(column); + } + private void removeColumnSkipSelectionColumnCheck(GridColumn column) { int columnIndex = columns.indexOf(column); int visibleIndex = findVisibleColumnIndex(column); columns.remove(columnIndex); @@ -1559,4 +1674,45 @@ public class Grid extends Composite implements SubPartAware { } return null; } + + private void setSelectColumnRenderer( + final Renderer selectColumnRenderer) { + if (this.selectColumnRenderer == selectColumnRenderer) { + return; + } + + if (this.selectColumnRenderer != null) { + removeColumnSkipSelectionColumnCheck(selectionColumn); + } + + this.selectColumnRenderer = selectColumnRenderer; + + if (selectColumnRenderer != null) { + selectionColumn = new SelectionColumn(selectColumnRenderer); + + // FIXME: this needs to be done elsewhere, requires design... + selectionColumn.setWidth(25); + addColumnSkipSelectionColumnCheck(selectionColumn, 0); + selectionColumn.initDone(); + } else { + selectionColumn = null; + } + } + + /* TODO remove before final */ + public void setSelectionCheckboxes(boolean set) { + if (set) { + setSelectColumnRenderer(new MultiSelectionRenderer()); + } else { + setSelectColumnRenderer(null); + } + } + + /* + * This is the same client-side Grid "isSelected" method as in the selection + * model design. + */ + private boolean isSelected(T row) { + return false; + } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java b/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java new file mode 100644 index 0000000000..847c897b43 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.selection; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.user.client.DOM; +import com.vaadin.client.ui.grid.FlyweightCell; +import com.vaadin.client.ui.grid.Renderer; + +/* This class will probably not survive the final merge of all selection functionality. */ +public class MultiSelectionRenderer implements Renderer { + @Override + public void render(FlyweightCell cell, Boolean data) { + Element checkbox = Element.as(DOM.createInputCheck()); + if (Boolean.TRUE.equals(data)) { + checkbox.setAttribute("checked", "checked"); + } + cell.getElement().removeAllChildren(); + cell.getElement().appendChild(checkbox); + } +} diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 49b3f1fa64..7f3e3440c7 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -925,4 +925,9 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { removeListener(SelectionChangeEvent.class, listener, SELECTION_CHANGE_METHOD); } + + /** FIXME remove once selection mode communcation is done. only for testing. */ + public void setSelectionCheckboxes(boolean value) { + getState().selectionCheckboxes = value; + } } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 8fdd8c8ec5..acb2a48e3c 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import com.vaadin.shared.AbstractComponentState; +import com.vaadin.shared.annotations.DelegateToWidget; /** * The shared state for the {@link com.vaadin.ui.components.grid.Grid} component @@ -85,4 +86,8 @@ public class GridState extends AbstractComponentState { */ public HeightMode heightMode = HeightMode.CSS; + /** FIXME remove once selection mode communcation is done. only for testing. */ + @DelegateToWidget + public boolean selectionCheckboxes; + } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java index 91bd6b032d..1dc500202e 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java @@ -147,6 +147,14 @@ public class GridBasicFeatures extends AbstractComponentTest { protected void createColumnActions() { createCategory("Columns", null); + createBooleanAction("Selection controls", "Columns", false, + new Command() { + @Override + public void execute(Grid grid, Boolean value, Object data) { + grid.setSelectionCheckboxes(value); + } + }); + for (int c = 0; c < COLUMNS; c++) { createCategory(getColumnProperty(c), "Columns"); -- cgit v1.2.3 From c53db1fe7ee2c6993dcb38574fdeed1391658cd9 Mon Sep 17 00:00:00 2001 From: John Ahlroos Date: Thu, 5 Jun 2014 09:53:44 +0300 Subject: Call ComplexRenderer.setContentVisible() from Grid body updater #13334 Change-Id: Icba9d9f3e5cdc6412c9c004356c33304024be33b --- client/src/com/vaadin/client/ui/grid/Grid.java | 238 ++++++++++++--------- .../client/ui/grid/renderers/ComplexRenderer.java | 34 ++- .../com/vaadin/shared/ui/grid/GridConstants.java | 1 - .../tests/components/grid/GridClientRenderers.java | 70 ++++++ .../grid/GridClientColumnRendererConnector.java | 86 +++++++- 5 files changed, 320 insertions(+), 109 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index ceaa63c9b6..ca3c272491 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -58,7 +58,7 @@ import com.vaadin.shared.util.SharedUtil; /** * A data grid view that supports columns and lazy loading of data rows from a * data source. - * + * *

Columns

*

* The {@link GridColumn} class defines the renderer used to render a cell in @@ -72,15 +72,15 @@ import com.vaadin.shared.util.SharedUtil; * specific column index using {@link Grid#getColumn(int)}. *

*

- * + * * TODO Explain about headers/footers once the multiple header/footer api has * been implemented - * + * *

Data sources

*

* TODO Explain about what a data source is and how it should be implemented. *

- * + * * @param * The row type of the grid. The row type is the POJO type from where * the data is retrieved into the column cells. @@ -217,6 +217,9 @@ public class Grid extends Composite implements private SelectionColumn selectionColumn; + private String rowHasDataStyleName; + private String rowSelectedStyleName; + /** * Current selection model. */ @@ -266,10 +269,10 @@ public class Grid extends Composite implements /** * Base class for grid columns internally used by the Grid. The user should * use {@link GridColumn} when creating new columns. - * + * * @param * the column type - * + * * @param * the row type */ @@ -317,7 +320,7 @@ public class Grid extends Composite implements /** * Constructs a new column with a custom renderer. - * + * * @param renderer * The renderer to use for rendering the cells */ @@ -331,7 +334,7 @@ public class Grid extends Composite implements /** * Constructs a new column with custom renderers for rows, header and * footer cells. - * + * * @param bodyRenderer * The renderer to use for rendering body cells * @param headerRenderer @@ -352,7 +355,7 @@ public class Grid extends Composite implements /** * Internally used by the grid to set itself - * + * * @param grid */ private void setGrid(Grid grid) { @@ -375,7 +378,7 @@ public class Grid extends Composite implements /** * Gets text in the header of the column. By default the header caption * is empty. - * + * * @return the text displayed in the column caption */ public String getHeaderCaption() { @@ -384,7 +387,7 @@ public class Grid extends Composite implements /** * Returns the renderer used for rendering the header cells - * + * * @return a renderer that renders header cells */ public Renderer getHeaderRenderer() { @@ -393,7 +396,7 @@ public class Grid extends Composite implements /** * Sets the renderer that renders header cells. Should not be null. - * + * * @param renderer * The renderer to use for rendering header cells. */ @@ -409,7 +412,7 @@ public class Grid extends Composite implements /** * Returns the renderer used for rendering the footer cells - * + * * @return a renderer that renders footer cells */ public Renderer getFooterRenderer() { @@ -418,7 +421,7 @@ public class Grid extends Composite implements /** * Sets the renderer that renders footer cells. Should not be null. - * + * * @param renderer * The renderer to use for rendering footer cells. */ @@ -434,7 +437,7 @@ public class Grid extends Composite implements /** * Sets the text in the header of the column. - * + * * @param caption * the text displayed in the column header */ @@ -453,7 +456,7 @@ public class Grid extends Composite implements /** * Gets text in the footer of the column. By default the footer caption * is empty. - * + * * @return The text displayed in the footer of the column */ public String getFooterCaption() { @@ -462,7 +465,7 @@ public class Grid extends Composite implements /** * Sets text in the footer of the column. - * + * * @param caption * the text displayed in the footer of the column */ @@ -480,7 +483,7 @@ public class Grid extends Composite implements /** * Is the column visible. By default all columns are visible. - * + * * @return true if the column is visible */ @Override @@ -490,7 +493,7 @@ public class Grid extends Composite implements /** * Sets a column as visible in the grid. - * + * * @param visible * true if the column should be displayed in the * grid @@ -525,10 +528,10 @@ public class Grid extends Composite implements *

* To support other types you will need to pass a custom renderer to the * column via the column constructor. - * + * * @param row * The row object that provides the cell content. - * + * * @return The cell content */ public abstract C getValue(T row); @@ -537,7 +540,7 @@ public class Grid extends Composite implements * The renderer to render the cell width. By default renders the data as * a String or adds the widget into the cell if the column type is of * widget type. - * + * * @return The renderer to render the cell content with */ public Renderer getRenderer() { @@ -546,7 +549,7 @@ public class Grid extends Composite implements /** * Finds the index of this column instance - * + * */ private int findIndexOfColumn() { return grid.findVisibleColumnIndex((GridColumn) this); @@ -555,7 +558,7 @@ public class Grid extends Composite implements /** * Sets the pixel width of the column. Use a negative value for the grid * to autosize column based on content and available space - * + * * @param pixels * the width in pixels or negative for auto sizing */ @@ -572,7 +575,7 @@ public class Grid extends Composite implements /** * Returns the pixel width of the column - * + * * @return pixel width of the column */ public int getWidth() { @@ -605,7 +608,7 @@ public class Grid extends Composite implements /** * Constructs an updater for updating a header / footer - * + * * @param rows * The row container * @param inverted @@ -618,17 +621,17 @@ public class Grid extends Composite implements /** * Gets the header/footer caption value - * + * * @param column * The column to get the value for. - * + * * @return The value that should be rendered for the column caption */ public abstract String getColumnValue(GridColumn column); /** * Gets the group caption value - * + * * @param group * The group for with the caption value should be returned * @return The value that should be rendered for the column caption @@ -637,34 +640,34 @@ public class Grid extends Composite implements /** * Is the row visible in the header/footer - * + * * @param row * the row to check - * + * * @return true if the row should be visible */ public abstract boolean isRowVisible(ColumnGroupRow row); /** * Should the first row be visible - * + * * @return true if the first row should be visible */ public abstract boolean firstRowIsVisible(); /** * The renderer that renders the cell - * + * * @param column * The column for which the cell should be rendered - * + * * @return renderer used for rendering */ public abstract Renderer getRenderer(GridColumn column); /** * The renderer that renders the cell for column groups - * + * * @param group * The group that should be rendered * @return renderer used for rendering @@ -784,12 +787,14 @@ public class Grid extends Composite implements public void setStylePrimaryName(String style) { super.setStylePrimaryName(style); escalator.setStylePrimaryName(style); + rowHasDataStyleName = getStylePrimaryName() + "-row-has-data"; + rowSelectedStyleName = getStylePrimaryName() + "-row-selected"; } /** * Creates the header updater that updates the escalator header rows from * the column and column group rows. - * + * * @return the updater that updates the data in the escalator. */ private EscalatorUpdater createHeaderUpdater() { @@ -865,26 +870,63 @@ public class Grid extends Composite implements @Override public void update(Row row, Iterable cellsToUpdate) { int rowIndex = row.getRow(); + Element rowElement = row.getElement(); T rowData = dataSource.getRow(rowIndex); - if (rowData == null) { - return; + + boolean hasData = rowData != null; + + // Assign stylename for rows with data + boolean usedToHaveData = rowElement + .hasClassName(rowHasDataStyleName); + + if (usedToHaveData != hasData) { + setStyleName(rowElement, rowHasDataStyleName, hasData); + } + + // Assign stylename for selected rows + if (hasData) { + setStyleName(rowElement, rowSelectedStyleName, + isSelected(rowData)); + } else if (usedToHaveData) { + setStyleName(rowElement, rowSelectedStyleName, false); } for (FlyweightCell cell : cellsToUpdate) { GridColumn column = getColumnFromVisibleIndex(cell .getColumn()); + assert column != null : "Column was not found from cell (" + cell.getColumn() + "," + cell.getRow() + ")"; - Object value = column.getValue(rowData); - Renderer renderer = findRenderer(cell); - renderer.render(cell, value); - } - final String selectedClassName = getStylePrimaryName() - + "-row-selected"; + Renderer renderer = column.getRenderer(); - setStyleName(row.getElement(), selectedClassName, - isSelected(rowData)); + // Hide cell content if needed + if (renderer instanceof ComplexRenderer) { + ComplexRenderer clxRenderer = (ComplexRenderer) renderer; + if (hasData) { + if (!usedToHaveData) { + // Prepare cell for rendering + clxRenderer.setContentVisible(cell, true); + } + + Object value = column.getValue(rowData); + clxRenderer.render(cell, value); + + } else if (usedToHaveData) { + // Prepare cell for no data + clxRenderer.setContentVisible(cell, false); + } + + } else if (hasData) { + // Simple renderers just render + Object value = column.getValue(rowData); + renderer.render(cell, value); + + } else { + // Clear cell if there is no data + cell.getElement().removeAllChildren(); + } + } } @Override @@ -922,7 +964,7 @@ public class Grid extends Composite implements /** * Creates the footer updater that updates the escalator footer rows from * the column and column group rows. - * + * * @return the updater that updates the data in the escalator. */ private EscalatorUpdater createFooterUpdater() { @@ -962,7 +1004,7 @@ public class Grid extends Composite implements /** * Refreshes header or footer rows on demand - * + * * @param rows * The row container * @param firstRowIsVisible @@ -1014,7 +1056,7 @@ public class Grid extends Composite implements /** * Adds a column as the last column in the grid. - * + * * @param column * the column to add */ @@ -1024,7 +1066,7 @@ public class Grid extends Composite implements /** * Inserts a column into a specific position in the grid. - * + * * @param index * the index where the column should be inserted into * @param column @@ -1128,7 +1170,7 @@ public class Grid extends Composite implements /** * Removes a column from the grid. - * + * * @param column * the column to remove */ @@ -1163,7 +1205,7 @@ public class Grid extends Composite implements /** * Returns the amount of columns in the grid. - * + * * @return The number of columns in the grid */ public int getColumnCount() { @@ -1172,7 +1214,7 @@ public class Grid extends Composite implements /** * Returns a list of columns in the grid. - * + * * @return A unmodifiable list of the columns in the grid */ public List> getColumns() { @@ -1182,7 +1224,7 @@ public class Grid extends Composite implements /** * Returns a column by its index in the grid. - * + * * @param index * the index of the column * @return The column in the given index @@ -1199,30 +1241,30 @@ public class Grid extends Composite implements /** * Set the column headers visible. - * + * *

* A column header is a single cell header on top of each column reserved * for a specific header for that column. The column header can be set by * {@link GridColumn#setHeaderCaption(String)} and column headers cannot be * merged with other column headers. *

- * + * *

* All column headers occupy the first header row of the grid. If you do not * wish to show the column headers in the grid you should hide the row by * setting visibility of the header row to false. *

- * + * *

* If you want to merge the column headers into groups you can use * {@link ColumnGroupRow}s to group columns together and give them a common * header. See {@link #addColumnGroupRow()} for details. *

- * + * *

* The header row is by default visible. *

- * + * * @param visible * true if header rows should be visible */ @@ -1236,7 +1278,7 @@ public class Grid extends Composite implements /** * Are the column headers visible - * + * * @return true if they are visible */ public boolean isColumnHeadersVisible() { @@ -1245,30 +1287,30 @@ public class Grid extends Composite implements /** * Set the column footers visible. - * + * *

* A column footer is a single cell footer below of each column reserved for * a specific footer for that column. The column footer can be set by * {@link GridColumn#setFooterCaption(String)} and column footers cannot be * merged with other column footers. *

- * + * *

* All column footers occupy the first footer row of the grid. If you do not * wish to show the column footers in the grid you should hide the row by * setting visibility of the footer row to false. *

- * + * *

* If you want to merge the column footers into groups you can use * {@link ColumnGroupRow}s to group columns together and give them a common * footer. See {@link #addColumnGroupRow()} for details. *

- * + * *

* The footer row is by default hidden. *

- * + * * @param visible * true if the footer row should be visible */ @@ -1282,9 +1324,9 @@ public class Grid extends Composite implements /** * Are the column footers visible - * + * * @return true if they are visible - * + * */ public boolean isColumnFootersVisible() { return columnFootersVisible; @@ -1292,15 +1334,15 @@ public class Grid extends Composite implements /** * Adds a new column group row to the grid. - * + * *

* Column group rows are rendered in the header and footer of the grid. * Column group rows are made up of column groups which groups together * columns for adding a common auxiliary header or footer for the columns. *

- * + * * Example usage: - * + * *
      * // Add a new column group row to the grid
      * ColumnGroupRow row = grid.addColumnGroupRow();
@@ -1314,7 +1356,7 @@ public class Grid extends Composite implements
      * // Set a common footer for "Column1" and "Column2"
      * column12.setFooter("Column 1&2");
      * 
- * + * * @return a column group row instance you can use to add column groups */ public ColumnGroupRow addColumnGroupRow() { @@ -1327,10 +1369,10 @@ public class Grid extends Composite implements /** * Adds a new column group row to the grid at a specific index. - * + * * @see #addColumnGroupRow() {@link Grid#addColumnGroupRow()} for example * usage - * + * * @param rowIndex * the index where the column group row should be added * @return a column group row instance you can use to add column groups @@ -1345,7 +1387,7 @@ public class Grid extends Composite implements /** * Removes a column group row - * + * * @param row * The row to remove */ @@ -1357,9 +1399,9 @@ public class Grid extends Composite implements /** * Get the column group rows - * + * * @return a unmodifiable list of column group rows - * + * */ public List> getColumnGroupRows() { return Collections.unmodifiableList(new ArrayList>( @@ -1368,7 +1410,7 @@ public class Grid extends Composite implements /** * Returns the column group for a row and column - * + * * @param row * The row of the column * @param column @@ -1392,7 +1434,7 @@ public class Grid extends Composite implements *

* Note: This method will change the widget's size in the browser * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}. - * + * * @see #setHeightMode(HeightMode) */ @Override @@ -1407,7 +1449,7 @@ public class Grid extends Composite implements /** * Sets the data source used by this grid. - * + * * @param dataSource * the data source to use, not null * @throws IllegalArgumentException @@ -1460,7 +1502,7 @@ public class Grid extends Composite implements *

* All columns up to and including the given column will be frozen in place * when the grid is scrolled sideways. - * + * * @param lastFrozenColumn * the rightmost column to freeze, or null to not * have any columns frozen @@ -1493,7 +1535,7 @@ public class Grid extends Composite implements * Note: Most usually, this method returns the very value set with * {@link #setLastFrozenColumn(GridColumn)}. This value, however, can be * reset to null if the column is removed from this grid. - * + * * @return the rightmost frozen column in the grid, or null if * no columns are frozen. */ @@ -1513,7 +1555,7 @@ public class Grid extends Composite implements /** * Scrolls to a certain row, using {@link ScrollDestination#ANY}. - * + * * @param rowIndex * zero-based index of the row to scroll to. * @throws IllegalArgumentException @@ -1527,7 +1569,7 @@ public class Grid extends Composite implements /** * Scrolls to a certain row, using user-specified scroll destination. - * + * * @param rowIndex * zero-based index of the row to scroll to. * @param destination @@ -1546,7 +1588,7 @@ public class Grid extends Composite implements /** * Scrolls to a certain row using only user-specified parameters. - * + * * @param rowIndex * zero-based index of the row to scroll to. * @param destination @@ -1603,7 +1645,7 @@ public class Grid extends Composite implements *

* If Grid is currently not in {@link HeightMode#ROW}, the given value is * remembered, and applied once the mode is applied. - * + * * @param rows * The height in terms of number of rows displayed in Grid's * body. If Grid doesn't contain enough rows, white space is @@ -1615,7 +1657,7 @@ public class Grid extends Composite implements * infinite} * @throws IllegalArgumentException * if {@code rows} is {@link Double#isNaN(double) NaN} - * + * * @see #setHeightMode(HeightMode) */ public void setHeightByRows(double rows) throws IllegalArgumentException { @@ -1627,7 +1669,7 @@ public class Grid extends Composite implements * {@link #getHeightMode()} is {@link HeightMode#ROW}. *

* By default, it is {@value Escalator#DEFAULT_HEIGHT_BY_ROWS}. - * + * * @return the amount of rows that should be shown in Grid's body, while in * {@link HeightMode#ROW}. * @see #setHeightByRows(double) @@ -1647,7 +1689,7 @@ public class Grid extends Composite implements * Note: If headers/footers are inserted or removed, the widget * will resize itself to still display the required amount of rows in its * body. It also takes the horizontal scrollbar into account. - * + * * @param heightMode * the mode in to which Grid should be set */ @@ -1669,7 +1711,7 @@ public class Grid extends Composite implements * Returns the current {@link HeightMode} the Grid is in. *

* Defaults to {@link HeightMode#CSS}. - * + * * @return the current HeightMode */ public HeightMode getHeightMode() { @@ -1832,7 +1874,7 @@ public class Grid extends Composite implements /** * Accesses the package private method Widget#setParent() - * + * * @param widget * The widget to access * @param parent @@ -1845,7 +1887,7 @@ public class Grid extends Composite implements /** * Sets the current selection model. - * + * * @param selectionModel * a selection model implementation. * @throws IllegalArgumentException @@ -1863,7 +1905,7 @@ public class Grid extends Composite implements /** * Gets a reference to the current selection model. - * + * * @return the currently used SelectionModel instance. */ public SelectionModel getSelectionModel() { @@ -1874,7 +1916,7 @@ public class Grid extends Composite implements * Sets current selection mode. *

* This is a shorthand method for {@link Grid#setSelectionModel}. - * + * * @param mode * a selection mode value * @see {@link SelectionMode}. @@ -1886,7 +1928,7 @@ public class Grid extends Composite implements /** * Test if a row is selected. - * + * * @param row * a row object * @return true, if the current selection model considers the provided row @@ -1902,7 +1944,7 @@ public class Grid extends Composite implements * Only selection models implementing {@link SelectionModel.Single} and * {@link SelectionModel.Multi} are supported; for anything else, an * exception will be thrown. - * + * * @param row * a row object * @return true iff the current selection changed @@ -1927,7 +1969,7 @@ public class Grid extends Composite implements * Only selection models implementing {@link SelectionModel.Single} and * {@link SelectionModel.Multi} are supported; for anything else, an * exception will be thrown. - * + * * @param row * a row object * @return true iff the current selection changed @@ -1952,7 +1994,7 @@ public class Grid extends Composite implements * Only selection models implementing {@link SelectionModel.Single} are * valid for this method; for anything else, use the * {@link Grid#getSelectedRows()} method. - * + * * @return a selected row reference, or null, if no row is selected * @throws IllegalStateException * if the current selection model is not an instance of @@ -1969,7 +2011,7 @@ public class Grid extends Composite implements /** * Gets currently selected rows from the current selection model. - * + * * @return a non-null collection containing all currently selected rows. */ public Collection getSelectedRows() { diff --git a/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java index cf3e43c88a..289f3809be 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java @@ -17,7 +17,10 @@ package com.vaadin.client.ui.grid.renderers; import java.util.Collection; +import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Node; +import com.google.gwt.dom.client.Style.Visibility; import com.vaadin.client.ui.grid.Cell; import com.vaadin.client.ui.grid.FlyweightCell; import com.vaadin.client.ui.grid.Renderer; @@ -96,18 +99,33 @@ public abstract class ComplexRenderer implements Renderer { } /** - * Hides content by setting visibility: hidden to all elements inside the - * cell. Text nodes are left as is for now - renderers that add such to the - * root element need to implement explicit support hiding them + * Used by Grid to toggle whether to show actual data or just an empty + * placeholder while data is loading. This method is invoked whenever a cell + * changes between data being available and data missing. + *

+ * Default implementation hides content by setting visibility: hidden to all + * elements inside the cell. Text nodes are left as is - renderers that add + * such to the root element need to implement explicit support hiding them. * * @param cell * The cell - * @param visible - * Is the cell content be visible - * @return true if the content should be set visible + * @param hasData + * Has the cell content been loaded from the data source + * */ - public boolean setContentVisible(FlyweightCell cell, boolean visible) { - return false; + public void setContentVisible(FlyweightCell cell, boolean hasData) { + Element cellElement = cell.getElement(); + for (int n = 0; n < cellElement.getChildCount(); n++) { + Node node = cellElement.getChild(n); + if (Element.is(node)) { + Element e = Element.as(node); + if (hasData) { + e.getStyle().clearVisibility(); + } else { + e.getStyle().setVisibility(Visibility.HIDDEN); + } + } + } } /** diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java index d5fdd40120..8b264bf426 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -31,5 +31,4 @@ public final class GridConstants implements Serializable { * explicitly defined padding value. */ public static final int DEFAULT_PADDING = 0; - } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java index 2e1a9ab4a7..2f14ccf3f3 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java @@ -16,6 +16,7 @@ package com.vaadin.tests.components.grid; import static org.junit.Assert.assertEquals; +import junit.framework.Assert; import org.junit.Test; import org.openqa.selenium.Alert; @@ -38,15 +39,31 @@ import com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers; */ public class GridClientRenderers extends MultiBrowserTest { + private int latency = 0; + @Override protected Class getUIClass() { return GridClientColumnRenderers.class; } + @Override + protected String getDeploymentPath() { + if (latency > 0) { + return super.getDeploymentPath() + "?latency=" + latency; + } + return super.getDeploymentPath(); + } + @ServerClass("com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers.GridController") public static class MyClientGridElement extends GridElement { } + @Override + public void setup() throws Exception { + latency = 0; // reset + super.setup(); + } + @Test public void addWidgetRenderer() throws Exception { openTestURL(); @@ -88,7 +105,60 @@ public class GridClientRenderers extends MultiBrowserTest { assertEquals(alert.getText(), "Click"); } + @Test + public void rowsWithDataHasStyleName() throws Exception { + + // Simulate network latency with 1000ms + latency = 1000; + + openTestURL(); + + TestBenchElement row = getGrid().getRow(1); + String className = row.getAttribute("class"); + Assert.assertFalse(className.contains("v-grid-row-has-data")); + + // Wait for data to arrive + sleep(3000); + + row = getGrid().getRow(1); + className = row.getAttribute("class"); + Assert.assertTrue(className.contains("v-grid-row-has-data")); + } + + @Test + public void complexRendererSetVisibleContent() throws Exception { + + // Simulate network latency with 1000ms + latency = 1000; + + openTestURL(); + + addColumn(Renderers.CPLX_RENDERER); + + // Fetch data + getGrid().scrollToRow(50); + + // Cell should be red (setContentVisible set cell red) + String backgroundColor = getGrid().getCell(1, 1).getCssValue( + "backgroundColor"); + assertEquals("rgba(255, 0, 0, 1)", backgroundColor); + + // Wait for data to arrive + sleep(3000); + + // Cell should no longer be red + backgroundColor = getGrid().getCell(1, 1) + .getCssValue("backgroundColor"); + assertEquals("rgba(255, 255, 255, 1)", backgroundColor); + } + private GridElement getGrid() { return $(MyClientGridElement.class).first(); } + + private void addColumn(Renderers renderer) { + // Add widget renderer column + $(NativeSelectElement.class).first().selectByText(renderer.toString()); + $(NativeButtonElement.class).caption("Add").first().click(); + } } diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java index a4403f54fc..e4d8042605 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java @@ -21,15 +21,20 @@ import java.util.List; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.HasWidgets; +import com.vaadin.client.data.DataChangeHandler; +import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.grid.FlyweightCell; import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.GridColumn; import com.vaadin.client.ui.grid.Renderer; import com.vaadin.client.ui.grid.datasources.ListDataSource; +import com.vaadin.client.ui.grid.renderers.ComplexRenderer; import com.vaadin.client.ui.grid.renderers.DateRenderer; import com.vaadin.client.ui.grid.renderers.HtmlRenderer; import com.vaadin.client.ui.grid.renderers.NumberRenderer; @@ -43,7 +48,57 @@ public class GridClientColumnRendererConnector extends AbstractComponentConnector { public static enum Renderers { - TEXT_RENDERER, WIDGET_RENDERER, HTML_RENDERER, NUMBER_RENDERER, DATE_RENDERER; + TEXT_RENDERER, WIDGET_RENDERER, HTML_RENDERER, NUMBER_RENDERER, DATE_RENDERER, CPLX_RENDERER; + } + + /** + * Datasource for simulating network latency + */ + private class DelayedDataSource implements DataSource { + + private DataSource ds; + private int firstRowIndex; + private int numberOfRows; + private DataChangeHandler dataChangeHandler; + private int latency; + + public DelayedDataSource(DataSource ds, int latency) { + this.ds = ds; + this.latency = latency; + } + + @Override + public void ensureAvailability(final int firstRowIndex, + final int numberOfRows) { + new Timer() { + + @Override + public void run() { + DelayedDataSource.this.firstRowIndex = firstRowIndex; + DelayedDataSource.this.numberOfRows = numberOfRows; + dataChangeHandler.dataUpdated(firstRowIndex, numberOfRows); + } + }.schedule(latency); + } + + @Override + public String getRow(int rowIndex) { + if (rowIndex >= firstRowIndex + && rowIndex <= firstRowIndex + numberOfRows) { + return ds.getRow(rowIndex); + } + return null; + } + + @Override + public int getEstimatedSize() { + return ds.getEstimatedSize(); + } + + @Override + public void setDataChangeHandler(DataChangeHandler dataChangeHandler) { + this.dataChangeHandler = dataChangeHandler; + } } @Override @@ -58,7 +113,13 @@ public class GridClientColumnRendererConnector extends } // Provide data as data source - grid.setDataSource(new ListDataSource(columnData)); + if (Location.getParameter("latency") != null) { + grid.setDataSource(new DelayedDataSource( + new ListDataSource(columnData), Integer + .parseInt(Location.getParameter("latency")))); + } else { + grid.setDataSource(new ListDataSource(columnData)); + } // Add a column to display the data in grid.addColumn(createColumnWithRenderer(Renderers.TEXT_RENDERER)); @@ -141,6 +202,27 @@ public class GridClientColumnRendererConnector extends case DATE_RENDERER: return new DateRenderer(); + case CPLX_RENDERER: + return new ComplexRenderer() { + + @Override + public void render(FlyweightCell cell, String data) { + cell.getElement().setInnerHTML("" + data + ""); + cell.getElement().getStyle().clearBackgroundColor(); + } + + @Override + public void setContentVisible(FlyweightCell cell, + boolean hasData) { + + // Visualize content visible property + cell.getElement().getStyle() + .setBackgroundColor(hasData ? "green" : "red"); + + super.setContentVisible(cell, hasData); + } + }; + default: return new TextRenderer(); } -- cgit v1.2.3 From 10f7eb04e354a072a04af362f95f831f0656abf7 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Fri, 13 Jun 2014 17:08:07 +0300 Subject: Server-side renderer groundwork (#13334) Change-Id: I12a0fec1b0e49cfb481b0a7c8b6a40a4d43db7f5 --- client/src/com/vaadin/client/ui/grid/Grid.java | 13 +- .../com/vaadin/client/ui/grid/GridConnector.java | 30 ++- .../grid/renderers/AbstractRendererConnector.java | 126 +++++++++++++ .../ui/grid/renderers/TextRendererConnector.java | 43 +++++ .../com/vaadin/data/RpcDataProviderExtension.java | 65 ++++++- server/src/com/vaadin/ui/components/grid/Grid.java | 11 ++ .../com/vaadin/ui/components/grid/GridColumn.java | 182 +++++++++++++++++++ .../com/vaadin/ui/components/grid/Renderer.java | 71 ++++++++ .../grid/renderers/AbstractRenderer.java | 69 +++++++ .../ui/components/grid/renderers/TextRenderer.java | 34 ++++ .../tests/server/component/grid/RendererTest.java | 201 +++++++++++++++++++++ .../com/vaadin/shared/data/DataProviderRpc.java | 2 + .../com/vaadin/shared/ui/grid/GridColumnState.java | 3 + 13 files changed, 831 insertions(+), 19 deletions(-) create mode 100644 client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java create mode 100644 client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java create mode 100644 server/src/com/vaadin/ui/components/grid/Renderer.java create mode 100644 server/src/com/vaadin/ui/components/grid/renderers/AbstractRenderer.java create mode 100644 server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java create mode 100644 server/tests/src/com/vaadin/tests/server/component/grid/RendererTest.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 0e22693638..dfcda40b04 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -515,12 +515,7 @@ public class Grid extends Composite implements } /** - * Returns the data that should be rendered into the cell. By default - * returning Strings and Widgets are supported. If the return type is a - * String then it will be treated as preformatted text. - *

- * To support other types you will need to pass a custom renderer to the - * column via the column constructor. + * Returns the data that should be rendered into the cell. * * @param row * The row object that provides the cell content. @@ -530,11 +525,9 @@ public class Grid extends Composite implements public abstract C getValue(T row); /** - * The renderer to render the cell width. By default renders the data as - * a String or adds the widget into the cell if the column type is of - * widget type. + * Returns the renderer used to render the cells of this column. * - * @return The renderer to render the cell content with + * @return the renderer to render the cell content with */ public Renderer getRenderer() { return bodyRenderer; diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 8685180d38..ab304a9214 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -26,7 +26,7 @@ import java.util.Set; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.client.ui.grid.renderers.TextRenderer; +import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.ColumnGroupState; @@ -53,16 +53,31 @@ public class GridConnector extends AbstractComponentConnector { private final String id; - public CustomGridColumn(String id) { - super(new TextRenderer()); + private AbstractRendererConnector rendererConnector; + + public CustomGridColumn(String id, + AbstractRendererConnector rendererConnector) { + super(rendererConnector.getRenderer()); + this.rendererConnector = rendererConnector; this.id = id; } @Override public String getValue(String[] obj) { + // TODO this should invoke AbstractRendererConnector.decode return obj[resolveCurrentIndexFromState()]; } + /* + * Only used to check that the renderer connector will not change during + * the column lifetime. + * + * TODO remove once support for changing renderers is implemented + */ + private AbstractRendererConnector getRendererConnector() { + return rendererConnector; + } + private int resolveCurrentIndexFromState() { List columns = getState().columns; int numColumns = columns.size(); @@ -195,6 +210,12 @@ public class GridConnector extends AbstractComponentConnector { GridColumn column = getWidget().getColumn(columnIndex); GridColumnState columnState = getState().columns.get(columnIndex); updateColumnFromState(column, columnState); + + if (columnState.rendererConnector != ((CustomGridColumn) column) + .getRendererConnector()) { + throw new UnsupportedOperationException( + "Changing column renderer after initialization is currently unsupported"); + } } /** @@ -205,7 +226,8 @@ public class GridConnector extends AbstractComponentConnector { */ private void addColumnFromStateChangeEvent(int columnIndex) { GridColumnState state = getState().columns.get(columnIndex); - CustomGridColumn column = new CustomGridColumn(state.id); + CustomGridColumn column = new CustomGridColumn(state.id, + ((AbstractRendererConnector) state.rendererConnector)); columnIdToColumn.put(state.id, column); // Adds a column to grid, and registers Grid with the column. diff --git a/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java new file mode 100644 index 0000000000..236fdbe9f6 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.renderers; + +import com.google.gwt.json.client.JSONValue; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.Util; +import com.vaadin.client.communication.JsonDecoder; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.metadata.NoDataException; +import com.vaadin.client.metadata.Type; +import com.vaadin.client.metadata.TypeData; +import com.vaadin.client.ui.grid.Renderer; + +/** + * An abstract base class for renderer connectors. A renderer connector is used + * to link a client-side {@link Renderer} to a server-side + * {@link com.vaadin.ui.components.grid.Renderer Renderer}. As a connector, it + * can use the regular Vaadin RPC and shared state mechanism to pass additional + * state and information between the client and the server. This base class + * itself only uses the basic + * {@link com.vaadin.shared.communication.SharedState SharedState} and no RPC + * interfaces. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public abstract class AbstractRendererConnector extends + AbstractExtensionConnector { + + private Renderer renderer = null; + + /** + * Returns the renderer associated with this renderer connector. + *

+ * A subclass of AbstractRendererConnector should override this method as + * shown below. The framework uses + * {@link com.google.gwt.core.client.GWT#create(Class) GWT.create(Class)} to + * create a renderer based on the return type of the overridden method, but + * only if {@link #createRenderer()} is not overridden as well: + * + *

+     * public MyRenderer getRenderer() {
+     *     return (MyRenderer) super.getRenderer();
+     * }
+     * 
+ * + * @return the renderer bound to this connector + */ + public Renderer getRenderer() { + if (renderer == null) { + renderer = createRenderer(); + } + return renderer; + } + + /** + * Creates a new Renderer instance associated with this renderer connector. + *

+ * You should typically not override this method since the framework by + * default generates an implementation that uses {@link GWT#create(Class)} + * to create a renderer of the same type as returned by the most specific + * override of {@link #getRenderer()}. If you do override the method, you + * can't call super.createRenderer() since the metadata needed + * for that implementation is not generated if there's an override of the + * method. + * + * @return a new renderer to be used with this connector + */ + protected Renderer createRenderer() { + // TODO generate type data + Type type = TypeData.getType(getClass()); + try { + Type rendererType = type.getMethod("getRenderer").getReturnType(); + @SuppressWarnings("unchecked") + Renderer instance = (Renderer) rendererType.createInstance(); + return instance; + } catch (NoDataException e) { + throw new IllegalStateException( + "Default implementation of createRenderer() does not work for " + + Util.getSimpleName(this) + + ". This might be caused by explicitely using " + + "super.createRenderer() or some unspecified " + + "problem with the widgetset compilation.", e); + } + } + + /** + * Decodes the given JSON value into a value of type T so it can be passed + * to the {@link #getRenderer() renderer}. + * + * TODO This method is currently not called from anywhere + * + * @param value + * the value to decode + * @return the decoded value of {@code value} + */ + public T decode(JSONValue value) { + @SuppressWarnings("unchecked") + T decodedValue = (T) JsonDecoder.decodeValue(new Type(getType()), + value, null, getConnection()); + return decodedValue; + } + + // TODO generate data type + protected abstract Class getType(); + + @Override + @Deprecated + protected void extend(ServerConnector target) { + // NOOP + } +} diff --git a/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java new file mode 100644 index 0000000000..76fce7b4b6 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.renderers; + +import com.vaadin.shared.ui.Connect; + +/** + * A connector for {@link TextRenderer}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +@Connect(com.vaadin.ui.components.grid.renderers.TextRenderer.class) +public class TextRendererConnector extends AbstractRendererConnector { + + @Override + public TextRenderer getRenderer() { + return (TextRenderer) super.getRenderer(); + } + + @Override + public TextRenderer createRenderer() { + return new TextRenderer(); + } + + @Override + public Class getType() { + return String.class; + } +} diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 507b2d0f22..79121af6b0 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import com.vaadin.data.Container.Indexed; @@ -31,18 +32,19 @@ import com.vaadin.data.Container.Indexed.ItemRemoveEvent; import com.vaadin.data.Container.ItemSetChangeEvent; import com.vaadin.data.Container.ItemSetChangeListener; import com.vaadin.data.Container.ItemSetChangeNotifier; -import com.vaadin.data.Container.PropertySetChangeListener; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.Property.ValueChangeNotifier; +import com.vaadin.data.util.converter.Converter; import com.vaadin.server.AbstractExtension; import com.vaadin.server.ClientConnector; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataProviderState; import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.grid.Range; -import com.vaadin.ui.Component; import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.GridColumn; +import com.vaadin.ui.components.grid.Renderer; /** * Provides Vaadin server-side container data source to a @@ -397,10 +399,17 @@ public class RpcDataProviderExtension extends AbstractExtension { String[] row = new String[propertyIds.size()]; int i = 0; + final Grid grid = getGrid(); for (Object propertyId : propertyIds) { - Object value = item.getItemProperty(propertyId).getValue(); - String stringValue = String.valueOf(value); - row[i++] = stringValue; + GridColumn column = grid.getColumn(propertyId); + + Object propertyValue = item.getItemProperty(propertyId).getValue(); + Object encodedValue = encodeValue(propertyValue, + column.getRenderer(), column.getConverter(), + grid.getLocale()); + + // TODO Drop string conversion once client supports Objects + row[i++] = String.valueOf(encodedValue); } return row; } @@ -531,4 +540,50 @@ public class RpcDataProviderExtension extends AbstractExtension { public void propertiesAdded(HashSet addedPropertyIds) { activeRowHandler.propertiesAdded(addedPropertyIds); } + + protected Grid getGrid() { + return (Grid) getParent(); + } + + /** + * Converts and encodes the given data model property value using the given + * converter and renderer. This method is public only for testing purposes. + * + * @param renderer + * the renderer to use + * @param converter + * the converter to use + * @param modelValue + * the value to convert and encode + * @param locale + * the locale to use in conversion + * @return an encoded value ready to be sent to the client + */ + public static Object encodeValue(Object modelValue, + Renderer renderer, Converter converter, Locale locale) { + Class presentationType = renderer.getPresentationType(); + T presentationValue; + + if (converter == null) { + try { + presentationValue = presentationType.cast(modelValue); + } catch (ClassCastException e) { + throw new Converter.ConversionException( + "Unable to convert value of type " + + modelValue.getClass().getName() + + " to presentation type " + + presentationType.getName() + + ". No converter is set and the types are not compatible."); + } + } else { + assert presentationType.isAssignableFrom(converter + .getPresentationType()); + @SuppressWarnings("unchecked") + Converter safeConverter = (Converter) converter; + presentationValue = safeConverter.convertToPresentation(modelValue, + safeConverter.getPresentationType(), locale); + } + + return renderer.encode(presentationValue); + } } diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 7f3e3440c7..0d7e799978 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -150,6 +150,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { GridColumn column = columns.remove(columnId); columnKeys.remove(columnId); getState().columns.remove(column.getState()); + removeExtension(column.getRenderer()); } datasourceExtension.propertiesRemoved(removedColumns); @@ -930,4 +931,14 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { public void setSelectionCheckboxes(boolean value) { getState().selectionCheckboxes = value; } + + /** + * Adds a renderer to this grid's connector hierarchy. + * + * @param renderer + * the renderer to add + */ + void addRenderer(Renderer renderer) { + addExtension(renderer); + } } diff --git a/server/src/com/vaadin/ui/components/grid/GridColumn.java b/server/src/com/vaadin/ui/components/grid/GridColumn.java index 76b17bda41..fd504fdae3 100644 --- a/server/src/com/vaadin/ui/components/grid/GridColumn.java +++ b/server/src/com/vaadin/ui/components/grid/GridColumn.java @@ -18,7 +18,12 @@ package com.vaadin.ui.components.grid; import java.io.Serializable; +import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.ConverterUtil; +import com.vaadin.server.VaadinSession; import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.ui.UI; +import com.vaadin.ui.components.grid.renderers.TextRenderer; /** * A column in the grid. Can be obtained by calling @@ -39,6 +44,15 @@ public class GridColumn implements Serializable { */ private final Grid grid; + private Converter converter; + + /** + * A check for allowing the {@link #GridColumn(Grid, GridColumnState) + * constructor} to call {@link #setConverter(Converter)} with a + * null, even if model and renderer aren't compatible. + */ + private boolean isFirstConverterAssignment = true; + /** * Internally used constructor. * @@ -50,6 +64,7 @@ public class GridColumn implements Serializable { GridColumn(Grid grid, GridColumnState state) { this.grid = grid; this.state = state; + internalSetRenderer(new TextRenderer()); } /** @@ -213,4 +228,171 @@ public class GridColumn implements Serializable { checkColumnIsAttached(); grid.setLastFrozenColumn(this); } + + /** + * Sets the renderer for this column. + *

+ * If a suitable converter isn't defined explicitly, the session converter + * factory is used to find a compatible converter. + * + * @param renderer + * the renderer to use + * @throws IllegalArgumentException + * if no compatible converter could be found + * @see VaadinSession#getConverterFactory() + * @see ConverterUtil#getConverter(Class, Class, VaadinSession) + * @see #setConverter(Converter) + */ + public void setRenderer(Renderer renderer) { + if (!internalSetRenderer(renderer)) { + throw new IllegalArgumentException( + "Could not find a converter for converting from the model type " + + getModelType() + + " to the renderer presentation type " + + renderer.getPresentationType()); + } + } + + /** + * Sets the renderer for this column and the converter used to convert from + * the property value type to the renderer presentation type. + * + * @param renderer + * the renderer to use, cannot be null + * @param converter + * the converter to use + * + * @throws IllegalArgumentException + * if the renderer is already associated with a grid column + */ + public void setRenderer(Renderer renderer, + Converter converter) { + if (renderer.getParent() != null) { + throw new IllegalArgumentException( + "Cannot set a renderer that is already connected to a grid column"); + } + + if (getRenderer() != null) { + grid.removeExtension(getRenderer()); + } + + grid.addRenderer(renderer); + state.rendererConnector = renderer; + setConverter(converter); + } + + /** + * Sets the converter used to convert from the property value type to the + * renderer presentation type. + * + * @param converter + * the converter to use, or {@code null} to not use any + * converters + * @throws IllegalArgumentException + * if the types are not compatible + */ + public void setConverter(Converter converter) + throws IllegalArgumentException { + Class modelType = getModelType(); + if (converter != null) { + if (!converter.getModelType().isAssignableFrom(modelType)) { + throw new IllegalArgumentException("The converter model type " + + converter.getModelType() + + " is not compatible with the property type " + + modelType); + + } else if (!getRenderer().getPresentationType().isAssignableFrom( + converter.getPresentationType())) { + throw new IllegalArgumentException( + "The converter presentation type " + + converter.getPresentationType() + + " is not compatible with the renderer presentation type " + + getRenderer().getPresentationType()); + } + } + + else { + /* + * Since the converter is null (i.e. will be removed), we need to + * know that the renderer and model are compatible. If not, we can't + * allow for this to happen. + * + * The constructor is allowed to call this method with null without + * any compatibility checks, therefore we have a special case for + * it. + */ + + Class rendererPresentationType = getRenderer() + .getPresentationType(); + if (!isFirstConverterAssignment + && !rendererPresentationType.isAssignableFrom(modelType)) { + throw new IllegalArgumentException("Cannot remove converter, " + + "as renderer's presentation type " + + rendererPresentationType.getName() + " and column's " + + "model " + modelType.getName() + " type aren't " + + "directly with each other"); + } + } + + isFirstConverterAssignment = false; + + @SuppressWarnings("unchecked") + Converter castConverter = (Converter) converter; + this.converter = castConverter; + } + + /** + * Returns the renderer instance used by this column. + * + * @return the renderer + */ + public Renderer getRenderer() { + return (Renderer) getState().rendererConnector; + } + + /** + * Returns the converter instance used by this column. + * + * @return the converter + */ + public Converter getConverter() { + return converter; + } + + private boolean internalSetRenderer(Renderer renderer) { + + Converter converter; + if (isCompatibleWithProperty(renderer, getConverter())) { + // Use the existing converter (possibly none) if types compatible + converter = (Converter) getConverter(); + } else { + converter = ConverterUtil.getConverter( + renderer.getPresentationType(), getModelType(), + getSession()); + } + setRenderer(renderer, converter); + return isCompatibleWithProperty(renderer, converter); + } + + private VaadinSession getSession() { + UI ui = grid.getUI(); + return ui != null ? ui.getSession() : null; + } + + private boolean isCompatibleWithProperty(Renderer renderer, + Converter converter) { + Class type; + if (converter == null) { + type = getModelType(); + } else { + type = converter.getPresentationType(); + } + return renderer.getPresentationType().isAssignableFrom(type); + } + + private Class getModelType() { + return grid.getContainerDatasource().getType( + grid.getPropertyIdByColumnId(state.id)); + } + } diff --git a/server/src/com/vaadin/ui/components/grid/Renderer.java b/server/src/com/vaadin/ui/components/grid/Renderer.java new file mode 100644 index 0000000000..f3d502eb85 --- /dev/null +++ b/server/src/com/vaadin/ui/components/grid/Renderer.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.ui.components.grid; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.Extension; + +/** + * A ClientConnector for controlling client-side + * {@link com.vaadin.client.ui.grid.Renderer Grid renderers}. Renderers + * currently extend the Extension interface, but this fact should be regarded as + * an implementation detail and subject to change in a future major or minor + * Vaadin revision. + * + * @param + * the type this renderer knows how to present + * + * @since 7.4 + * @author Vaadin Ltd + */ +public interface Renderer extends Extension { + + /** + * Returns the class literal corresponding to the presentation type T. + * + * @return the class literal of T + */ + Class getPresentationType(); + + /** + * Encodes the given value into a form that can be transferred to the + * client. The type of the returned value must be one of the types that are + * accepted by {@code org.json.JSONObject#put(String, Object)}. + * + * @param value + * the value to encode + * @return an encoded form of the given value + */ + Object encode(T value); + + /** + * This method is inherited from Extension but should never be called + * directly with a Renderer. + */ + @Override + @Deprecated + void remove(); + + /** + * This method is inherited from Extension but should never be called + * directly with a Renderer. + */ + @Override + @Deprecated + void setParent(ClientConnector parent); +} diff --git a/server/src/com/vaadin/ui/components/grid/renderers/AbstractRenderer.java b/server/src/com/vaadin/ui/components/grid/renderers/AbstractRenderer.java new file mode 100644 index 0000000000..d5631c6b60 --- /dev/null +++ b/server/src/com/vaadin/ui/components/grid/renderers/AbstractRenderer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.ui.components.grid.renderers; + +import com.vaadin.server.AbstractClientConnector; +import com.vaadin.server.AbstractExtension; +import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.Renderer; + +/** + * An abstract base class for server-side Grid renderers. + * {@link com.vaadin.client.ui.grid.Renderer Grid renderers}. This class + * currently extends the AbstractExtension superclass, but this fact should be + * regarded as an implementation detail and subject to change in a future major + * or minor Vaadin revision. + * + * @param + * the type this renderer knows how to present + * + * @since 7.4 + * @author Vaadin Ltd + */ +public abstract class AbstractRenderer extends AbstractExtension implements + Renderer { + + private final Class presentationType; + + protected AbstractRenderer(Class presentationType) { + this.presentationType = presentationType; + } + + /** + * This method is inherited from AbstractExtension but should never be + * called directly with an AbstractRenderer. + */ + @Deprecated + @Override + protected Class getSupportedParentType() { + return Grid.class; + } + + /** + * This method is inherited from AbstractExtension but should never be + * called directly with an AbstractRenderer. + */ + @Deprecated + @Override + protected void extend(AbstractClientConnector target) { + super.extend(target); + } + + @Override + public Class getPresentationType() { + return presentationType; + } +} diff --git a/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java b/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java new file mode 100644 index 0000000000..28aae27085 --- /dev/null +++ b/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.ui.components.grid.renderers; + +/** + * A renderer for presenting simple plain-text string values. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class TextRenderer extends AbstractRenderer { + + public TextRenderer() { + super(String.class); + } + + @Override + public Object encode(String value) { + return value; + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/RendererTest.java b/server/tests/src/com/vaadin/tests/server/component/grid/RendererTest.java new file mode 100644 index 0000000000..1ea501ff01 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/grid/RendererTest.java @@ -0,0 +1,201 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.util.Locale; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Item; +import com.vaadin.data.RpcDataProviderExtension; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.Converter.ConversionException; +import com.vaadin.data.util.converter.StringToIntegerConverter; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.GridColumn; +import com.vaadin.ui.components.grid.renderers.TextRenderer; + +/** + * @since 7.4 + * @author Vaadin Ltd + */ +public class RendererTest { + + private static class TestBean { + int i = 42; + } + + private static class ExtendedBean extends TestBean { + float f = 3.14f; + } + + private static class TestRenderer extends TextRenderer { + @Override + public Object encode(String value) { + return "renderer(" + super.encode(value) + ")"; + } + } + + private static class TestConverter implements Converter { + + @Override + public TestBean convertToModel(String value, + Class targetType, Locale locale) + throws ConversionException { + return null; + } + + @Override + public String convertToPresentation(TestBean value, + Class targetType, Locale locale) + throws ConversionException { + if (value instanceof ExtendedBean) { + return "ExtendedBean(" + value.i + ", " + + ((ExtendedBean) value).f + ")"; + } else { + return "TestBean(" + value.i + ")"; + } + } + + @Override + public Class getModelType() { + return TestBean.class; + } + + @Override + public Class getPresentationType() { + return String.class; + } + } + + private Grid grid; + + private GridColumn foo; + private GridColumn bar; + private GridColumn baz; + private GridColumn bah; + + @Before + public void setUp() { + VaadinSession.setCurrent(new VaadinSession(null)); + + IndexedContainer c = new IndexedContainer(); + + c.addContainerProperty("foo", Integer.class, 0); + c.addContainerProperty("bar", String.class, ""); + c.addContainerProperty("baz", TestBean.class, null); + c.addContainerProperty("bah", ExtendedBean.class, null); + + Object id = c.addItem(); + Item item = c.getItem(id); + item.getItemProperty("foo").setValue(123); + item.getItemProperty("bar").setValue("321"); + item.getItemProperty("baz").setValue(new TestBean()); + item.getItemProperty("bah").setValue(new ExtendedBean()); + + grid = new Grid(c); + + foo = grid.getColumn("foo"); + bar = grid.getColumn("bar"); + baz = grid.getColumn("baz"); + bah = grid.getColumn("bah"); + } + + @Test + public void testDefaultRendererAndConverter() throws Exception { + assertSame(TextRenderer.class, foo.getRenderer().getClass()); + assertSame(StringToIntegerConverter.class, foo.getConverter() + .getClass()); + + assertSame(TextRenderer.class, bar.getRenderer().getClass()); + // String->String; converter not needed + assertNull(bar.getConverter()); + + assertSame(TextRenderer.class, baz.getRenderer().getClass()); + // MyBean->String; converter not found + assertNull(baz.getConverter()); + } + + @Test + public void testFindCompatibleConverter() throws Exception { + foo.setRenderer(renderer()); + assertSame(StringToIntegerConverter.class, foo.getConverter() + .getClass()); + + bar.setRenderer(renderer()); + assertNull(bar.getConverter()); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotFindConverter() { + baz.setRenderer(renderer()); + } + + @Test + public void testExplicitConverter() throws Exception { + baz.setRenderer(renderer(), converter()); + bah.setRenderer(renderer(), converter()); + } + + @Test + public void testEncoding() throws Exception { + assertEquals("42", render(foo, 42)); + foo.setRenderer(renderer()); + assertEquals("renderer(42)", render(foo, 42)); + + assertEquals("2.72", render(bar, "2.72")); + bar.setRenderer(new TestRenderer()); + assertEquals("renderer(2.72)", render(bar, "2.72")); + } + + @Test(expected = ConversionException.class) + public void testEncodingWithoutConverter() throws Exception { + render(baz, new TestBean()); + } + + @Test + public void testBeanEncoding() throws Exception { + baz.setRenderer(renderer(), converter()); + bah.setRenderer(renderer(), converter()); + + assertEquals("renderer(TestBean(42))", render(baz, new TestBean())); + assertEquals("renderer(ExtendedBean(42, 3.14))", + render(baz, new ExtendedBean())); + + assertEquals("renderer(ExtendedBean(42, 3.14))", + render(bah, new ExtendedBean())); + } + + private TestConverter converter() { + return new TestConverter(); + } + + private TestRenderer renderer() { + return new TestRenderer(); + } + + private Object render(GridColumn column, Object value) { + return RpcDataProviderExtension.encodeValue(value, + column.getRenderer(), column.getConverter(), grid.getLocale()); + } +} diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index 79e3f17f8d..a2f85159ba 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -30,6 +30,8 @@ public interface DataProviderRpc extends ClientRpc { /** * Sends updated row data to a client. + *

+ * TODO rowData should be List * * @param firstRowIndex * the index of the first updated row diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java index 0301c5ead2..581d4d4365 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java @@ -17,6 +17,8 @@ package com.vaadin.shared.ui.grid; import java.io.Serializable; +import com.vaadin.shared.Connector; + /** * Column state DTO for transferring column properties from the server to the * client @@ -52,4 +54,5 @@ public class GridColumnState implements Serializable { */ public int width = 100; + public Connector rendererConnector; } -- cgit v1.2.3 From f4a538019bc6c5abeeb453d9f116088d03d7c32f Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Wed, 18 Jun 2014 16:04:46 +0300 Subject: Change row data type from String[] to String (#13334) Instead of having the data type as one-string-per-column, we now have the entire row encoded as JSON Change-Id: I709b2daa88c516d98203ef463b57257a6647bacd --- .../vaadin/client/data/RpcDataSourceConnector.java | 27 +++++- .../com/vaadin/client/ui/grid/GridConnector.java | 50 ++++++---- .../grid/renderers/AbstractRendererConnector.java | 2 - .../com/vaadin/data/RpcDataProviderExtension.java | 107 +++++++++++++++------ .../com/vaadin/shared/data/DataProviderRpc.java | 22 ++++- .../src/com/vaadin/shared/ui/grid/GridState.java | 7 ++ .../tests/components/grid/CustomRenderer.java | 60 ++++++++++++ .../tests/components/grid/CustomRendererTest.java | 42 ++++++++ .../tests/components/grid/IntArrayRenderer.java | 36 +++++++ .../client/grid/IntArrayRendererConnector.java | 51 ++++++++++ 10 files changed, 344 insertions(+), 60 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/grid/CustomRenderer.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/IntArrayRenderer.java create mode 100644 uitest/src/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java index fba8c732f6..2b9bf5c90e 100644 --- a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java @@ -16,8 +16,12 @@ package com.vaadin.client.data; -import java.util.List; +import java.util.ArrayList; +import com.google.gwt.json.client.JSONArray; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONParser; +import com.google.gwt.json.client.JSONValue; import com.vaadin.client.ServerConnector; import com.vaadin.client.extensions.AbstractExtensionConnector; import com.vaadin.client.ui.grid.GridConnector; @@ -39,7 +43,7 @@ import com.vaadin.shared.ui.grid.Range; @Connect(com.vaadin.data.RpcDataProviderExtension.class) public class RpcDataSourceConnector extends AbstractExtensionConnector { - private final AbstractRemoteDataSource dataSource = new AbstractRemoteDataSource() { + private final AbstractRemoteDataSource dataSource = new AbstractRemoteDataSource() { @Override protected void requestRows(int firstRowIndex, int numberOfRows) { Range cached = getCachedRange(); @@ -49,7 +53,7 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { } @Override - public Object getRowKey(String[] row) { + public Object getRowKey(JSONObject row) { /* * FIXME will be properly implemented by another patch (Henrik Paul: * 16.6.2014) @@ -65,7 +69,22 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { registerRpc(DataProviderRpc.class, new DataProviderRpc() { @Override - public void setRowData(int firstRow, List rows) { + public void setRowData(int firstRow, String rowsJson) { + JSONValue parsedJson = JSONParser.parseStrict(rowsJson); + JSONArray rowArray = parsedJson.isArray(); + assert rowArray != null : "Was unable to parse JSON into an array: " + + parsedJson; + + ArrayList rows = new ArrayList(rowArray + .size()); + for (int i = 0; i < rowArray.size(); i++) { + JSONValue rowValue = rowArray.get(i); + JSONObject rowObject = rowValue.isObject(); + assert rowObject != null : "Was unable to parse JSON into an object: " + + rowValue; + rows.add(rowObject); + } + dataSource.setRowData(firstRow, rows); } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index ab304a9214..95b493b451 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -24,6 +24,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.google.gwt.json.client.JSONArray; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONValue; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; @@ -38,6 +41,10 @@ import com.vaadin.shared.ui.grid.ScrollDestination; /** * Connects the client side {@link Grid} widget with the server side * {@link com.vaadin.ui.components.grid.Grid} component. + *

+ * The Grid is typed to JSONObject. The structure of the JSONObject is described + * at {@link com.vaadin.shared.data.DataProviderRpc#setRowData(int, List) + * DataProviderRpc.setRowData(int, List)}. * * @since 7.4 * @author Vaadin Ltd @@ -49,23 +56,29 @@ public class GridConnector extends AbstractComponentConnector { * Custom implementation of the custom grid column using a String[] to * represent the cell value and String as a column type. */ - private class CustomGridColumn extends GridColumn { + private class CustomGridColumn extends GridColumn { private final String id; - private AbstractRendererConnector rendererConnector; + private AbstractRendererConnector rendererConnector; public CustomGridColumn(String id, - AbstractRendererConnector rendererConnector) { + AbstractRendererConnector rendererConnector) { super(rendererConnector.getRenderer()); this.rendererConnector = rendererConnector; this.id = id; } @Override - public String getValue(String[] obj) { - // TODO this should invoke AbstractRendererConnector.decode - return obj[resolveCurrentIndexFromState()]; + public Object getValue(final JSONObject obj) { + final JSONValue rowData = obj.get(GridState.JSONKEY_DATA); + final JSONArray rowDataArray = rowData.isArray(); + assert rowDataArray != null : "Was unable to parse JSON into an array: " + + rowData; + + final int columnIndex = resolveCurrentIndexFromState(); + final JSONValue columnValue = rowDataArray.get(columnIndex); + return rendererConnector.decode(columnValue); } /* @@ -74,7 +87,7 @@ public class GridConnector extends AbstractComponentConnector { * * TODO remove once support for changing renderers is implemented */ - private AbstractRendererConnector getRendererConnector() { + private AbstractRendererConnector getRendererConnector() { return rendererConnector; } @@ -97,8 +110,8 @@ public class GridConnector extends AbstractComponentConnector { @Override @SuppressWarnings("unchecked") - public Grid getWidget() { - return (Grid) super.getWidget(); + public Grid getWidget() { + return (Grid) super.getWidget(); } @Override @@ -207,7 +220,7 @@ public class GridConnector extends AbstractComponentConnector { * The index of the column to update */ private void updateColumnFromStateChangeEvent(int columnIndex) { - GridColumn column = getWidget().getColumn(columnIndex); + GridColumn column = getWidget().getColumn(columnIndex); GridColumnState columnState = getState().columns.get(columnIndex); updateColumnFromState(column, columnState); @@ -226,8 +239,9 @@ public class GridConnector extends AbstractComponentConnector { */ private void addColumnFromStateChangeEvent(int columnIndex) { GridColumnState state = getState().columns.get(columnIndex); + @SuppressWarnings("unchecked") CustomGridColumn column = new CustomGridColumn(state.id, - ((AbstractRendererConnector) state.rendererConnector)); + ((AbstractRendererConnector) state.rendererConnector)); columnIdToColumn.put(state.id, column); // Adds a column to grid, and registers Grid with the column. @@ -252,7 +266,7 @@ public class GridConnector extends AbstractComponentConnector { * @param state * The state to get the data from */ - private static void updateColumnFromState(GridColumn column, + private static void updateColumnFromState(GridColumn column, GridColumnState state) { column.setVisible(state.visible); column.setHeaderCaption(state.header); @@ -293,23 +307,25 @@ public class GridConnector extends AbstractComponentConnector { // FIXME When something changes the header/footer rows will be // re-created. At some point we should optimize this so partial updates // can be made on the header/footer. - for (ColumnGroupRow row : getWidget().getColumnGroupRows()) { + for (ColumnGroupRow row : getWidget().getColumnGroupRows()) { getWidget().removeColumnGroupRow(row); } for (ColumnGroupRowState rowState : getState().columnGroupRows) { - ColumnGroupRow row = getWidget().addColumnGroupRow(); + ColumnGroupRow row = getWidget().addColumnGroupRow(); row.setFooterVisible(rowState.footerVisible); row.setHeaderVisible(rowState.headerVisible); for (ColumnGroupState groupState : rowState.groups) { - List> columns = new ArrayList>(); + List> columns = new ArrayList>(); for (String columnId : groupState.columns) { CustomGridColumn column = columnIdToColumn.get(columnId); columns.add(column); } - ColumnGroup group = row.addGroup(columns - .toArray(new GridColumn[columns.size()])); + @SuppressWarnings("unchecked") + final GridColumn[] gridColumns = columns + .toArray(new GridColumn[columns.size()]); + ColumnGroup group = row.addGroup(gridColumns); group.setFooterCaption(groupState.footer); group.setHeaderCaption(groupState.header); } diff --git a/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java index 236fdbe9f6..b57b674292 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java @@ -102,8 +102,6 @@ public abstract class AbstractRendererConnector extends * Decodes the given JSON value into a value of type T so it can be passed * to the {@link #getRenderer() renderer}. * - * TODO This method is currently not called from anywhere - * * @param value * the value to decode * @return the decoded value of {@code value} diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 79121af6b0..0046b256bb 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -17,7 +17,6 @@ package com.vaadin.data; import java.io.Serializable; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -26,6 +25,10 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import com.vaadin.data.Container.Indexed; import com.vaadin.data.Container.Indexed.ItemAddEvent; import com.vaadin.data.Container.Indexed.ItemRemoveEvent; @@ -41,6 +44,7 @@ import com.vaadin.server.ClientConnector; import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataProviderState; import com.vaadin.shared.data.DataRequestRpc; +import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.Range; import com.vaadin.ui.components.grid.Grid; import com.vaadin.ui.components.grid.GridColumn; @@ -68,8 +72,8 @@ public class RpcDataProviderExtension extends AbstractExtension { *
    *
  • listening to the currently visible {@link Property Properties'} value * changes on the server side and sending those back to the client; and - *
  • attaching and detaching {@link Component Components} from the Vaadin - * Component hierarchy. + *
  • attaching and detaching {@link com.vaadin.ui.Component Components} + * from the Vaadin Component hierarchy. *
*/ private class ActiveRowHandler implements Serializable { @@ -191,7 +195,8 @@ public class RpcDataProviderExtension extends AbstractExtension { * @param removedPropertyIds * the property ids that have been removed from the container */ - public void propertiesRemoved(Collection removedPropertyIds) { + public void propertiesRemoved(@SuppressWarnings("unused") + Collection removedPropertyIds) { /* * no-op, for now. * @@ -387,31 +392,48 @@ public class RpcDataProviderExtension extends AbstractExtension { private void pushRows(int firstRow, int numberOfRows) { List itemIds = container.getItemIds(firstRow, numberOfRows); Collection propertyIds = container.getContainerPropertyIds(); - List rows = new ArrayList(itemIds.size()); + JSONArray rows = new JSONArray(); for (Object itemId : itemIds) { - rows.add(getRowData(propertyIds, itemId)); + rows.put(getRowData(propertyIds, itemId)); } - getRpcProxy(DataProviderRpc.class).setRowData(firstRow, rows); + String jsonString = rows.toString(); + getRpcProxy(DataProviderRpc.class).setRowData(firstRow, jsonString); } - private String[] getRowData(Collection propertyIds, Object itemId) { + private JSONObject getRowData(Collection propertyIds, Object itemId) { Item item = container.getItem(itemId); - String[] row = new String[propertyIds.size()]; - int i = 0; - final Grid grid = getGrid(); - for (Object propertyId : propertyIds) { - GridColumn column = grid.getColumn(propertyId); + JSONArray rowData = new JSONArray(); + + Grid grid = getGrid(); + try { + for (Object propertyId : propertyIds) { + GridColumn column = grid.getColumn(propertyId); - Object propertyValue = item.getItemProperty(propertyId).getValue(); - Object encodedValue = encodeValue(propertyValue, - column.getRenderer(), column.getConverter(), - grid.getLocale()); + Object propertyValue = item.getItemProperty(propertyId) + .getValue(); + Object encodedValue = encodeValue(propertyValue, + column.getRenderer(), column.getConverter(), + grid.getLocale()); + + rowData.put(encodedValue); + } - // TODO Drop string conversion once client supports Objects - row[i++] = String.valueOf(encodedValue); + final JSONObject rowObject = new JSONObject(); + rowObject.put(GridState.JSONKEY_DATA, rowData); + /* + * TODO: selection wants to put here something in the lines of: + * + * rowObject.put(GridState.JSONKEY_ROWKEY, getKey(itemId)) + * + * Henrik Paul: 18.6.2014 + */ + return rowObject; + } catch (final JSONException e) { + throw new RuntimeException("Grid was unable to serialize " + + "data for row (this should've been caught " + + "eariler by other Grid logic)", e); } - return row; } @Override @@ -487,9 +509,10 @@ public class RpcDataProviderExtension extends AbstractExtension { * roundtrip. */ Object itemId = container.getIdByIndex(index); - String[] row = getRowData(container.getContainerPropertyIds(), itemId); - getRpcProxy(DataProviderRpc.class).setRowData(index, - Collections.singletonList(row)); + JSONObject row = getRowData(container.getContainerPropertyIds(), itemId); + JSONArray rowArray = new JSONArray(Collections.singleton(row)); + String jsonString = rowArray.toString(); + getRpcProxy(DataProviderRpc.class).setRowData(index, jsonString); } @Override @@ -513,10 +536,11 @@ public class RpcDataProviderExtension extends AbstractExtension { * Informs this data provider that some of the properties have been removed * from the container. *

- * Please note that we could add our own {@link PropertySetChangeListener} - * to the container, but then we'd need to implement the same bookeeping for - * finding what's added and removed that Grid already does in its own - * listener. + * Please note that we could add our own + * {@link com.vaadin.data.Container.PropertySetChangeListener + * PropertySetChangeListener} to the container, but then we'd need to + * implement the same bookeeping for finding what's added and removed that + * Grid already does in its own listener. * * @param removedColumns * a list of property ids for the removed columns @@ -529,10 +553,11 @@ public class RpcDataProviderExtension extends AbstractExtension { * Informs this data provider that some of the properties have been added to * the container. *

- * Please note that we could add our own {@link PropertySetChangeListener} - * to the container, but then we'd need to implement the same bookeeping for - * finding what's added and removed that Grid already does in its own - * listener. + * Please note that we could add our own + * {@link com.vaadin.data.Container.PropertySetChangeListener + * PropertySetChangeListener} to the container, but then we'd need to + * implement the same bookeeping for finding what's added and removed that + * Grid already does in its own listener. * * @param addedPropertyIds * a list of property ids for the added columns @@ -584,6 +609,24 @@ public class RpcDataProviderExtension extends AbstractExtension { safeConverter.getPresentationType(), locale); } - return renderer.encode(presentationValue); + Object encodedValue = renderer.encode(presentationValue); + + /* + * because this is a relatively heavy operation, we'll hide this behind + * an assert so that the check will be removed in production mode + */ + assert jsonSupports(encodedValue) : "org.json.JSONObject does not know how to serialize objects of type " + + encodedValue.getClass().getName(); + return encodedValue; + } + + private static boolean jsonSupports(Object encodedValue) { + JSONObject jsonObject = new JSONObject(); + try { + jsonObject.accumulate("test", encodedValue); + } catch (JSONException e) { + return false; + } + return true; } } diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index a2f85159ba..a92ffe0421 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -16,8 +16,6 @@ package com.vaadin.shared.data; -import java.util.List; - import com.vaadin.shared.communication.ClientRpc; /** @@ -31,14 +29,28 @@ public interface DataProviderRpc extends ClientRpc { /** * Sends updated row data to a client. *

- * TODO rowData should be List + * rowDataJson represents a JSON array of JSON objects in the following + * format: + * + *

+     * [{
+     *   "d": [COL_1_JSON, COL_2_json, ...]
+     * },
+     * ...
+     * ]
+     * 
+ * + * where COL_INDEX is the index of the column (as a string), and COL_n_JSON + * is valid JSON of the column's data. * * @param firstRowIndex * the index of the first updated row - * @param rowData + * @param rowDataJson * the updated row data + * @see com.vaadin.shared.ui.grid.GridState#JSONKEY_DATA + * @see com.vaadin.ui.components.grid.Renderer#encode(Object) */ - public void setRowData(int firstRowIndex, List rowData); + public void setRowData(int firstRowIndex, String rowDataJson); /** * Informs the client to remove row data. diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index acb2a48e3c..eceaedd1fc 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -37,6 +37,13 @@ public class GridState extends AbstractComponentState { */ public static final double DEFAULT_HEIGHT_BY_ROWS = 10.0d; + /** + * The key in which a row's data can be found + * {@link com.vaadin.shared.data.DataProviderRpc#setRowData(int, List) + * DataProviderRpc.setRowData(int, List)} + */ + public static final String JSONKEY_DATA = "d"; + { // FIXME Grid currently does not support undefined size width = "400px"; diff --git a/uitest/src/com/vaadin/tests/components/grid/CustomRenderer.java b/uitest/src/com/vaadin/tests/components/grid/CustomRenderer.java new file mode 100644 index 0000000000..7e079e69b7 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/CustomRenderer.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.grid; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.components.grid.Grid; + +@Widgetset(TestingWidgetSet.NAME) +public class CustomRenderer extends AbstractTestUI { + + private static final Object INT_ARRAY_PROPERTY = "int array"; + + @Override + protected void setup(VaadinRequest request) { + IndexedContainer container = new IndexedContainer(); + container.addContainerProperty(INT_ARRAY_PROPERTY, int[].class, + new int[] {}); + + Object itemId = new Object(); + Item item = container.addItem(itemId); + @SuppressWarnings("unchecked") + Property property = item.getItemProperty(INT_ARRAY_PROPERTY); + property.setValue(new int[] { 1, 1, 2, 3, 5, 8, 13 }); + + Grid grid = new Grid(container); + grid.getColumn(INT_ARRAY_PROPERTY).setRenderer(new IntArrayRenderer()); + addComponent(grid); + } + + @Override + protected String getTestDescription() { + return "Verifies that renderers operating on other data than " + + "just Strings also work "; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(13334); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java b/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java new file mode 100644 index 0000000000..1827f66777 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/CustomRendererTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.grid; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +import com.vaadin.tests.annotations.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +@TestCategory("grid") +public class CustomRendererTest extends MultiBrowserTest { + @Test + public void testIntArrayIsRendered() throws Exception { + openTestURL(); + + GridElement grid = findGrid(); + assertEquals("1 :: 1 :: 2 :: 3 :: 5 :: 8 :: 13", grid.getCell(0, 0) + .getText()); + } + + private GridElement findGrid() { + List elements = $(GridElement.class).all(); + return elements.get(0); + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/IntArrayRenderer.java b/uitest/src/com/vaadin/tests/components/grid/IntArrayRenderer.java new file mode 100644 index 0000000000..9ebae4587d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/IntArrayRenderer.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.grid; + +import org.json.JSONArray; +import org.json.JSONException; + +import com.vaadin.ui.components.grid.renderers.AbstractRenderer; + +public class IntArrayRenderer extends AbstractRenderer { + public IntArrayRenderer() { + super(int[].class); + } + + @Override + public Object encode(int[] value) { + try { + return new JSONArray(value); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java new file mode 100644 index 0000000000..be358c2738 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/IntArrayRendererConnector.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.widgetset.client.grid; + +import com.vaadin.client.ui.grid.FlyweightCell; +import com.vaadin.client.ui.grid.Renderer; +import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; +import com.vaadin.shared.ui.Connect; + +@Connect(com.vaadin.tests.components.grid.IntArrayRenderer.class) +public class IntArrayRendererConnector extends AbstractRendererConnector { + + public class IntArrayRenderer implements Renderer { + private static final String JOINER = " :: "; + + @Override + public void render(FlyweightCell cell, int[] data) { + String text = ""; + for (int i : data) { + text += i + JOINER; + } + if (!text.isEmpty()) { + text = text.substring(0, text.length() - JOINER.length()); + } + cell.getElement().setInnerText(text); + } + } + + @Override + protected IntArrayRenderer createRenderer() { + return new IntArrayRenderer(); + } + + @Override + public Class getType() { + return int[].class; + } +} -- cgit v1.2.3 From 6294a26ab8ae5df83d25318c4a8b14db34f5b8a4 Mon Sep 17 00:00:00 2001 From: Patrik Lindström Date: Tue, 17 Jun 2014 18:30:04 +0300 Subject: Implement Grid client-side Sorting API (#13334) Change-Id: I9ab18c93bdc1aaf66aa2701c3939311671a60f04 --- client/src/com/vaadin/client/ui/grid/Grid.java | 287 ++++++++++++++------- .../client/ui/grid/datasources/ListDataSource.java | 42 ++- .../src/com/vaadin/client/ui/grid/sort/Sort.java | 155 +++++++++++ .../com/vaadin/client/ui/grid/sort/SortEvent.java | 112 ++++++++ .../client/ui/grid/sort/SortEventHandler.java | 38 +++ .../com/vaadin/client/ui/grid/sort/SortOrder.java | 73 ++++++ .../vaadin/client/ui/grid/ListDataSourceTest.java | 27 +- .../com/vaadin/shared/ui/grid/SortDirection.java | 35 +++ .../tests/components/grid/GridClientRenderers.java | 24 +- .../grid/GridClientColumnRendererConnector.java | 39 ++- .../client/grid/GridClientColumnRendererRpc.java | 13 +- .../server/grid/GridClientColumnRenderers.java | 29 ++- 12 files changed, 750 insertions(+), 124 deletions(-) create mode 100644 client/src/com/vaadin/client/ui/grid/sort/Sort.java create mode 100644 client/src/com/vaadin/client/ui/grid/sort/SortEvent.java create mode 100644 client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java create mode 100644 client/src/com/vaadin/client/ui/grid/sort/SortOrder.java create mode 100644 shared/src/com/vaadin/shared/ui/grid/SortDirection.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index dfcda40b04..1739e28608 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -49,16 +49,21 @@ import com.vaadin.client.ui.grid.selection.SelectionModel; import com.vaadin.client.ui.grid.selection.SelectionModelMulti; import com.vaadin.client.ui.grid.selection.SelectionModelNone; import com.vaadin.client.ui.grid.selection.SelectionModelSingle; +import com.vaadin.client.ui.grid.sort.Sort; +import com.vaadin.client.ui.grid.sort.SortEvent; +import com.vaadin.client.ui.grid.sort.SortEventHandler; +import com.vaadin.client.ui.grid.sort.SortOrder; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.ui.grid.ScrollDestination; +import com.vaadin.shared.ui.grid.SortDirection; import com.vaadin.shared.util.SharedUtil; /** * A data grid view that supports columns and lazy loading of data rows from a * data source. - * + * *

Columns

*

* The {@link GridColumn} class defines the renderer used to render a cell in @@ -72,15 +77,15 @@ import com.vaadin.shared.util.SharedUtil; * specific column index using {@link Grid#getColumn(int)}. *

*

- * + * * TODO Explain about headers/footers once the multiple header/footer api has * been implemented - * + * *

Data sources

*

* TODO Explain about what a data source is and how it should be implemented. *

- * + * * @param * The row type of the grid. The row type is the POJO type from where * the data is retrieved into the column cells. @@ -213,6 +218,12 @@ public class Grid extends Composite implements */ private GridColumn lastFrozenColumn; + /** + * Current sort order. The (private) sort() method reads this list to + * determine the order in which to present rows. + */ + private List sortOrder = new ArrayList(); + private Renderer selectColumnRenderer = null; private SelectionColumn selectionColumn; @@ -269,10 +280,10 @@ public class Grid extends Composite implements /** * Base class for grid columns internally used by the Grid. The user should * use {@link GridColumn} when creating new columns. - * + * * @param * the column type - * + * * @param * the row type */ @@ -320,7 +331,7 @@ public class Grid extends Composite implements /** * Constructs a new column with a custom renderer. - * + * * @param renderer * The renderer to use for rendering the cells */ @@ -334,7 +345,7 @@ public class Grid extends Composite implements /** * Constructs a new column with custom renderers for rows, header and * footer cells. - * + * * @param bodyRenderer * The renderer to use for rendering body cells * @param headerRenderer @@ -355,7 +366,7 @@ public class Grid extends Composite implements /** * Internally used by the grid to set itself - * + * * @param grid */ private void setGrid(Grid grid) { @@ -371,7 +382,7 @@ public class Grid extends Composite implements /** * Gets text in the header of the column. By default the header caption * is empty. - * + * * @return the text displayed in the column caption */ public String getHeaderCaption() { @@ -380,7 +391,7 @@ public class Grid extends Composite implements /** * Returns the renderer used for rendering the header cells - * + * * @return a renderer that renders header cells */ public Renderer getHeaderRenderer() { @@ -389,7 +400,7 @@ public class Grid extends Composite implements /** * Sets the renderer that renders header cells. Should not be null. - * + * * @param renderer * The renderer to use for rendering header cells. */ @@ -405,7 +416,7 @@ public class Grid extends Composite implements /** * Returns the renderer used for rendering the footer cells - * + * * @return a renderer that renders footer cells */ public Renderer getFooterRenderer() { @@ -414,7 +425,7 @@ public class Grid extends Composite implements /** * Sets the renderer that renders footer cells. Should not be null. - * + * * @param renderer * The renderer to use for rendering footer cells. */ @@ -430,7 +441,7 @@ public class Grid extends Composite implements /** * Sets the text in the header of the column. - * + * * @param caption * the text displayed in the column header */ @@ -449,7 +460,7 @@ public class Grid extends Composite implements /** * Gets text in the footer of the column. By default the footer caption * is empty. - * + * * @return The text displayed in the footer of the column */ public String getFooterCaption() { @@ -458,7 +469,7 @@ public class Grid extends Composite implements /** * Sets text in the footer of the column. - * + * * @param caption * the text displayed in the footer of the column */ @@ -476,7 +487,7 @@ public class Grid extends Composite implements /** * Is the column visible. By default all columns are visible. - * + * * @return true if the column is visible */ @Override @@ -486,7 +497,7 @@ public class Grid extends Composite implements /** * Sets a column as visible in the grid. - * + * * @param visible * true if the column should be displayed in the * grid @@ -515,19 +526,26 @@ public class Grid extends Composite implements } /** - * Returns the data that should be rendered into the cell. - * + * Returns the data that should be rendered into the cell. By default + * returning Strings and Widgets are supported. If the return type is a + * String then it will be treated as preformatted text. + *

+ * To support other types you will need to pass a custom renderer to the + * column via the column constructor. + * * @param row * The row object that provides the cell content. - * + * * @return The cell content */ public abstract C getValue(T row); /** - * Returns the renderer used to render the cells of this column. - * - * @return the renderer to render the cell content with + * The renderer to render the cell width. By default renders the data as + * a String or adds the widget into the cell if the column type is of + * widget type. + * + * @return The renderer to render the cell content with */ public Renderer getRenderer() { return bodyRenderer; @@ -535,7 +553,7 @@ public class Grid extends Composite implements /** * Finds the index of this column instance - * + * */ private int findIndexOfColumn() { return grid.findVisibleColumnIndex((GridColumn) this); @@ -544,7 +562,7 @@ public class Grid extends Composite implements /** * Sets the pixel width of the column. Use a negative value for the grid * to autosize column based on content and available space - * + * * @param pixels * the width in pixels or negative for auto sizing */ @@ -561,7 +579,7 @@ public class Grid extends Composite implements /** * Returns the pixel width of the column - * + * * @return pixel width of the column */ public int getWidth() { @@ -594,7 +612,7 @@ public class Grid extends Composite implements /** * Constructs an updater for updating a header / footer - * + * * @param rows * The row container * @param inverted @@ -607,17 +625,17 @@ public class Grid extends Composite implements /** * Gets the header/footer caption value - * + * * @param column * The column to get the value for. - * + * * @return The value that should be rendered for the column caption */ public abstract String getColumnValue(GridColumn column); /** * Gets the group caption value - * + * * @param group * The group for with the caption value should be returned * @return The value that should be rendered for the column caption @@ -626,34 +644,34 @@ public class Grid extends Composite implements /** * Is the row visible in the header/footer - * + * * @param row * the row to check - * + * * @return true if the row should be visible */ public abstract boolean isRowVisible(ColumnGroupRow row); /** * Should the first row be visible - * + * * @return true if the first row should be visible */ public abstract boolean firstRowIsVisible(); /** * The renderer that renders the cell - * + * * @param column * The column for which the cell should be rendered - * + * * @return renderer used for rendering */ public abstract Renderer getRenderer(GridColumn column); /** * The renderer that renders the cell for column groups - * + * * @param group * The group that should be rendered * @return renderer used for rendering @@ -780,7 +798,7 @@ public class Grid extends Composite implements /** * Creates the header updater that updates the escalator header rows from * the column and column group rows. - * + * * @return the updater that updates the data in the escalator. */ private EscalatorUpdater createHeaderUpdater() { @@ -950,7 +968,7 @@ public class Grid extends Composite implements /** * Creates the footer updater that updates the escalator footer rows from * the column and column group rows. - * + * * @return the updater that updates the data in the escalator. */ private EscalatorUpdater createFooterUpdater() { @@ -990,7 +1008,7 @@ public class Grid extends Composite implements /** * Refreshes header or footer rows on demand - * + * * @param rows * The row container * @param firstRowIsVisible @@ -1042,7 +1060,7 @@ public class Grid extends Composite implements /** * Adds a column as the last column in the grid. - * + * * @param column * the column to add */ @@ -1052,7 +1070,7 @@ public class Grid extends Composite implements /** * Inserts a column into a specific position in the grid. - * + * * @param index * the index where the column should be inserted into * @param column @@ -1161,7 +1179,7 @@ public class Grid extends Composite implements /** * Removes a column from the grid. - * + * * @param column * the column to remove */ @@ -1196,7 +1214,7 @@ public class Grid extends Composite implements /** * Returns the amount of columns in the grid. - * + * * @return The number of columns in the grid */ public int getColumnCount() { @@ -1205,7 +1223,7 @@ public class Grid extends Composite implements /** * Returns a list of columns in the grid. - * + * * @return A unmodifiable list of the columns in the grid */ public List> getColumns() { @@ -1215,7 +1233,7 @@ public class Grid extends Composite implements /** * Returns a column by its index in the grid. - * + * * @param index * the index of the column * @return The column in the given index @@ -1232,30 +1250,30 @@ public class Grid extends Composite implements /** * Set the column headers visible. - * + * *

* A column header is a single cell header on top of each column reserved * for a specific header for that column. The column header can be set by * {@link GridColumn#setHeaderCaption(String)} and column headers cannot be * merged with other column headers. *

- * + * *

* All column headers occupy the first header row of the grid. If you do not * wish to show the column headers in the grid you should hide the row by * setting visibility of the header row to false. *

- * + * *

* If you want to merge the column headers into groups you can use * {@link ColumnGroupRow}s to group columns together and give them a common * header. See {@link #addColumnGroupRow()} for details. *

- * + * *

* The header row is by default visible. *

- * + * * @param visible * true if header rows should be visible */ @@ -1269,7 +1287,7 @@ public class Grid extends Composite implements /** * Are the column headers visible - * + * * @return true if they are visible */ public boolean isColumnHeadersVisible() { @@ -1278,30 +1296,30 @@ public class Grid extends Composite implements /** * Set the column footers visible. - * + * *

* A column footer is a single cell footer below of each column reserved for * a specific footer for that column. The column footer can be set by * {@link GridColumn#setFooterCaption(String)} and column footers cannot be * merged with other column footers. *

- * + * *

* All column footers occupy the first footer row of the grid. If you do not * wish to show the column footers in the grid you should hide the row by * setting visibility of the footer row to false. *

- * + * *

* If you want to merge the column footers into groups you can use * {@link ColumnGroupRow}s to group columns together and give them a common * footer. See {@link #addColumnGroupRow()} for details. *

- * + * *

* The footer row is by default hidden. *

- * + * * @param visible * true if the footer row should be visible */ @@ -1315,9 +1333,9 @@ public class Grid extends Composite implements /** * Are the column footers visible - * + * * @return true if they are visible - * + * */ public boolean isColumnFootersVisible() { return columnFootersVisible; @@ -1325,15 +1343,15 @@ public class Grid extends Composite implements /** * Adds a new column group row to the grid. - * + * *

* Column group rows are rendered in the header and footer of the grid. * Column group rows are made up of column groups which groups together * columns for adding a common auxiliary header or footer for the columns. *

- * + * * Example usage: - * + * *
      * // Add a new column group row to the grid
      * ColumnGroupRow row = grid.addColumnGroupRow();
@@ -1347,7 +1365,7 @@ public class Grid extends Composite implements
      * // Set a common footer for "Column1" and "Column2"
      * column12.setFooter("Column 1&2");
      * 
- * + * * @return a column group row instance you can use to add column groups */ public ColumnGroupRow addColumnGroupRow() { @@ -1360,10 +1378,10 @@ public class Grid extends Composite implements /** * Adds a new column group row to the grid at a specific index. - * + * * @see #addColumnGroupRow() {@link Grid#addColumnGroupRow()} for example * usage - * + * * @param rowIndex * the index where the column group row should be added * @return a column group row instance you can use to add column groups @@ -1378,7 +1396,7 @@ public class Grid extends Composite implements /** * Removes a column group row - * + * * @param row * The row to remove */ @@ -1390,9 +1408,9 @@ public class Grid extends Composite implements /** * Get the column group rows - * + * * @return a unmodifiable list of column group rows - * + * */ public List> getColumnGroupRows() { return Collections.unmodifiableList(new ArrayList>( @@ -1401,7 +1419,7 @@ public class Grid extends Composite implements /** * Returns the column group for a row and column - * + * * @param row * The row of the column * @param column @@ -1425,7 +1443,7 @@ public class Grid extends Composite implements *

* Note: This method will change the widget's size in the browser * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}. - * + * * @see #setHeightMode(HeightMode) */ @Override @@ -1440,7 +1458,7 @@ public class Grid extends Composite implements /** * Sets the data source used by this grid. - * + * * @param dataSource * the data source to use, not null * @throws IllegalArgumentException @@ -1493,7 +1511,7 @@ public class Grid extends Composite implements *

* All columns up to and including the given column will be frozen in place * when the grid is scrolled sideways. - * + * * @param lastFrozenColumn * the rightmost column to freeze, or null to not * have any columns frozen @@ -1526,7 +1544,7 @@ public class Grid extends Composite implements * Note: Most usually, this method returns the very value set with * {@link #setLastFrozenColumn(GridColumn)}. This value, however, can be * reset to null if the column is removed from this grid. - * + * * @return the rightmost frozen column in the grid, or null if * no columns are frozen. */ @@ -1546,7 +1564,7 @@ public class Grid extends Composite implements /** * Scrolls to a certain row, using {@link ScrollDestination#ANY}. - * + * * @param rowIndex * zero-based index of the row to scroll to. * @throws IllegalArgumentException @@ -1560,7 +1578,7 @@ public class Grid extends Composite implements /** * Scrolls to a certain row, using user-specified scroll destination. - * + * * @param rowIndex * zero-based index of the row to scroll to. * @param destination @@ -1579,7 +1597,7 @@ public class Grid extends Composite implements /** * Scrolls to a certain row using only user-specified parameters. - * + * * @param rowIndex * zero-based index of the row to scroll to. * @param destination @@ -1636,7 +1654,7 @@ public class Grid extends Composite implements *

* If Grid is currently not in {@link HeightMode#ROW}, the given value is * remembered, and applied once the mode is applied. - * + * * @param rows * The height in terms of number of rows displayed in Grid's * body. If Grid doesn't contain enough rows, white space is @@ -1648,7 +1666,7 @@ public class Grid extends Composite implements * infinite} * @throws IllegalArgumentException * if {@code rows} is {@link Double#isNaN(double) NaN} - * + * * @see #setHeightMode(HeightMode) */ public void setHeightByRows(double rows) throws IllegalArgumentException { @@ -1660,7 +1678,7 @@ public class Grid extends Composite implements * {@link #getHeightMode()} is {@link HeightMode#ROW}. *

* By default, it is {@value Escalator#DEFAULT_HEIGHT_BY_ROWS}. - * + * * @return the amount of rows that should be shown in Grid's body, while in * {@link HeightMode#ROW}. * @see #setHeightByRows(double) @@ -1680,7 +1698,7 @@ public class Grid extends Composite implements * Note: If headers/footers are inserted or removed, the widget * will resize itself to still display the required amount of rows in its * body. It also takes the horizontal scrollbar into account. - * + * * @param heightMode * the mode in to which Grid should be set */ @@ -1702,7 +1720,7 @@ public class Grid extends Composite implements * Returns the current {@link HeightMode} the Grid is in. *

* Defaults to {@link HeightMode#CSS}. - * + * * @return the current HeightMode */ public HeightMode getHeightMode() { @@ -1865,7 +1883,7 @@ public class Grid extends Composite implements /** * Accesses the package private method Widget#setParent() - * + * * @param widget * The widget to access * @param parent @@ -1878,7 +1896,7 @@ public class Grid extends Composite implements /** * Sets the current selection model. - * + * * @param selectionModel * a selection model implementation. * @throws IllegalArgumentException @@ -1896,7 +1914,7 @@ public class Grid extends Composite implements /** * Gets a reference to the current selection model. - * + * * @return the currently used SelectionModel instance. */ public SelectionModel getSelectionModel() { @@ -1907,7 +1925,7 @@ public class Grid extends Composite implements * Sets current selection mode. *

* This is a shorthand method for {@link Grid#setSelectionModel}. - * + * * @param mode * a selection mode value * @see {@link SelectionMode}. @@ -1919,7 +1937,7 @@ public class Grid extends Composite implements /** * Test if a row is selected. - * + * * @param row * a row object * @return true, if the current selection model considers the provided row @@ -1935,7 +1953,7 @@ public class Grid extends Composite implements * Only selection models implementing {@link SelectionModel.Single} and * {@link SelectionModel.Multi} are supported; for anything else, an * exception will be thrown. - * + * * @param row * a row object * @return true iff the current selection changed @@ -1960,7 +1978,7 @@ public class Grid extends Composite implements * Only selection models implementing {@link SelectionModel.Single} and * {@link SelectionModel.Multi} are supported; for anything else, an * exception will be thrown. - * + * * @param row * a row object * @return true iff the current selection changed @@ -1985,7 +2003,7 @@ public class Grid extends Composite implements * Only selection models implementing {@link SelectionModel.Single} are * valid for this method; for anything else, use the * {@link Grid#getSelectedRows()} method. - * + * * @return a selected row reference, or null, if no row is selected * @throws IllegalStateException * if the current selection model is not an instance of @@ -2002,7 +2020,7 @@ public class Grid extends Composite implements /** * Gets currently selected rows from the current selection model. - * + * * @return a non-null collection containing all currently selected rows. */ public Collection getSelectedRows() { @@ -2015,4 +2033,91 @@ public class Grid extends Composite implements return addHandler(handler, SelectionChangeEvent.getType()); } + /** + * Sets the current sort order using the fluid Sort API. Read the + * documentation for {@link Sort} for more information. + * + * @param s + * a sort instance + */ + public void sort(Sort s) { + setSortOrder(s.build()); + } + + /** + * Sorts the Grid data in ascending order along one column. + * + * @param column + * a grid column reference + */ + public void sort(GridColumn column) { + sort(column, SortDirection.ASCENDING); + } + + /** + * Sorts the Grid data along one column. + * + * @param column + * a grid column reference + * @param direction + * a sort direction value + */ + public void sort(GridColumn column, SortDirection direction) { + sort(Sort.by(column, direction)); + } + + /** + * Sets the sort order to use. Setting this causes the Grid to re-sort + * itself. + * + * @param order + * a sort order list. If set to null, the sort order is cleared. + */ + public void setSortOrder(List order) { + sortOrder.clear(); + if (order != null) { + sortOrder.addAll(order); + } + sort(); + } + + /** + * Get a copy of the current sort order array. + * + * @return a copy of the current sort order array + */ + public List getSortOrder() { + return Collections.unmodifiableList(sortOrder); + } + + /** + * Register a GWT event handler for a sorting event. This handler gets + * called whenever this Grid needs its data source to provide data sorted in + * a specific order. + * + * @param handler + * a sort event handler + * @return the registration for the event + */ + public HandlerRegistration addSortHandler(SortEventHandler handler) { + return addHandler(handler, SortEvent.getType()); + } + + /** + * Apply sorting to data source. + */ + private void sort() { + fireEvent(new SortEvent(this, + Collections.unmodifiableList(sortOrder))); + } + + /** + * Missing getDataSource method. TODO: remove this and other duplicates + * after The Merge + * + * @return a DataSource reference + */ + public DataSource getDataSource() { + return dataSource; + } } diff --git a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java index 94c32bfb33..97b358a299 100644 --- a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java +++ b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * 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 @@ -18,6 +18,8 @@ package com.vaadin.client.ui.grid.datasources; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -30,23 +32,23 @@ import com.vaadin.shared.util.SharedUtil; * A simple list based on an in-memory data source for simply adding a list of * row pojos to the grid. Based on a wrapped list instance which supports adding * and removing of items. - * + * *

* Usage: - * + * *

  * ListDataSource<Integer> ds = new ListDataSource<Integer>(1, 2, 3, 4);
- * 
+ *
  * // Add item to the data source
  * ds.asList().add(5);
- * 
+ *
  * // Remove item from the data source
  * ds.asList().remove(3);
- * 
+ *
  * // Add multiple items
  * ds.asList().addAll(Arrays.asList(5, 6, 7));
  * 
- * + * * @since 7.4 * @author Vaadin Ltd */ @@ -342,8 +344,8 @@ public class ListDataSource implements DataSource { * data source after the data source has been constructed. To add or remove * items to the data source after it has been constructed use * {@link ListDataSource#asList()}. - * - * + * + * * @param datasource * The list to use for providing the data to the grid */ @@ -359,7 +361,7 @@ public class ListDataSource implements DataSource { * Constructs a data source with a set of rows. You can dynamically add and * remove rows from the data source via the list you get from * {@link ListDataSource#asList()} - * + * * @param rows * The rows to initially add to the data source */ @@ -401,7 +403,7 @@ public class ListDataSource implements DataSource { *

* Note: The list is not the same list as passed into the data source via * the constructor. - * + * * @return Returns a list implementation that wraps the real list that backs * the data source and provides events for the data source * listeners. @@ -416,4 +418,18 @@ public class ListDataSource implements DataSource { + row; return new RowHandleImpl(row); } + + /** + * Sort entire container according to a {@link Comparator}. + * + * @param comparator + * a comparator object, which compares two data source entries + * (beans/pojos) + */ + public void sort(Comparator comparator) { + Collections.sort(ds, comparator); + if (changeHandler != null) { + changeHandler.dataUpdated(0, ds.size()); + } + } } diff --git a/client/src/com/vaadin/client/ui/grid/sort/Sort.java b/client/src/com/vaadin/client/ui/grid/sort/Sort.java new file mode 100644 index 0000000000..fdf3c64d60 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/sort/Sort.java @@ -0,0 +1,155 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.sort; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.client.ui.grid.GridColumn; +import com.vaadin.shared.ui.grid.SortDirection; + +/** + * Fluid Sort descriptor object. + * + * @since 7.4 + * @author Vaadin Ltd + * @param T + * grid data type + */ +public class Sort { + + private final Sort previous; + private final SortOrder order; + private final int count; + + /** + * Basic constructor, used by the {@link #by(GridColumn)} and + * {@link #by(GridColumn, SortDirection)} methods. + * + * @param column + * a grid column + * @param direction + * a sort direction + */ + private Sort(GridColumn column, SortDirection direction) { + previous = null; + count = 1; + order = new SortOrder(column, direction); + } + + /** + * Extension constructor. Performs object equality checks on all previous + * Sort objects in the chain to make sure that the column being passed in + * isn't already used earlier (which would indicate a bug). If the column + * has been used before, this constructor throws an + * {@link IllegalStateException}. + * + * @param previous + * the sort instance that the new sort instance is to extend + * @param column + * a (previously unused) grid column reference + * @param direction + * a sort direction + */ + private Sort(Sort previous, GridColumn column, SortDirection direction) { + this.previous = previous; + count = previous.count + 1; + order = new SortOrder(column, direction); + + Sort s = previous; + while (s != null) { + if (s.order.getColumn() == column) { + throw new IllegalStateException( + "Can not sort along the same column twice"); + } + s = s.previous; + } + } + + /** + * Start building a Sort order by sorting a provided column in ascending + * order. + * + * @param column + * a grid column object reference + * @return a sort instance, typed to the grid data type + */ + public static Sort by(GridColumn column) { + return by(column, SortDirection.ASCENDING); + } + + /** + * Start building a Sort order by sorting a provided column. + * + * @param column + * a grid column object reference + * @param direction + * indicator of sort direction - either ascending or descending + * @return a sort instance, typed to the grid data type + */ + public static Sort by(GridColumn column, SortDirection direction) { + return new Sort(column, direction); + } + + /** + * Continue building a Sort order. The provided column is sorted in + * ascending order if the previously added columns have been evaluated as + * equals. + * + * @param column + * a grid column object reference + * @return a sort instance, typed to the grid data type + */ + public Sort then(GridColumn column) { + return then(column, SortDirection.ASCENDING); + } + + /** + * Continue building a Sort order. The provided column is sorted in + * specified order if the previously added columns have been evaluated as + * equals. + * + * @param column + * a grid column object reference + * @param direction + * indicator of sort direction - either ascending or descending + * @return a sort instance, typed to the grid data type + */ + public Sort then(GridColumn column, SortDirection direction) { + return new Sort(this, column, direction); + } + + /** + * Build a sort order list. This method is called internally by Grid when + * calling {@link com.vaadin.client.ui.grid.Grid#sort(Sort)}, but can also + * be called manually to create a SortOrder list, which can also be provided + * directly to Grid. + * + * @return a sort order list. + */ + public List build() { + + List order = new ArrayList(count); + + Sort s = this; + for (int i = count - 1; i >= 0; --i) { + order.add(0, s.order); + s = s.previous; + } + + return order; + } +} diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java b/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java new file mode 100644 index 0000000000..d39cdfc4f2 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.sort; + +import java.util.List; + +import com.google.gwt.event.shared.GwtEvent; +import com.vaadin.client.data.DataSource; +import com.vaadin.client.ui.grid.Grid; + +/** + * A sort event, fired by the Grid when it needs its data source to provide data + * sorted in a specific manner. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class SortEvent extends GwtEvent> { + + private static final Type> TYPE = new Type>(); + + private final Grid grid; + private final List order; + + /** + * Creates a new Sort Event. All provided parameters are final, and passed + * on as-is. + * + * @param grid + * a grid reference + * @param datasource + * a reference to the grid's data source + * @param order + * an array dictating the desired sort order of the data source + */ + public SortEvent(Grid grid, List order) { + this.grid = grid; + this.order = order; + } + + @Override + public Type> getAssociatedType() { + return TYPE; + } + + /** + * Static access to the GWT event type identifier associated with this Event + * class + * + * @return a type object, uniquely describing this event type. + */ + public static Type> getType() { + return TYPE; + } + + /** + * Get access to the Grid that fired this event + * + * @return the grid instance + */ + @Override + public Grid getSource() { + return grid; + } + + /** + * Get access to the Grid that fired this event + * + * @return the grid instance + */ + public Grid getGrid() { + return grid; + } + + /** + * Access the data source of the Grid that fired this event + * + * @return a data source instance + */ + public DataSource getDataSource() { + return grid.getDataSource(); + } + + /** + * Get the sort ordering that is to be applied to the Grid + * + * @return a list of sort order objects + */ + public List getOrder() { + return order; + } + + @SuppressWarnings("unchecked") + @Override + protected void dispatch(SortEventHandler handler) { + ((SortEventHandler) handler).sort(this); + } + +} diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java b/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java new file mode 100644 index 0000000000..8895b43631 --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.sort; + +import com.google.gwt.event.shared.EventHandler; + +/** + * Handler for a Grid sort event, called when the Grid needs its data source to + * provide data sorted in a specific manner. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public interface SortEventHandler extends EventHandler { + + /** + * Handle sorting of the Grid. This method is called when a re-sorting of + * the Grid's data is requested. + * + * @param event + * the sort event + */ + public void sort(SortEvent event); + +} diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java b/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java new file mode 100644 index 0000000000..bd76124d0c --- /dev/null +++ b/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.client.ui.grid.sort; + +import com.vaadin.client.ui.grid.GridColumn; +import com.vaadin.shared.ui.grid.SortDirection; + +/** + * Sort order descriptor. Contains column and direction references. + * + * @since 7.4 + * @author Vaadin Ltd + * @param T + * grid data type + */ +public class SortOrder { + + private final GridColumn column; + private final SortDirection direction; + + /** + * Create a sort order descriptor. + * + * @param column + * a grid column descriptor object + * @param direction + * a sorting direction value (ascending or descending) + */ + public SortOrder(GridColumn column, SortDirection direction) { + if (column == null) { + throw new IllegalArgumentException( + "Grid column reference can not be null!"); + } + if (direction == null) { + throw new IllegalArgumentException( + "Direction value can not be null!"); + } + this.column = column; + this.direction = direction; + } + + /** + * Returns the {@link GridColumn} reference given in the constructor. + * + * @return a grid column reference + */ + public GridColumn getColumn() { + return column; + } + + /** + * Returns the {@link SortDirection} value given in the constructor. + * + * @return a sort direction value + */ + public SortDirection getDirection() { + return direction; + } + +} diff --git a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java index 5c5e88bf69..823eb224ea 100644 --- a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java +++ b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2013 Vaadin Ltd. - * + * * 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 @@ -16,8 +16,10 @@ package com.vaadin.client.ui.grid; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.util.Arrays; +import java.util.Comparator; import org.easymock.EasyMock; import org.junit.Test; @@ -26,7 +28,7 @@ import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.ui.grid.datasources.ListDataSource; /** - * + * * @since 7.2 * @author Vaadin Ltd */ @@ -175,4 +177,21 @@ public class ListDataSourceTest { ds.asList().iterator().remove(); } + @Test + public void sortColumn() { + ListDataSource ds = new ListDataSource(3, 4, 2, 3, 1); + + // TODO Should be simplified to sort(). No point in providing these + // trivial comparators. + ds.sort(new Comparator() { + @Override + public int compare(Integer o1, Integer o2) { + return o1.compareTo(o2); + } + }); + + assertTrue(Arrays.equals(ds.asList().toArray(), new Integer[] { 1, 2, + 3, 3, 4 })); + } + } diff --git a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java new file mode 100644 index 0000000000..3a1828992e --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.shared.ui.grid; + +/** + * Describes sorting direction for a Grid column + * + * @since 7.4 + * @author Vaadin Ltd + */ +public enum SortDirection { + + /** + * Ascending (e.g. A-Z, 1..9) sort order + */ + ASCENDING, + + /** + * Descending (e.g. Z-A, 9..1) sort order + */ + DESCENDING +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java index 15bd323e08..91a4e19886 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * 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 @@ -24,6 +24,7 @@ import org.openqa.selenium.WebElement; import com.vaadin.testbench.By; import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elements.LabelElement; import com.vaadin.testbench.elements.NativeButtonElement; import com.vaadin.testbench.elements.NativeSelectElement; import com.vaadin.testbench.elements.ServerClass; @@ -33,7 +34,7 @@ import com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers; /** * Tests Grid client side renderers - * + * * @since 7.4 * @author Vaadin Ltd */ @@ -161,6 +162,21 @@ public class GridClientRenderers extends MultiBrowserTest { backgroundColor); } + @Test + public void testSortingEvent() throws Exception { + openTestURL(); + + $(NativeButtonElement.class).caption("Trigger sorting").first().click(); + sleep(1000); + + String consoleText = $(LabelElement.class).id("testDebugConsole") + .getText(); + + assertTrue("Console text as expected", + consoleText.contains("Columns: 1, order: Column 1: ASCENDING")); + + } + private GridElement getGrid() { return $(MyClientGridElement.class).first(); } diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java index 24e79d775f..c07f6192b2 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererConnector.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * 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,6 +19,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.Timer; @@ -39,6 +41,10 @@ import com.vaadin.client.ui.grid.renderers.HtmlRenderer; import com.vaadin.client.ui.grid.renderers.NumberRenderer; import com.vaadin.client.ui.grid.renderers.TextRenderer; import com.vaadin.client.ui.grid.renderers.WidgetRenderer; +import com.vaadin.client.ui.grid.sort.Sort; +import com.vaadin.client.ui.grid.sort.SortEvent; +import com.vaadin.client.ui.grid.sort.SortEventHandler; +import com.vaadin.client.ui.grid.sort.SortOrder; import com.vaadin.shared.ui.Connect; import com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers; @@ -109,7 +115,7 @@ public class GridClientColumnRendererConnector extends @Override protected void init() { Grid grid = getWidget(); - grid.setColumnHeadersVisible(false); + // grid.setColumnHeadersVisible(false); // Generated some column data List columnData = new ArrayList(); @@ -127,7 +133,25 @@ public class GridClientColumnRendererConnector extends } // Add a column to display the data in - grid.addColumn(createColumnWithRenderer(Renderers.TEXT_RENDERER)); + GridColumn c = createColumnWithRenderer(Renderers.TEXT_RENDERER); + c.setHeaderCaption("Column 1"); + grid.addColumn(c); + + // Add method for testing sort event firing + grid.addSortHandler(new SortEventHandler() { + @Override + public void sort(SortEvent event) { + Element console = Document.get().getElementById( + "testDebugConsole"); + String text = "Client-side sort event received
" + + "Columns: " + event.getOrder().size() + ", order: "; + for (SortOrder order : event.getOrder()) { + text += order.getColumn().getHeaderCaption() + ": " + + order.getDirection().toString(); + } + console.setInnerHTML(text); + } + }); // Handle RPC calls registerRpc(GridClientColumnRendererRpc.class, @@ -160,6 +184,11 @@ public class GridClientColumnRendererConnector extends // Re-attach parent.add(getWidget()); } + + @Override + public void triggerClientSorting() { + getWidget().sort(Sort.by(getWidget().getColumn(0))); + } }); } diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererRpc.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererRpc.java index d156bf9b88..ade239466e 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererRpc.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridClientColumnRendererRpc.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * 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 @@ -22,7 +22,7 @@ public interface GridClientColumnRendererRpc extends ClientRpc { /** * Adds a new column with a specific renderer to the grid - * + * */ void addColumn(Renderers renderer); @@ -30,4 +30,9 @@ public interface GridClientColumnRendererRpc extends ClientRpc { * Detaches and attaches the client side Grid */ void detachAttach(); + + /** + * Used for client-side sorting API test + */ + void triggerClientSorting(); } diff --git a/uitest/src/com/vaadin/tests/widgetset/server/grid/GridClientColumnRenderers.java b/uitest/src/com/vaadin/tests/widgetset/server/grid/GridClientColumnRenderers.java index e968f13ff5..d41370cc02 100644 --- a/uitest/src/com/vaadin/tests/widgetset/server/grid/GridClientColumnRenderers.java +++ b/uitest/src/com/vaadin/tests/widgetset/server/grid/GridClientColumnRenderers.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * 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,6 +19,7 @@ import java.util.Arrays; import com.vaadin.annotations.Widgetset; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.tests.widgetset.TestingWidgetSet; import com.vaadin.tests.widgetset.client.grid.GridClientColumnRendererConnector.Renderers; import com.vaadin.tests.widgetset.client.grid.GridClientColumnRendererRpc; @@ -26,6 +27,7 @@ import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.CssLayout; +import com.vaadin.ui.Label; import com.vaadin.ui.NativeButton; import com.vaadin.ui.NativeSelect; import com.vaadin.ui.UI; @@ -56,6 +58,13 @@ public class GridClientColumnRenderers extends UI { public void detachAttach() { rpc().detachAttach(); } + + /** + * @since + */ + public void triggerClientSorting() { + rpc().triggerClientSorting(); + } } @Override @@ -94,5 +103,19 @@ public class GridClientColumnRenderers extends UI { } }); controls.addComponent(detachAttachBtn); + + NativeButton sortButton = new NativeButton("Trigger sorting"); + sortButton.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + controller.triggerClientSorting(); + } + }); + controls.addComponent(sortButton); + + Label console = new Label(); + console.setContentMode(ContentMode.HTML); + console.setId("testDebugConsole"); + content.addComponent(console); } } -- cgit v1.2.3 From 8f71585df37d3f0e2e55ceac9e6abbaf2806855e Mon Sep 17 00:00:00 2001 From: John Ahlroos Date: Wed, 25 Jun 2014 11:16:46 +0300 Subject: Allow turning sorting on/off for columns #13334 Change-Id: I161dfd2cd534cdf4fc13467811f77be7d8cbc339 --- client/src/com/vaadin/client/ui/grid/Grid.java | 105 ++++++++++++++------- .../com/vaadin/client/ui/grid/GridConnector.java | 1 + server/src/com/vaadin/ui/components/grid/Grid.java | 8 ++ .../com/vaadin/ui/components/grid/GridColumn.java | 18 ++++ .../com/vaadin/shared/ui/grid/GridColumnState.java | 5 + .../tests/components/grid/GridBasicFeatures.java | 15 +++ 6 files changed, 119 insertions(+), 33 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 299aa739b3..a1c4b4308a 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -296,9 +296,6 @@ public class Grid extends Composite implements * Renderer for columns which are sortable * * FIXME Currently assumes multisorting - * - * FIXME Currently all columns are assumed sortable - * */ private class SortableColumnHeaderRenderer extends ComplexRenderer { @@ -331,26 +328,36 @@ public class Grid extends Composite implements if (grid != null) { SortOrder sortingOrder = getSortingOrder(); Element cellElement = cell.getElement(); - if (sortingOrder != null) { - int sortIndex = grid.getSortOrder().indexOf( - sortingOrder); - if (sortIndex > -1 && grid.getSortOrder().size() > 1) { - // Show sort order indicator if column is sorted and - // other sorted columns also exists. - cellElement.setAttribute("sort-order", - String.valueOf(sortIndex + 1)); - + if (grid.getColumn(cell.getColumn()).isSortable()) { + if (sortingOrder != null) { + int sortIndex = grid.getSortOrder().indexOf( + sortingOrder); + if (sortIndex > -1 + && grid.getSortOrder().size() > 1) { + // Show sort order indicator if column is sorted + // and other sorted columns also exists. + cellElement.setAttribute("sort-order", + String.valueOf(sortIndex + 1)); + + } else { + cellElement.removeAttribute("sort-order"); + } } else { - cellElement.removeAttribute("sort-order"); + cleanup(cell); } } else { - cellElement.removeAttribute("sort-order"); - cellElement.removeClassName("sort-desc"); - cellElement.removeClassName("sort-asc"); + cleanup(cell); } } } + private void cleanup(FlyweightCell cell) { + Element cellElement = cell.getElement(); + cellElement.removeAttribute("sort-order"); + cellElement.removeClassName("sort-desc"); + cellElement.removeClassName("sort-asc"); + } + @Override public Collection getConsumedEvents() { return Arrays.asList(BrowserEvents.MOUSEDOWN); @@ -358,25 +365,31 @@ public class Grid extends Composite implements @Override public void onBrowserEvent(Cell cell, NativeEvent event) { - if (BrowserEvents.MOUSEDOWN.equals(event.getType())) { - event.preventDefault(); - SortOrder sortingOrder = getSortingOrder(); - if (sortingOrder == null) { - /* - * No previous sorting, sort Ascending - */ - sort(cell, SortDirection.ASCENDING, event.getShiftKey()); + // Handle sorting events if column is sortable + if (grid.getColumn(cell.getColumn()).isSortable()) { + if (BrowserEvents.MOUSEDOWN.equals(event.getType())) { + event.preventDefault(); - } else { - // Toggle sorting - SortDirection direction = sortingOrder.getDirection(); - if (direction == SortDirection.ASCENDING) { - sort(cell, SortDirection.DESCENDING, - event.getShiftKey()); - } else { + SortOrder sortingOrder = getSortingOrder(); + if (sortingOrder == null) { + /* + * No previous sorting, sort Ascending + */ sort(cell, SortDirection.ASCENDING, event.getShiftKey()); + + } else { + // Toggle sorting + SortDirection direction = sortingOrder + .getDirection(); + if (direction == SortDirection.ASCENDING) { + sort(cell, SortDirection.DESCENDING, + event.getShiftKey()); + } else { + sort(cell, SortDirection.ASCENDING, + event.getShiftKey()); + } } } } @@ -439,7 +452,6 @@ public class Grid extends Composite implements } return null; } - } /** @@ -475,7 +487,7 @@ public class Grid extends Composite implements /** * Renderer for rendering the header cell value into the cell */ - private SortableColumnHeaderRenderer headerRenderer = new SortableColumnHeaderRenderer( + private Renderer headerRenderer = new SortableColumnHeaderRenderer( new TextRenderer()); /** @@ -483,6 +495,8 @@ public class Grid extends Composite implements */ private Renderer footerRenderer = new TextRenderer(); + private boolean sortable = false; + /** * Constructs a new column with a custom renderer. * @@ -747,6 +761,31 @@ public class Grid extends Composite implements return conf.getColumnWidth(index); } } + + /** + * Enables sort indicators for the grid. + *

+ * Note:The API can still sort the column even if this is set to + * false. + * + * @param sortable + * true when column sort indicators are visible. + */ + public void setSortable(boolean sortable) { + if (this.sortable != sortable) { + this.sortable = sortable; + grid.refreshHeader(); + } + } + + /** + * Are sort indicators shown for the column. + * + * @return true if the column is sortable + */ + public boolean isSortable() { + return sortable; + } } /** diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 95b493b451..0bfcf8ffcd 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -272,6 +272,7 @@ public class GridConnector extends AbstractComponentConnector { column.setHeaderCaption(state.header); column.setFooterCaption(state.footer); column.setWidth(state.width); + column.setSortable(state.sortable); } /** diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 0d7e799978..1ebf227330 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -30,6 +30,7 @@ import com.vaadin.data.Container; import com.vaadin.data.Container.PropertySetChangeEvent; import com.vaadin.data.Container.PropertySetChangeListener; import com.vaadin.data.Container.PropertySetChangeNotifier; +import com.vaadin.data.Container.Sortable; import com.vaadin.data.RpcDataProviderExtension; import com.vaadin.server.KeyMapper; import com.vaadin.shared.ui.grid.ColumnGroupRowState; @@ -253,6 +254,13 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { if (!columns.containsKey(propertyId)) { GridColumn column = appendColumn(propertyId); + // Initial sorting is defined by container + if (datasource instanceof Sortable) { + column.setSortable(((Sortable) datasource) + .getSortableContainerPropertyIds().contains( + propertyId)); + } + // Add by default property id as column header column.setHeaderCaption(String.valueOf(propertyId)); } diff --git a/server/src/com/vaadin/ui/components/grid/GridColumn.java b/server/src/com/vaadin/ui/components/grid/GridColumn.java index fd504fdae3..634526ea7c 100644 --- a/server/src/com/vaadin/ui/components/grid/GridColumn.java +++ b/server/src/com/vaadin/ui/components/grid/GridColumn.java @@ -395,4 +395,22 @@ public class GridColumn implements Serializable { grid.getPropertyIdByColumnId(state.id)); } + /** + * Should sorting controls be available for the column + * + * @param sortable + * true if the sorting controls should be visible. + */ + public void setSortable(boolean sortable) { + checkColumnIsAttached(); + state.sortable = sortable; + grid.markAsDirty(); + } + + /** + * Are the sorting controls visible in the column header + */ + public boolean isSortable() { + return state.sortable; + } } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java index 581d4d4365..d1df08c294 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java @@ -55,4 +55,9 @@ public class GridColumnState implements Serializable { public int width = 100; public Connector rendererConnector; + + /** + * Are sorting indicators shown for a column. Default is false. + */ + public boolean sortable = false; } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java index 1dc500202e..06fe088dee 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java @@ -192,6 +192,21 @@ public class GridBasicFeatures extends AbstractComponentTest { } }, null, c); + createBooleanAction("Sortable", getColumnProperty(c), true, + new Command() { + + @Override + public void execute(Grid grid, Boolean value, + Object columnIndex) { + Object propertyId = (new ArrayList(grid + .getContainerDatasource() + .getContainerPropertyIds()) + .get((Integer) columnIndex)); + GridColumn column = grid.getColumn(propertyId); + column.setSortable(value); + } + }, c); + createCategory("Column" + c + " Width", getColumnProperty(c)); createClickAction("Auto", "Column" + c + " Width", -- cgit v1.2.3 From c4a1ee8a4fbc3fafaabea695d8aaf40aecbeba48 Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Tue, 10 Jun 2014 21:50:51 +0300 Subject: Send selection between server and client (#13334) Change-Id: I75174af63092fca72d9aa63ccf3c06a77f42c4f6 --- WebContent/VAADIN/themes/base/grid/grid.scss | 6 +- .../client/data/AbstractRemoteDataSource.java | 2 +- .../vaadin/client/data/RpcDataSourceConnector.java | 26 +- .../src/com/vaadin/client/ui/grid/Escalator.java | 3 + client/src/com/vaadin/client/ui/grid/Grid.java | 44 +++- .../com/vaadin/client/ui/grid/GridConnector.java | 91 ++++++- .../ui/grid/selection/MultiSelectionRenderer.java | 26 +- .../client/ui/grid/selection/SelectionModel.java | 34 +-- .../ui/grid/selection/SelectionModelMulti.java | 50 +++- .../ui/grid/selection/SelectionModelNone.java | 4 +- .../ui/grid/selection/SelectionModelSingle.java | 39 +-- .../com/vaadin/data/RpcDataProviderExtension.java | 278 ++++++++++++++++++--- server/src/com/vaadin/ui/components/grid/Grid.java | 127 +++++++++- .../grid/selection/SelectionChangeEvent.java | 10 +- .../component/grid/DataProviderExtension.java | 88 +++++++ .../com/vaadin/shared/data/DataProviderRpc.java | 3 +- .../com/vaadin/shared/ui/grid/GridServerRpc.java | 30 +++ .../src/com/vaadin/shared/ui/grid/GridState.java | 14 +- .../tests/components/grid/GridBasicFeatures.java | 14 ++ .../components/grid/GridBasicFeaturesTest.java | 78 ++++++ .../tests/components/grid/GridClientRenderers.java | 9 + 21 files changed, 850 insertions(+), 126 deletions(-) create mode 100644 server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java create mode 100644 shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java (limited to 'shared/src/com') diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss index 6a050405cb..d1875a7ab3 100644 --- a/WebContent/VAADIN/themes/base/grid/grid.scss +++ b/WebContent/VAADIN/themes/base/grid/grid.scss @@ -14,4 +14,8 @@ } } -} \ No newline at end of file + + .#{$primaryStyleName}-row-selected > td { + background: lightblue; + } +} diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index 2395dc848c..d6a609a3c8 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -42,7 +42,7 @@ import com.vaadin.shared.ui.grid.Range; */ public abstract class AbstractRemoteDataSource implements DataSource { - private class RowHandleImpl extends RowHandle { + protected class RowHandleImpl extends RowHandle { private T row; private final Object key; diff --git a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java index 2b9bf5c90e..3761ea92df 100644 --- a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; +import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONValue; import com.vaadin.client.ServerConnector; import com.vaadin.client.extensions.AbstractExtensionConnector; @@ -29,6 +30,7 @@ import com.vaadin.shared.data.DataProviderRpc; import com.vaadin.shared.data.DataProviderState; import com.vaadin.shared.data.DataRequestRpc; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.Range; /** @@ -43,7 +45,8 @@ import com.vaadin.shared.ui.grid.Range; @Connect(com.vaadin.data.RpcDataProviderExtension.class) public class RpcDataSourceConnector extends AbstractExtensionConnector { - private final AbstractRemoteDataSource dataSource = new AbstractRemoteDataSource() { + public class RpcDataSource extends AbstractRemoteDataSource { + @Override protected void requestRows(int firstRowIndex, int numberOfRows) { Range cached = getCachedRange(); @@ -54,18 +57,25 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { @Override public Object getRowKey(JSONObject row) { - /* - * FIXME will be properly implemented by another patch (Henrik Paul: - * 16.6.2014) - */ - return row; + JSONString string = row.get(GridState.JSONKEY_ROWKEY).isString(); + if (string != null) { + return string.stringValue(); + } else { + return null; + } + } + + public RowHandle getHandleByKey(Object key) { + return new RowHandleImpl(null, key); } - }; + } + + private final RpcDataSource dataSource = new RpcDataSource(); @Override protected void extend(ServerConnector target) { dataSource.setEstimatedSize(getState().containerSize); - ((GridConnector) target).getWidget().setDataSource(dataSource); + ((GridConnector) target).setDataSource(dataSource); registerRpc(DataProviderRpc.class, new DataProviderRpc() { @Override diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java index 8a1f6f5842..c8feb6d18e 100644 --- a/client/src/com/vaadin/client/ui/grid/Escalator.java +++ b/client/src/com/vaadin/client/ui/grid/Escalator.java @@ -2678,6 +2678,9 @@ public class Escalator extends Widget { @Override protected void paintRemoveRows(final int index, final int numberOfRows) { + if (numberOfRows == 0) { + return; + } final Range viewportRange = Range.withLength( getLogicalRowIndex(visualRowOrder.getFirst()), diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index b5461e4a3b..9a75b37c42 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -48,7 +48,6 @@ import com.vaadin.client.ui.grid.renderers.ComplexRenderer; import com.vaadin.client.ui.grid.renderers.TextRenderer; import com.vaadin.client.ui.grid.renderers.WidgetRenderer; import com.vaadin.client.ui.grid.selection.HasSelectionChangeHandlers; -import com.vaadin.client.ui.grid.selection.MultiSelectionRenderer; import com.vaadin.client.ui.grid.selection.SelectionChangeEvent; import com.vaadin.client.ui.grid.selection.SelectionChangeHandler; import com.vaadin.client.ui.grid.selection.SelectionModel; @@ -1061,7 +1060,7 @@ public class Grid extends Composite implements refreshHeader(); refreshFooter(); - selectionModel = SelectionMode.SINGLE.createModel(); + setSelectionMode(SelectionMode.SINGLE); escalator .addRowVisibilityChangeHandler(new RowVisibilityChangeHandler() { @@ -1075,6 +1074,16 @@ public class Grid extends Composite implements } } }); + + // Default action on SelectionChangeEvents. Refresh the body so changed + // become visible. + addSelectionChangeHandler(new SelectionChangeHandler() { + + @Override + public void onSelectionChange(SelectionChangeEvent event) { + refreshBody(); + } + }); } @Override @@ -1340,6 +1349,13 @@ public class Grid extends Composite implements true); } + /** + * Refreshes all body rows + */ + private void refreshBody() { + escalator.getBody().refreshRows(0, escalator.getBody().getRowCount()); + } + /** * Refreshes all footer rows */ @@ -1796,6 +1812,15 @@ public class Grid extends Composite implements } + /** + * Gets the {@Link DataSource} for this Grid. + * + * @return the data source used by this grid + */ + public DataSource getDataSource() { + return dataSource; + } + /** * Sets the rightmost frozen column in the grid. *

@@ -2177,7 +2202,7 @@ public class Grid extends Composite implements /* TODO remove before final */ public void setSelectionCheckboxes(boolean set) { if (set) { - setSelectColumnRenderer(new MultiSelectionRenderer(this)); + setSelectColumnRenderer(selectionModel.getSelectionColumnRenderer()); } else { setSelectColumnRenderer(null); } @@ -2198,6 +2223,8 @@ public class Grid extends Composite implements /** * Sets the current selection model. + *

+ * This function will call {@link SelectionModel#setGrid(Grid)}. * * @param selectionModel * a selection model implementation. @@ -2211,6 +2238,7 @@ public class Grid extends Composite implements } this.selectionModel = selectionModel; + selectionModel.setGrid(this); } @@ -2412,14 +2440,4 @@ public class Grid extends Composite implements fireEvent(new SortEvent(this, Collections.unmodifiableList(sortOrder))); } - - /** - * Missing getDataSource method. TODO: remove this and other duplicates - * after The Merge - * - * @return a DataSource reference - */ - public DataSource getDataSource() { - return dataSource; - } } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 0bfcf8ffcd..3b1ecb44d8 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -17,9 +17,11 @@ package com.vaadin.client.ui.grid; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -28,13 +30,18 @@ import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONValue; import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; +import com.vaadin.client.ui.grid.selection.SelectionChangeEvent; +import com.vaadin.client.ui.grid.selection.SelectionChangeHandler; +import com.vaadin.client.ui.grid.selection.SelectionModelMulti; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.ColumnGroupState; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.ScrollDestination; @@ -53,7 +60,66 @@ import com.vaadin.shared.ui.grid.ScrollDestination; public class GridConnector extends AbstractComponentConnector { /** - * Custom implementation of the custom grid column using a String[] to + * Hacked SelectionModelMulti to make selection communication work for now. + */ + private class RowKeyBasedMultiSelection extends + SelectionModelMulti { + + private final LinkedHashSet selectedKeys = new LinkedHashSet(); + + public List getSelectedKeys() { + List keys = new ArrayList(); + keys.addAll(selectedKeys); + return keys; + } + + public void updateFromState() { + boolean changed = false; + Set stateKeys = new LinkedHashSet(); + stateKeys.addAll(getState().selectedKeys); + for (String key : stateKeys) { + if (!selectedKeys.contains(key)) { + changed = true; + selectByHandle(dataSource.getHandleByKey(key)); + } + } + for (String key : selectedKeys) { + changed = true; + if (!stateKeys.contains(key)) { + deselectByHandle(dataSource.getHandleByKey(key)); + } + } + selectedKeys.clear(); + selectedKeys.addAll(stateKeys); + + if (changed) { + // At least for now there's no way to send the selected and/or + // deselected row data. Some data is only stored as keys + getWidget().fireEvent( + new SelectionChangeEvent(getWidget(), + (List) null, null)); + } + } + + @Override + public boolean select(Collection rows) { + for (JSONObject row : rows) { + selectedKeys.add((String) dataSource.getRowKey(row)); + } + return super.select(rows); + } + + @Override + public boolean deselect(Collection rows) { + for (JSONObject row : rows) { + selectedKeys.remove(dataSource.getRowKey(row)); + } + return super.deselect(rows); + } + } + + /** + * Custom implementation of the custom grid column using a JSONObject to * represent the cell value and String as a column type. */ private class CustomGridColumn extends GridColumn { @@ -107,6 +173,8 @@ public class GridConnector extends AbstractComponentConnector { * Maps a generated column id to a grid column instance */ private Map columnIdToColumn = new HashMap(); + private final RowKeyBasedMultiSelection selectionModel = new RowKeyBasedMultiSelection(); + private RpcDataSource dataSource; @Override @SuppressWarnings("unchecked") @@ -139,6 +207,18 @@ public class GridConnector extends AbstractComponentConnector { getWidget().scrollToRow(row, destination); } }); + + getWidget().setSelectionModel(selectionModel); + + getWidget().addSelectionChangeHandler(new SelectionChangeHandler() { + @Override + public void onSelectionChange(SelectionChangeEvent event) { + // TODO change this to diff based. (henrik paul 24.6.2014) + getRpcProxy(GridServerRpc.class).selectionChange( + selectionModel.getSelectedKeys()); + } + }); + } @Override @@ -211,6 +291,10 @@ public class GridConnector extends AbstractComponentConnector { if (stateChangeEvent.hasPropertyChanged("heightMode")) { getWidget().setHeightMode(getState().heightMode); } + + if (stateChangeEvent.hasPropertyChanged("selectedKeys")) { + selectionModel.updateFromState(); + } } /** @@ -332,4 +416,9 @@ public class GridConnector extends AbstractComponentConnector { } } } + + public void setDataSource(RpcDataSource dataSource) { + this.dataSource = dataSource; + getWidget().setDataSource(this.dataSource); + } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java b/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java index 52bb6c0f60..53b0d064ab 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java @@ -17,7 +17,6 @@ package com.vaadin.client.ui.grid.selection; import java.util.Collection; import java.util.HashSet; -import java.util.logging.Logger; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; @@ -37,7 +36,7 @@ import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.renderers.ComplexRenderer; /* This class will probably not survive the final merge of all selection functionality. */ -public class MultiSelectionRenderer extends ComplexRenderer { +public class MultiSelectionRenderer extends ComplexRenderer { private class TouchEventHandler implements NativePreviewHandler { @Override @@ -168,12 +167,12 @@ public class MultiSelectionRenderer extends ComplexRenderer { private static final String LOGICAL_ROW_PROPERTY_INT = "vEscalatorLogicalRow"; - private final Grid grid; + private final Grid grid; private HandlerRegistration nativePreviewHandlerRegistration; private final SelectionHandler selectionHandler = new SelectionHandler(); - public MultiSelectionRenderer(final Grid grid) { + public MultiSelectionRenderer(final Grid grid) { this.grid = grid; } @@ -276,23 +275,16 @@ public class MultiSelectionRenderer extends ComplexRenderer { } } - private boolean isSelected(final int logicalRow) { - // TODO - // return grid.getSelectionModel().isSelected(logicalRow); - return false; + protected boolean isSelected(final int logicalRow) { + return grid.isSelected(grid.getDataSource().getRow(logicalRow)); } - private void setSelected(final int logicalRow, final boolean select) { + protected void setSelected(final int logicalRow, final boolean select) { + T row = grid.getDataSource().getRow(logicalRow); if (select) { - // TODO - // grid.getSelectionModel().select(logicalRow); - Logger.getLogger(getClass().getName()).warning( - "Selecting " + logicalRow); + grid.select(row); } else { - // TODO - // grid.getSelectionModel().deselect(logicalRow); - Logger.getLogger(getClass().getName()).warning( - "Deselecting " + logicalRow); + grid.deselect(row); } } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java index d11b7764d0..989a8946c7 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java @@ -25,7 +25,7 @@ import com.vaadin.client.ui.grid.Renderer; *

* Selection models perform tracking of selected rows in the Grid, as well as * dispatching events when the selection state changes. - * + * * @author Vaadin Ltd * @since 7.4 * @param @@ -36,7 +36,7 @@ public interface SelectionModel { /** * Return true if the provided row is considered selected under the * implementing selection model. - * + * * @param row * row object instance * @return true, if the row given as argument is considered @@ -47,18 +47,18 @@ public interface SelectionModel { /** * Return the {@link Renderer} responsible for rendering the selection * column. - * + * * @return a renderer instance. If null is returned, a selection column will * not be drawn. */ - public Renderer getSelectionColumnRenderer(); + public Renderer getSelectionColumnRenderer(); /** * Tells this SelectionModel which Grid it belongs to. *

* Implementations are free to have this be a no-op. This method is called * internally by Grid. - * + * * @param grid * a {@link Grid} instance */ @@ -74,7 +74,7 @@ public interface SelectionModel { /** * Returns a Collection containing all selected rows. - * + * * @return a non-null collection. */ public Collection getSelectedRows(); @@ -82,7 +82,7 @@ public interface SelectionModel { /** * Selection model that allows a maximum of one row to be selected at any * one time. - * + * * @param * type parameter corresponding with Grid row type */ @@ -90,7 +90,7 @@ public interface SelectionModel { /** * Selects a row. - * + * * @param row * a {@link Grid} row object * @return true, if this row as not previously selected. @@ -101,7 +101,7 @@ public interface SelectionModel { * Deselects a row. *

* This is a no-op unless {@link row} is the currently selected row. - * + * * @param row * a {@link Grid} row object * @return true, if the currently selected row was deselected. @@ -110,7 +110,7 @@ public interface SelectionModel { /** * Returns the currently selected row. - * + * * @return a {@link Grid} row object or null, if nothing is selected. */ public T getSelectedRow(); @@ -119,7 +119,7 @@ public interface SelectionModel { /** * Selection model that allows for several rows to be selected at once. - * + * * @param * type parameter corresponding with Grid row type */ @@ -127,7 +127,7 @@ public interface SelectionModel { /** * Selects one or more rows. - * + * * @param rows * {@link Grid} row objects * @return true, if the set of selected rows was changed. @@ -136,7 +136,7 @@ public interface SelectionModel { /** * Deselects one or more rows. - * + * * @param rows * Grid row objects * @return true, if the set of selected rows was changed. @@ -145,14 +145,14 @@ public interface SelectionModel { /** * De-selects all rows. - * + * * @return true, if any row was previously selected. */ public boolean deselectAll(); /** * Select all rows in a {@link Collection}. - * + * * @param rows * a collection of Grid row objects * @return true, if the set of selected rows was changed. @@ -161,7 +161,7 @@ public interface SelectionModel { /** * Deselect all rows in a {@link Collection}. - * + * * @param rows * a collection of Grid row objects * @return true, if the set of selected rows was changed. @@ -173,7 +173,7 @@ public interface SelectionModel { /** * Interface for a selection model that does not allow anything to be * selected. - * + * * @param * type parameter corresponding with Grid row type */ diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java index 8afb592771..de62dc9cbc 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java @@ -17,38 +17,38 @@ package com.vaadin.client.ui.grid.selection; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.Renderer; /** * Multi-row selection model. - * + * * @author Vaadin Ltd * @since 7.4 */ public class SelectionModelMulti implements SelectionModel.Multi { - private final Renderer renderer; - private final Set selectedRows; + private final Set> selectedRows; + private Renderer renderer; private Grid grid; public SelectionModelMulti() { grid = null; renderer = null; - selectedRows = new LinkedHashSet(); + selectedRows = new LinkedHashSet>(); } @Override public boolean isSelected(T row) { - return selectedRows.contains(row); + return isSelectedByHandle(grid.getDataSource().getHandle(row)); } @Override - public Renderer getSelectionColumnRenderer() { + public Renderer getSelectionColumnRenderer() { return renderer; } @@ -64,6 +64,8 @@ public class SelectionModelMulti implements SelectionModel.Multi { throw new IllegalStateException( "Grid reference cannot be reassigned"); } + + this.renderer = new MultiSelectionRenderer(grid); } @Override @@ -87,7 +89,7 @@ public class SelectionModelMulti implements SelectionModel.Multi { if (selectedRows.size() > 0) { SelectionChangeEvent event = new SelectionChangeEvent(grid, - null, selectedRows); + null, getSelectedRows()); selectedRows.clear(); grid.fireEvent(event); @@ -105,7 +107,8 @@ public class SelectionModelMulti implements SelectionModel.Multi { Set added = new LinkedHashSet(); for (T row : rows) { - if (selectedRows.add(row)) { + RowHandle handle = grid.getDataSource().getHandle(row); + if (selectByHandle(handle)) { added.add(row); } } @@ -127,7 +130,7 @@ public class SelectionModelMulti implements SelectionModel.Multi { Set removed = new LinkedHashSet(); for (T row : rows) { - if (selectedRows.remove(row)) { + if (deselectByHandle(grid.getDataSource().getHandle(row))) { removed.add(row); } } @@ -140,14 +143,37 @@ public class SelectionModelMulti implements SelectionModel.Multi { return false; } + protected boolean isSelectedByHandle(RowHandle handle) { + return selectedRows.contains(handle); + } + + protected boolean selectByHandle(RowHandle handle) { + if (selectedRows.add(handle)) { + handle.pin(); + return true; + } + return false; + } + + protected boolean deselectByHandle(RowHandle handle) { + if (selectedRows.remove(handle)) { + handle.unpin(); + return true; + } + return false; + } + @Override public Collection getSelectedRows() { - return Collections.unmodifiableSet(selectedRows); + Set selected = new LinkedHashSet(); + for (RowHandle handle : selectedRows) { + selected.add(handle.getRow()); + } + return selected; } @Override public void reset() { deselectAll(); } - } diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java index bcb0357089..93dfb49df2 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java @@ -23,7 +23,7 @@ import com.vaadin.client.ui.grid.Renderer; /** * No-row selection model. - * + * * @author Vaadin Ltd * @since 7.4 */ @@ -35,7 +35,7 @@ public class SelectionModelNone implements SelectionModel.None { } @Override - public Renderer getSelectionColumnRenderer() { + public Renderer getSelectionColumnRenderer() { return null; } diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java index 6b5f645e23..775e1878c5 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java @@ -18,30 +18,31 @@ package com.vaadin.client.ui.grid.selection; import java.util.Collection; import java.util.Collections; +import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.Renderer; /** * Single-row selection model. - * + * * @author Vaadin Ltd * @since 7.4 */ public class SelectionModelSingle implements SelectionModel.Single { private Grid grid; - private T selectedRow; + private RowHandle selectedRow; + private Renderer renderer; @Override public boolean isSelected(T row) { - return row == null ? null : row.equals(getSelectedRow()); + return selectedRow != null + && selectedRow.equals(grid.getDataSource().getHandle(row)); } @Override - public Renderer getSelectionColumnRenderer() { - // TODO: Add implementation of SelectionColumnRenderer; currently none - // exists - return null; + public Renderer getSelectionColumnRenderer() { + return renderer; } @Override @@ -56,6 +57,7 @@ public class SelectionModelSingle implements SelectionModel.Single { throw new IllegalStateException( "Grid reference cannot be reassigned"); } + renderer = new MultiSelectionRenderer(grid); } @Override @@ -65,12 +67,17 @@ public class SelectionModelSingle implements SelectionModel.Single { throw new IllegalArgumentException("Row cannot be null"); } - if (row.equals(getSelectedRow())) { + if (isSelected(row)) { return false; } - T removed = selectedRow; - selectedRow = row; + T removed = getSelectedRow(); + if (selectedRow != null) { + selectedRow.unpin(); + } + selectedRow = grid.getDataSource().getHandle(row); + selectedRow.pin(); + grid.fireEvent(new SelectionChangeEvent(grid, row, removed)); return true; @@ -83,8 +90,9 @@ public class SelectionModelSingle implements SelectionModel.Single { throw new IllegalArgumentException("Row cannot be null"); } - if (row.equals(selectedRow)) { - T removed = selectedRow; + if (isSelected(row)) { + T removed = selectedRow.getRow(); + selectedRow.unpin(); selectedRow = null; grid.fireEvent(new SelectionChangeEvent(grid, null, removed)); return true; @@ -95,16 +103,15 @@ public class SelectionModelSingle implements SelectionModel.Single { @Override public T getSelectedRow() { - return selectedRow; + return (selectedRow != null ? selectedRow.getRow() : null); } @Override public void reset() { - T removed = selectedRow; - selectedRow = null; + T removed = getSelectedRow(); if (removed != null) { - grid.fireEvent(new SelectionChangeEvent(grid, null, removed)); + deselect(removed); } } diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 0046b256bb..1834822d99 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -17,6 +17,7 @@ package com.vaadin.data; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -24,11 +25,14 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import com.google.gwt.thirdparty.guava.common.collect.BiMap; +import com.google.gwt.thirdparty.guava.common.collect.HashBiMap; import com.vaadin.data.Container.Indexed; import com.vaadin.data.Container.Indexed.ItemAddEvent; import com.vaadin.data.Container.Indexed.ItemRemoveEvent; @@ -62,6 +66,227 @@ import com.vaadin.ui.components.grid.Renderer; */ public class RpcDataProviderExtension extends AbstractExtension { + /** + * ItemId to Key to ItemId mapper. + *

+ * This class is used when transmitting information about items in container + * related to Grid. It introduces a consistent way of mapping ItemIds and + * its container to a String that can be mapped back to ItemId. + *

+ * Technical note: This class also keeps tabs on which indices are + * being shown/selected, and is able to clean up after itself once the + * itemId ⇆ key mapping is not needed anymore. In other words, this + * doesn't leak memory. + */ + public class DataProviderKeyMapper { + private final BiMap indexToItemId = HashBiMap.create(); + private final BiMap itemIdToKey = HashBiMap.create(); + private Set pinnedItemIds = new HashSet(); + private Range activeRange = Range.withLength(0, 0); + private long rollingIndex = 0; + + private DataProviderKeyMapper() { + // private implementation + } + + void preActiveRowsChange(Range newActiveRange, int firstNewIndex, + List itemIds) { + final Range[] removed = activeRange.partitionWith(newActiveRange); + final Range[] added = newActiveRange.partitionWith(activeRange); + + removeActiveRows(removed[0]); + removeActiveRows(removed[2]); + addActiveRows(added[0], firstNewIndex, itemIds); + addActiveRows(added[2], firstNewIndex, itemIds); + + activeRange = newActiveRange; + } + + private void removeActiveRows(final Range deprecated) { + for (int i = deprecated.getStart(); i < deprecated.getEnd(); i++) { + final Integer ii = Integer.valueOf(i); + final Object itemId = indexToItemId.get(ii); + + if (!pinnedItemIds.contains(itemId)) { + itemIdToKey.remove(itemId); + } + indexToItemId.remove(ii); + } + } + + private void addActiveRows(final Range added, int firstNewIndex, + List newItemIds) { + + for (int i = added.getStart(); i < added.getEnd(); i++) { + + /* + * We might be in a situation we have an index <-> itemId entry + * already. This happens when something was selected, scrolled + * out of view and now we're scrolling it back into view. It's + * unnecessary to overwrite it in that case. + * + * Fun thought: considering branch prediction, it _might_ even + * be a bit faster to simply always run the code beyond this + * if-state. But it sounds too stupid (and most often too + * insignificant) to try out. + */ + final Integer ii = Integer.valueOf(i); + if (indexToItemId.containsKey(ii)) { + continue; + } + + /* + * We might be in a situation where we have an itemId <-> key + * entry already, but no index for it. This happens when + * something that is out of view is selected programmatically. + * In that case, we only want to add an index for that entry, + * and not overwrite the key. + */ + final Object itemId = newItemIds.get(i - firstNewIndex); + if (!itemIdToKey.containsKey(itemId)) { + itemIdToKey.put(itemId, nextKey()); + } + indexToItemId.put(ii, itemId); + } + } + + private String nextKey() { + return String.valueOf(rollingIndex++); + } + + String getKey(Object itemId) { + String key = itemIdToKey.get(itemId); + if (key == null) { + key = nextKey(); + itemIdToKey.put(itemId, key); + } + return key; + } + + /** + * Gets keys for a collection of item ids. + *

+ * If the itemIds are currently cached, the existing keys will be used. + * Otherwise new ones will be created. + * + * @param itemIds + * the item ids for which to get keys + * @return keys for the {@code itemIds} + */ + public List getKeys(Collection itemIds) { + if (itemIds == null) { + throw new IllegalArgumentException("itemIds can't be null"); + } + + ArrayList keys = new ArrayList(itemIds.size()); + for (Object itemId : itemIds) { + keys.add(getKey(itemId)); + } + return keys; + } + + Object getItemId(String key) throws IllegalStateException { + Object itemId = itemIdToKey.inverse().get(key); + if (itemId != null) { + return itemId; + } else { + throw new IllegalStateException("No item id for key " + key + + " found."); + } + } + + /** + * Gets corresponding item ids for each of the keys in a collection. + * + * @param keys + * the keys for which to retrieve item ids + * @return a collection of item ids for the {@code keys} + * @throws IllegalStateException + * if one or more of keys don't have a corresponding item id + * in the cache + */ + public Collection getItemIds(Collection keys) + throws IllegalStateException { + if (keys == null) { + throw new IllegalArgumentException("keys may not be null"); + } + + ArrayList itemIds = new ArrayList(keys.size()); + for (String key : keys) { + itemIds.add(getItemId(key)); + } + return itemIds; + } + + /** + * Pin an item id to be cached indefinitely. + *

+ * Normally when an itemId is not an active row, it is discarded from + * the cache. Pinning an item id will make sure that it is kept in the + * cache. + *

+ * In effect, while an item id is pinned, it always has the same key. + * + * @param itemId + * the item id to pin + * @throws IllegalStateException + * if {@code itemId} was already pinned + * @see #unpin(Object) + * @see #isPinned(Object) + * @see #getItemIds(Collection) + */ + public void pin(Object itemId) throws IllegalStateException { + if (isPinned(itemId)) { + throw new IllegalStateException("Item id " + itemId + + " was pinned already"); + } + pinnedItemIds.add(itemId); + } + + /** + * Unpin an item id. + *

+ * This cancels the effect of pinning an item id. If the item id is + * currently inactive, it will be immediately removed from the cache. + * + * @param itemId + * the item id to unpin + * @throws IllegalStateException + * if {@code itemId} was not pinned + * @see #pin(Object) + * @see #isPinned(Object) + * @see #getItemIds(Collection) + */ + public void unpin(Object itemId) throws IllegalStateException { + if (!isPinned(itemId)) { + throw new IllegalStateException("Item id " + itemId + + " was not pinned"); + } + + pinnedItemIds.remove(itemId); + final Integer removedIndex = indexToItemId.inverse().remove(itemId); + if (removedIndex == null + || !activeRange.contains(removedIndex.intValue())) { + itemIdToKey.remove(itemId); + } + } + + /** + * Checks whether an item id is pinned or not. + * + * @param itemId + * the item id to check for pin status + * @return {@code true} iff the item id is currently pinned + */ + public boolean isPinned(Object itemId) { + return pinnedItemIds.contains(itemId); + } + + Object itemIdAtIndex(int index) { + return indexToItemId.inverse().get(Integer.valueOf(index)); + } + } + /** * A helper class that handles the client-side Escalator logic relating to * making sure that whatever is currently visible to the user, is properly @@ -70,8 +295,9 @@ public class RpcDataProviderExtension extends AbstractExtension { *

* This bookeeping includes, but is not limited to: *

    - *
  • listening to the currently visible {@link Property Properties'} value - * changes on the server side and sending those back to the client; and + *
  • listening to the currently visible {@link com.vaadin.data.Property + * Properties'} value changes on the server side and sending those back to + * the client; and *
  • attaching and detaching {@link com.vaadin.ui.Component Components} * from the Vaadin Component hierarchy. *
@@ -340,7 +566,7 @@ public class RpcDataProviderExtension extends AbstractExtension { ItemRemoveEvent removeEvent = (ItemRemoveEvent) event; int firstIndex = removeEvent.getFirstIndex(); int count = removeEvent.getRemovedItemsCount(); - removeRowData(firstIndex, count, removeEvent.getFirstItemId()); + removeRowData(firstIndex, count); } else { @@ -353,6 +579,8 @@ public class RpcDataProviderExtension extends AbstractExtension { } }; + private final DataProviderKeyMapper keyMapper = new DataProviderKeyMapper(); + /** * Creates a new data provider using the given container. * @@ -366,8 +594,6 @@ public class RpcDataProviderExtension extends AbstractExtension { @Override public void requestRows(int firstRow, int numberOfRows, int firstCachedRowIndex, int cacheSize) { - pushRows(firstRow, numberOfRows); - Range active = Range.withLength(firstRow, numberOfRows); if (cacheSize != 0) { Range cached = Range.withLength(firstCachedRowIndex, @@ -375,6 +601,11 @@ public class RpcDataProviderExtension extends AbstractExtension { active = active.combineWith(cached); } + List itemIds = RpcDataProviderExtension.this.container + .getItemIds(firstRow, numberOfRows); + keyMapper.preActiveRowsChange(active, firstRow, itemIds); + pushRows(firstRow, itemIds); + activeRowHandler.setActiveRows(active.getStart(), active.length()); } @@ -389,8 +620,7 @@ public class RpcDataProviderExtension extends AbstractExtension { } - private void pushRows(int firstRow, int numberOfRows) { - List itemIds = container.getItemIds(firstRow, numberOfRows); + private void pushRows(int firstRow, List itemIds) { Collection propertyIds = container.getContainerPropertyIds(); JSONArray rows = new JSONArray(); for (Object itemId : itemIds) { @@ -402,6 +632,7 @@ public class RpcDataProviderExtension extends AbstractExtension { private JSONObject getRowData(Collection propertyIds, Object itemId) { Item item = container.getItem(itemId); + String[] row = new String[propertyIds.size()]; JSONArray rowData = new JSONArray(); @@ -421,13 +652,7 @@ public class RpcDataProviderExtension extends AbstractExtension { final JSONObject rowObject = new JSONObject(); rowObject.put(GridState.JSONKEY_DATA, rowData); - /* - * TODO: selection wants to put here something in the lines of: - * - * rowObject.put(GridState.JSONKEY_ROWKEY, getKey(itemId)) - * - * Henrik Paul: 18.6.2014 - */ + rowObject.put(GridState.JSONKEY_ROWKEY, keyMapper.getKey(itemId)); return rowObject; } catch (final JSONException e) { throw new RuntimeException("Grid was unable to serialize " @@ -477,23 +702,16 @@ public class RpcDataProviderExtension extends AbstractExtension { * @param firstItemId * the item id of the first removed item */ - private void removeRowData(int firstIndex, int count, Object firstItemId) { + private void removeRowData(int firstIndex, int count) { getState().containerSize -= count; getRpcProxy(DataProviderRpc.class).removeRowData(firstIndex, count); - /* - * Unfortunately, there's no sane way of getting the rest of the removed - * itemIds unless we cache a mapping between index and itemId. - * - * Fortunately, the only time _currently_ an event with more than one - * removed item seems to be when calling - * AbstractInMemoryContainer.removeAllElements(). Otherwise, it's only - * removing one item at a time. - * - * We _could_ have a backup of all the itemIds, and compare to that one, - * but we really really don't want to go there. - */ - activeRowHandler.removeItemId(firstItemId); + for (int i = 0; i < count; i++) { + Object itemId = keyMapper.itemIdAtIndex(firstIndex + i); + if (itemId != null) { + activeRowHandler.removeItemId(itemId); + } + } } /** @@ -566,6 +784,10 @@ public class RpcDataProviderExtension extends AbstractExtension { activeRowHandler.propertiesAdded(addedPropertyIds); } + public DataProviderKeyMapper getKeyMapper() { + return keyMapper; + } + protected Grid getGrid() { return (Grid) getParent(); } diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 1ebf227330..bc6a69e850 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -26,16 +26,23 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.json.JSONArray; +import org.json.JSONException; + +import com.google.gwt.thirdparty.guava.common.collect.Sets; +import com.google.gwt.thirdparty.guava.common.collect.Sets.SetView; import com.vaadin.data.Container; import com.vaadin.data.Container.PropertySetChangeEvent; import com.vaadin.data.Container.PropertySetChangeListener; import com.vaadin.data.Container.PropertySetChangeNotifier; import com.vaadin.data.Container.Sortable; import com.vaadin.data.RpcDataProviderExtension; +import com.vaadin.data.RpcDataProviderExtension.DataProviderKeyMapper; import com.vaadin.server.KeyMapper; import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; @@ -181,6 +188,15 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { */ private SelectionModel selectionModel; + /** + * The number of times to ignore selection state sync to the client. + *

+ * This usually means that the client side has modified the selection. We + * still want to inform the listeners that the selection has changed, but we + * don't want to send those changes "back to the client". + */ + private int ignoreSelectionClientSync = 0; + private static final Method SELECTION_CHANGE_METHOD = ReflectTools .findMethod(SelectionChangeListener.class, "selectionChange", SelectionChangeEvent.class); @@ -191,9 +207,105 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * @param datasource * the data source for the grid */ - public Grid(Container.Indexed datasource) { + public Grid(final Container.Indexed datasource) { setContainerDataSource(datasource); setSelectionMode(SelectionMode.MULTI); + addSelectionChangeListener(new SelectionChangeListener() { + @Override + public void selectionChange(SelectionChangeEvent event) { + for (Object removedItemId : event.getRemoved()) { + keyMapper().unpin(removedItemId); + } + + for (Object addedItemId : event.getAdded()) { + keyMapper().pin(addedItemId); + } + + List keys = keyMapper().getKeys(getSelectedRows()); + + boolean markAsDirty = true; + + /* + * If this clause is true, it means that the selection event + * originated from the client. This means that we don't want to + * send the changes back to the client (markAsDirty => false). + */ + if (ignoreSelectionClientSync > 0) { + ignoreSelectionClientSync--; + markAsDirty = false; + + try { + + /* + * Make sure that the diffstate is aware of the + * "undirty" modification, so that the diffs are + * calculated correctly the next time we actually want + * to send the selection state to the client. + */ + getUI().getConnectorTracker().getDiffState(Grid.this) + .put("selectedKeys", new JSONArray(keys)); + } catch (JSONException e) { + throw new RuntimeException("Internal error", e); + } + } + + getState(markAsDirty).selectedKeys = keys; + } + }); + + registerRpc(new GridServerRpc() { + + @Override + public void selectionChange(List selection) { + final HashSet newSelection = new HashSet( + keyMapper().getItemIds(selection)); + final HashSet oldSelection = new HashSet( + getSelectedRows()); + + SetView addedItemIds = Sets.difference(newSelection, + oldSelection); + SetView removedItemIds = Sets.difference(oldSelection, + newSelection); + + if (!addedItemIds.isEmpty()) { + /* + * Since these changes come from the client, we want to + * modify the selection model and get that event fired to + * all the listeners. One of the listeners is our internal + * selection listener, and this tells it not to send the + * selection event back to the client. + */ + ignoreSelectionClientSync++; + + if (addedItemIds.size() == 1) { + select(addedItemIds.iterator().next()); + } else { + assert getSelectionModel() instanceof SelectionModel.Multi : "Got multiple selections, but the selection model is not a SelectionModel.Multi"; + ((SelectionModel.Multi) getSelectionModel()) + .select(addedItemIds); + } + } + + if (!removedItemIds.isEmpty()) { + /* + * Since these changes come from the client, we want to + * modify the selection model and get that event fired to + * all the listeners. One of the listeners is our internal + * selection listener, and this tells it not to send the + * selection event back to the client. + */ + ignoreSelectionClientSync++; + + if (removedItemIds.size() == 1) { + deselect(removedItemIds.iterator().next()); + } else { + assert getSelectionModel() instanceof SelectionModel.Multi : "Got multiple deselections, but the selection model is not a SelectionModel.Multi"; + ((SelectionModel.Multi) getSelectionModel()) + .deselect(removedItemIds); + } + } + } + }); } /** @@ -205,6 +317,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * if the data source is null */ public void setContainerDataSource(Container.Indexed container) { + if (container == null) { throw new IllegalArgumentException( "Cannot set the datasource to null"); @@ -935,11 +1048,21 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { SELECTION_CHANGE_METHOD); } - /** FIXME remove once selection mode communcation is done. only for testing. */ + /** + * FIXME remove once selection mode communication is done. only for testing. + */ public void setSelectionCheckboxes(boolean value) { getState().selectionCheckboxes = value; } + /** + * A shortcut for + * {@link #datasourceExtension}.{@link com.vaadin.data.RpcDataProviderExtension#getKeyMapper() getKeyMapper()} + */ + private DataProviderKeyMapper keyMapper() { + return datasourceExtension.getKeyMapper(); + } + /** * Adds a renderer to this grid's connector hierarchy. * diff --git a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java index cecdca80df..f0e25405cc 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java +++ b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java @@ -17,7 +17,7 @@ package com.vaadin.ui.components.grid.selection; import java.util.Collection; import java.util.EventObject; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import com.google.gwt.thirdparty.guava.common.collect.Sets; @@ -32,14 +32,14 @@ import com.vaadin.ui.components.grid.Grid; */ public class SelectionChangeEvent extends EventObject { - private Set oldSelection; - private Set newSelection; + private LinkedHashSet oldSelection; + private LinkedHashSet newSelection; public SelectionChangeEvent(Grid source, Collection oldSelection, Collection newSelection) { super(source); - this.oldSelection = new HashSet(oldSelection); - this.newSelection = new HashSet(newSelection); + this.oldSelection = new LinkedHashSet(oldSelection); + this.newSelection = new LinkedHashSet(newSelection); } /** diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java b/server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java new file mode 100644 index 0000000000..9ecf131c5b --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/grid/DataProviderExtension.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.grid; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.RpcDataProviderExtension; +import com.vaadin.data.RpcDataProviderExtension.DataProviderKeyMapper; +import com.vaadin.data.util.IndexedContainer; + +public class DataProviderExtension { + private RpcDataProviderExtension dataProvider; + private DataProviderKeyMapper keyMapper; + private Container.Indexed container; + + private static final Object ITEM_ID1 = "itemid1"; + private static final Object ITEM_ID2 = "itemid2"; + private static final Object ITEM_ID3 = "itemid3"; + + private static final Object PROPERTY_ID1_STRING = "property1"; + + @Before + public void setup() { + container = new IndexedContainer(); + populate(container); + + dataProvider = new RpcDataProviderExtension(container); + keyMapper = dataProvider.getKeyMapper(); + } + + private static void populate(Indexed container) { + container.addContainerProperty(PROPERTY_ID1_STRING, String.class, ""); + for (Object itemId : Arrays.asList(ITEM_ID1, ITEM_ID2, ITEM_ID3)) { + final Item item = container.addItem(itemId); + @SuppressWarnings("unchecked") + final Property stringProperty = item + .getItemProperty(PROPERTY_ID1_STRING); + stringProperty.setValue(itemId.toString()); + } + } + + @Test + public void pinBasics() { + assertFalse("itemId1 should not start as pinned", + keyMapper.isPinned(ITEM_ID2)); + + keyMapper.pin(ITEM_ID1); + assertTrue("itemId1 should now be pinned", keyMapper.isPinned(ITEM_ID1)); + + keyMapper.unpin(ITEM_ID1); + assertFalse("itemId1 should not be pinned anymore", + keyMapper.isPinned(ITEM_ID2)); + } + + @Test(expected = IllegalStateException.class) + public void doublePinning() { + keyMapper.pin(ITEM_ID1); + keyMapper.pin(ITEM_ID1); + } + + @Test(expected = IllegalStateException.class) + public void nonexistentUnpin() { + keyMapper.unpin(ITEM_ID1); + } +} diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index a92ffe0421..43469914e5 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -34,7 +34,8 @@ public interface DataProviderRpc extends ClientRpc { * *
      * [{
-     *   "d": [COL_1_JSON, COL_2_json, ...]
+     *   "d": [COL_1_JSON, COL_2_json, ...],
+     *   "k": "1"
      * },
      * ...
      * ]
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java
new file mode 100644
index 0000000000..b763174e53
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * 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.vaadin.shared.ui.grid;
+
+import java.util.List;
+
+import com.vaadin.shared.communication.ServerRpc;
+
+/**
+ * Client-to-server RPC interface for the Grid component
+ * 
+ * @since 7.4
+ * @author Vaadin Ltd
+ */
+public interface GridServerRpc extends ServerRpc {
+    void selectionChange(List newSelection);
+}
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java
index eceaedd1fc..0b23e2c11d 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java
@@ -39,11 +39,18 @@ public class GridState extends AbstractComponentState {
 
     /**
      * The key in which a row's data can be found
-     * {@link com.vaadin.shared.data.DataProviderRpc#setRowData(int, List)
-     * DataProviderRpc.setRowData(int, List)}
+     * 
+     * @see com.vaadin.shared.data.DataProviderRpc#setRowData(int, String)
      */
     public static final String JSONKEY_DATA = "d";
 
+    /**
+     * The key in which a row's own key can be found
+     * 
+     * @see com.vaadin.shared.data.DataProviderRpc#setRowData(int, String)
+     */
+    public static final String JSONKEY_ROWKEY = "k";
+
     {
         // FIXME Grid currently does not support undefined size
         width = "400px";
@@ -97,4 +104,7 @@ public class GridState extends AbstractComponentState {
     @DelegateToWidget
     public boolean selectionCheckboxes;
 
+    // instantiated just to avoid NPEs
+    public List selectedKeys = new ArrayList();
+
 }
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
index 06fe088dee..c6597ef23b 100644
--- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
+++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
@@ -363,6 +363,20 @@ public class GridBasicFeatures extends AbstractComponentTest {
                         }
                     }
                 }, null);
+
+        createBooleanAction("Select first row", "Body rows", false,
+                new Command() {
+                    @Override
+                    public void execute(Grid grid, Boolean select, Object data) {
+                        final Object firstItemId = grid
+                                .getContainerDatasource().firstItemId();
+                        if (select.booleanValue()) {
+                            grid.select(firstItemId);
+                        } else {
+                            grid.deselect(firstItemId);
+                        }
+                    }
+                });
     }
 
     @SuppressWarnings("boxing")
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java
index a11b0f1be9..3dc8ac814f 100644
--- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java
+++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java
@@ -18,6 +18,7 @@ package com.vaadin.tests.components.grid;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
@@ -301,6 +302,83 @@ public class GridBasicFeaturesTest extends MultiBrowserTest {
                 "modified: Column0", getBodyCellByRowAndColumn(0, 0).getText());
     }
 
+    @Test
+    public void testSelectOnOff() throws Exception {
+        openTestURL();
+
+        assertFalse("row shouldn't start out as selected",
+                isSelected(getRow(0)));
+        toggleFirstRowSelection();
+        assertTrue("row should become selected", isSelected(getRow(0)));
+        toggleFirstRowSelection();
+        assertFalse("row shouldn't remain selected", isSelected(getRow(0)));
+    }
+
+    @Test
+    public void testSelectOnScrollOffScroll() throws Exception {
+        openTestURL();
+        assertFalse("row shouldn't start out as selected",
+                isSelected(getRow(0)));
+        toggleFirstRowSelection();
+        assertTrue("row should become selected", isSelected(getRow(0)));
+
+        scrollGridVerticallyTo(10000); // make sure the row is out of cache
+        scrollGridVerticallyTo(0); // scroll it back into view
+
+        assertTrue("row should still be selected when scrolling "
+                + "back into view", isSelected(getRow(0)));
+    }
+
+    @Test
+    public void testSelectScrollOnScrollOff() throws Exception {
+        openTestURL();
+        assertFalse("row shouldn't start out as selected",
+                isSelected(getRow(0)));
+
+        scrollGridVerticallyTo(10000); // make sure the row is out of cache
+        toggleFirstRowSelection();
+
+        scrollGridVerticallyTo(0); // scroll it back into view
+        assertTrue("row should still be selected when scrolling "
+                + "back into view", isSelected(getRow(0)));
+
+        toggleFirstRowSelection();
+        assertFalse("row shouldn't remain selected", isSelected(getRow(0)));
+    }
+
+    @Test
+    public void testSelectScrollOnOffScroll() throws Exception {
+        openTestURL();
+        assertFalse("row shouldn't start out as selected",
+                isSelected(getRow(0)));
+
+        scrollGridVerticallyTo(10000); // make sure the row is out of cache
+        toggleFirstRowSelection();
+        toggleFirstRowSelection();
+
+        scrollGridVerticallyTo(0); // make sure the row is out of cache
+        assertFalse("row shouldn't be selected when scrolling "
+                + "back into view", isSelected(getRow(0)));
+    }
+
+    private void toggleFirstRowSelection() {
+        selectMenuPath("Component", "Body rows", "Select first row");
+    }
+
+    @SuppressWarnings("static-method")
+    private boolean isSelected(TestBenchElement row) {
+        /*
+         * FIXME We probably should get a GridRow instead of a plain
+         * TestBenchElement, that has an "isSelected" thing integrated. (henrik
+         * paul 26.6.2014)
+         */
+        return row.getAttribute("class").contains("-row-selected");
+    }
+
+    private TestBenchElement getRow(int i) {
+        return getGridElement().getRow(i);
+    }
+
     private void assertPrimaryStylename(String stylename) {
         assertTrue(getGridElement().getAttribute("class").contains(stylename));
 
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java
index 91a4e19886..8ea652cc74 100644
--- a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java
+++ b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 import org.openqa.selenium.WebElement;
+import org.openqa.selenium.remote.DesiredCapabilities;
 
 import com.vaadin.testbench.By;
 import com.vaadin.testbench.TestBenchElement;
@@ -42,6 +43,14 @@ public class GridClientRenderers extends MultiBrowserTest {
 
     private int latency = 0;
 
+    @Override
+    protected DesiredCapabilities getDesiredCapabilities() {
+        DesiredCapabilities c = new DesiredCapabilities(
+                super.getDesiredCapabilities());
+        c.setCapability("handlesAlerts", true);
+        return c;
+    }
+
     @Override
     protected Class getUIClass() {
         return GridClientColumnRenderers.class;
-- 
cgit v1.2.3


From cad8f2bf2c02fbe2a6f406d51103909d78033c9d Mon Sep 17 00:00:00 2001
From: Henrik Paul 
Date: Fri, 27 Jun 2014 15:32:46 +0300
Subject: Use @DelegateToWidget instead of manual handling (#13334)

Change-Id: Iac7f742053b43e15e1bdf16d51cce7363987f291
---
 client/src/com/vaadin/client/ui/grid/GridConnector.java | 14 --------------
 shared/src/com/vaadin/shared/ui/grid/GridState.java     | 12 ++----------
 2 files changed, 2 insertions(+), 24 deletions(-)

(limited to 'shared/src/com')

diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java
index 3b1ecb44d8..31e52b1c1c 100644
--- a/client/src/com/vaadin/client/ui/grid/GridConnector.java
+++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java
@@ -278,20 +278,6 @@ public class GridConnector extends AbstractComponentConnector {
             }
         }
 
-        /*
-         * @DelegateToWidget annotation doesn't work because of
-         * http://dev.vaadin.com/ticket/12900. Remove manual code and uncomment
-         * annotations at GridState once fixed.
-         */
-
-        if (stateChangeEvent.hasPropertyChanged("heightByRows")) {
-            getWidget().setHeightByRows(getState().heightByRows);
-        }
-
-        if (stateChangeEvent.hasPropertyChanged("heightMode")) {
-            getWidget().setHeightMode(getState().heightMode);
-        }
-
         if (stateChangeEvent.hasPropertyChanged("selectedKeys")) {
             selectionModel.updateFromState();
         }
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java
index 0b23e2c11d..d223d64e7e 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java
@@ -85,19 +85,11 @@ public class GridState extends AbstractComponentState {
     public String lastFrozenColumnId = null;
 
     /** The height of the Grid in terms of body rows. */
-    // @DelegateToWidget
-    /*
-     * Annotation doesn't work because of http://dev.vaadin.com/ticket/12900.
-     * Remove manual code from Connector once fixed
-     */
+    @DelegateToWidget
     public double heightByRows = DEFAULT_HEIGHT_BY_ROWS;
 
     /** The mode by which Grid defines its height. */
-    // @DelegateToWidget
-    /*
-     * Annotation doesn't work because of http://dev.vaadin.com/ticket/12900.
-     * Remove manual code from Connector once fixed
-     */
+    @DelegateToWidget
     public HeightMode heightMode = HeightMode.CSS;
 
     /** FIXME remove once selection mode communcation is done. only for testing. */
-- 
cgit v1.2.3


From fdba3d43f594bb0236df022f505b64f4970885e5 Mon Sep 17 00:00:00 2001
From: Henrik Paul 
Date: Fri, 27 Jun 2014 12:55:45 +0300
Subject: Clean up test code for selection column (#13334)

Change-Id: I98963a367f05c39ad6e185ebf8a1d903efa686a2
---
 client/src/com/vaadin/client/ui/grid/Grid.java     | 48 +++++++++-------------
 .../com/vaadin/client/ui/grid/GridConnector.java   | 45 ++++++++++++++++++--
 server/src/com/vaadin/ui/components/grid/Grid.java |  7 ----
 .../src/com/vaadin/shared/ui/grid/GridState.java   |  4 --
 .../tests/components/grid/GridBasicFeatures.java   |  8 ----
 5 files changed, 61 insertions(+), 51 deletions(-)

(limited to 'shared/src/com')

diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java
index 9a75b37c42..c223f8786f 100644
--- a/client/src/com/vaadin/client/ui/grid/Grid.java
+++ b/client/src/com/vaadin/client/ui/grid/Grid.java
@@ -123,9 +123,9 @@ public class Grid extends Composite implements
 
         @Override
         public void setFooterCaption(String caption) {
-            if (initDone) {
-                throw new UnsupportedOperationException(
-                        "The selection column is read only");
+            if (!SharedUtil.equals(caption, getFooterCaption()) && initDone) {
+                throw new UnsupportedOperationException("The selection "
+                        + "column cannot be modified after init");
             } else {
                 super.setFooterCaption(caption);
             }
@@ -133,9 +133,9 @@ public class Grid extends Composite implements
 
         @Override
         public void setFooterRenderer(Renderer renderer) {
-            if (initDone) {
-                throw new UnsupportedOperationException(
-                        "The selection column is read only");
+            if (!SharedUtil.equals(renderer, getFooterRenderer()) && initDone) {
+                throw new UnsupportedOperationException("The selection "
+                        + "column cannot be modified after init");
             } else {
                 super.setFooterRenderer(renderer);
             }
@@ -143,9 +143,9 @@ public class Grid extends Composite implements
 
         @Override
         public void setHeaderCaption(String caption) {
-            if (initDone) {
-                throw new UnsupportedOperationException(
-                        "The selection column is read only");
+            if (!SharedUtil.equals(caption, getHeaderCaption()) && initDone) {
+                throw new UnsupportedOperationException("The selection "
+                        + "column cannot be modified after init");
             } else {
                 super.setHeaderCaption(caption);
             }
@@ -153,9 +153,9 @@ public class Grid extends Composite implements
 
         @Override
         public void setHeaderRenderer(Renderer renderer) {
-            if (initDone) {
-                throw new UnsupportedOperationException(
-                        "The selection column is read only");
+            if (!SharedUtil.equals(renderer, getHeaderRenderer()) && initDone) {
+                throw new UnsupportedOperationException("The selection "
+                        + "column cannot be modified after init");
             } else {
                 super.setHeaderRenderer(renderer);
             }
@@ -163,9 +163,9 @@ public class Grid extends Composite implements
 
         @Override
         public void setVisible(boolean visible) {
-            if (initDone) {
-                throw new UnsupportedOperationException(
-                        "The selection column is read only");
+            if (!visible && initDone) {
+                throw new UnsupportedOperationException("The selection "
+                        + "column cannot be modified after init");
             } else {
                 super.setVisible(visible);
             }
@@ -173,9 +173,9 @@ public class Grid extends Composite implements
 
         @Override
         public void setWidth(int pixels) {
-            if (initDone) {
-                throw new UnsupportedOperationException(
-                        "The selection column is read only");
+            if (pixels != getWidth() && initDone) {
+                throw new UnsupportedOperationException("The selection "
+                        + "column cannot be modified after init");
             } else {
                 super.setWidth(pixels);
             }
@@ -2199,15 +2199,6 @@ public class Grid extends Composite implements
         }
     }
 
-    /* TODO remove before final */
-    public void setSelectionCheckboxes(boolean set) {
-        if (set) {
-            setSelectColumnRenderer(selectionModel.getSelectionColumnRenderer());
-        } else {
-            setSelectColumnRenderer(null);
-        }
-    }
-
     /**
      * Accesses the package private method Widget#setParent()
      * 
@@ -2239,7 +2230,8 @@ public class Grid extends Composite implements
 
         this.selectionModel = selectionModel;
         selectionModel.setGrid(this);
-
+        setSelectColumnRenderer(this.selectionModel
+                .getSelectionColumnRenderer());
     }
 
     /**
diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java
index 31e52b1c1c..59b8c4047c 100644
--- a/client/src/com/vaadin/client/ui/grid/GridConnector.java
+++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java
@@ -234,6 +234,9 @@ public class GridConnector extends AbstractComponentConnector {
             purgeRemovedColumns();
 
             int currentColumns = getWidget().getColumnCount();
+            if (getWidget().getSelectionModel().getSelectionColumnRenderer() != null) {
+                currentColumns--;
+            }
 
             // Add new columns
             for (int columnIndex = currentColumns; columnIndex < totalColumns; columnIndex++) {
@@ -289,11 +292,24 @@ public class GridConnector extends AbstractComponentConnector {
      * @param columnIndex
      *            The index of the column to update
      */
-    private void updateColumnFromStateChangeEvent(int columnIndex) {
-        GridColumn column = getWidget().getColumn(columnIndex);
+    private void updateColumnFromStateChangeEvent(final int columnIndex) {
+        /*
+         * We use the widget column index here instead of the given column
+         * index. SharedState contains information only about the explicitly
+         * defined columns, while the widget counts the selection column as an
+         * explicit one.
+         */
+        GridColumn column = getWidget().getColumn(
+                getWidgetColumnIndex(columnIndex));
+
         GridColumnState columnState = getState().columns.get(columnIndex);
         updateColumnFromState(column, columnState);
 
+        assert column instanceof CustomGridColumn : "column at index "
+                + columnIndex + " is not a "
+                + CustomGridColumn.class.getSimpleName() + ", but a "
+                + column.getClass().getSimpleName();
+
         if (columnState.rendererConnector != ((CustomGridColumn) column)
                 .getRendererConnector()) {
             throw new UnsupportedOperationException(
@@ -314,8 +330,15 @@ public class GridConnector extends AbstractComponentConnector {
                 ((AbstractRendererConnector) state.rendererConnector));
         columnIdToColumn.put(state.id, column);
 
-        // Adds a column to grid, and registers Grid with the column.
-        getWidget().addColumn(column, columnIndex);
+        /*
+         * Adds a column to grid, and registers Grid with the column.
+         * 
+         * We use the widget column index here instead of the given column
+         * index. SharedState contains information only about the explicitly
+         * defined columns, while the widget counts the selection column as an
+         * explicit one.
+         */
+        getWidget().addColumn(column, getWidgetColumnIndex(columnIndex));
 
         /*
          * Have to update state _after_ the column has been added to the grid as
@@ -328,6 +351,20 @@ public class GridConnector extends AbstractComponentConnector {
         updateColumnFromState(column, state);
     }
 
+    /**
+     * If we have a selection column renderer, we need to offset the index by
+     * one when referring to the column index in the widget.
+     */
+    private int getWidgetColumnIndex(final int columnIndex) {
+        Renderer selectionColumnRenderer = getWidget()
+                .getSelectionModel().getSelectionColumnRenderer();
+        int widgetColumnIndex = columnIndex;
+        if (selectionColumnRenderer != null) {
+            widgetColumnIndex++;
+        }
+        return widgetColumnIndex;
+    }
+
     /**
      * Updates the column values from a state
      * 
diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java
index bc6a69e850..21cb50154f 100644
--- a/server/src/com/vaadin/ui/components/grid/Grid.java
+++ b/server/src/com/vaadin/ui/components/grid/Grid.java
@@ -1048,13 +1048,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier {
                 SELECTION_CHANGE_METHOD);
     }
 
-    /**
-     * FIXME remove once selection mode communication is done. only for testing.
-     */
-    public void setSelectionCheckboxes(boolean value) {
-        getState().selectionCheckboxes = value;
-    }
-
     /**
      * A shortcut for
      * {@link #datasourceExtension}.{@link com.vaadin.data.RpcDataProviderExtension#getKeyMapper() getKeyMapper()}
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java
index d223d64e7e..6ca0021817 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java
@@ -92,10 +92,6 @@ public class GridState extends AbstractComponentState {
     @DelegateToWidget
     public HeightMode heightMode = HeightMode.CSS;
 
-    /** FIXME remove once selection mode communcation is done. only for testing. */
-    @DelegateToWidget
-    public boolean selectionCheckboxes;
-
     // instantiated just to avoid NPEs
     public List selectedKeys = new ArrayList();
 
diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
index 07fb80e0dd..cfe1d0c00d 100644
--- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
+++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java
@@ -190,14 +190,6 @@ public class GridBasicFeatures extends AbstractComponentTest {
     protected void createColumnActions() {
         createCategory("Columns", null);
 
-        createBooleanAction("Selection controls", "Columns", false,
-                new Command() {
-                    @Override
-                    public void execute(Grid grid, Boolean value, Object data) {
-                        grid.setSelectionCheckboxes(value);
-                    }
-                });
-
         for (int c = 0; c < COLUMNS; c++) {
             createCategory(getColumnProperty(c), "Columns");
 
-- 
cgit v1.2.3


From 1f94f03f56182697fda5c79ff42c492f26a8767b Mon Sep 17 00:00:00 2001
From: Henrik Paul 
Date: Mon, 30 Jun 2014 17:37:51 +0300
Subject: Client now recognizes server-side selection model changes. (#13334)

Change-Id: Ic42e0e96871620fde6b9ce17dd5b487b1d6b8370
---
 .../com/vaadin/client/ui/grid/GridConnector.java   | 17 ++++++++++
 server/src/com/vaadin/ui/components/grid/Grid.java | 13 ++++++++
 .../src/com/vaadin/shared/ui/grid/GridState.java   | 37 ++++++++++++++++++++++
 .../tests/components/grid/GridBasicFeatures.java   | 14 ++++++++
 4 files changed, 81 insertions(+)

(limited to 'shared/src/com')

diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java
index 59b8c4047c..ee1cc0ee75 100644
--- a/client/src/com/vaadin/client/ui/grid/GridConnector.java
+++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java
@@ -25,10 +25,12 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.logging.Logger;
 
 import com.google.gwt.json.client.JSONArray;
 import com.google.gwt.json.client.JSONObject;
 import com.google.gwt.json.client.JSONValue;
+import com.vaadin.client.annotations.OnStateChange;
 import com.vaadin.client.communication.StateChangeEvent;
 import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource;
 import com.vaadin.client.ui.AbstractComponentConnector;
@@ -43,6 +45,7 @@ import com.vaadin.shared.ui.grid.GridClientRpc;
 import com.vaadin.shared.ui.grid.GridColumnState;
 import com.vaadin.shared.ui.grid.GridServerRpc;
 import com.vaadin.shared.ui.grid.GridState;
+import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode;
 import com.vaadin.shared.ui.grid.ScrollDestination;
 
 /**
@@ -444,4 +447,18 @@ public class GridConnector extends AbstractComponentConnector {
         this.dataSource = dataSource;
         getWidget().setDataSource(this.dataSource);
     }
+
+    @OnStateChange("selectionMode")
+    private void onSelectionModeChange() {
+        SharedSelectionMode mode = getState().selectionMode;
+        if (mode == null) {
+            getLogger().warning("ignored mode change");
+            return;
+        }
+        getLogger().warning(mode.toString());
+    }
+
+    private Logger getLogger() {
+        return Logger.getLogger(getClass().getName());
+    }
 }
diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java
index 21cb50154f..d20e4efe8b 100644
--- a/server/src/com/vaadin/ui/components/grid/Grid.java
+++ b/server/src/com/vaadin/ui/components/grid/Grid.java
@@ -44,6 +44,7 @@ import com.vaadin.shared.ui.grid.GridClientRpc;
 import com.vaadin.shared.ui.grid.GridColumnState;
 import com.vaadin.shared.ui.grid.GridServerRpc;
 import com.vaadin.shared.ui.grid.GridState;
+import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode;
 import com.vaadin.shared.ui.grid.HeightMode;
 import com.vaadin.shared.ui.grid.ScrollDestination;
 import com.vaadin.ui.AbstractComponent;
@@ -830,6 +831,18 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier {
             this.selectionModel = selectionModel;
             this.selectionModel.setGrid(this);
             this.selectionModel.reset();
+
+            if (selectionModel.getClass().equals(SingleSelectionModel.class)) {
+                getState().selectionMode = SharedSelectionMode.SINGLE;
+            } else if (selectionModel.getClass().equals(
+                    MultiSelectionModel.class)) {
+                getState().selectionMode = SharedSelectionMode.MULTI;
+            } else if (selectionModel.getClass().equals(NoSelectionModel.class)) {
+                getState().selectionMode = SharedSelectionMode.NONE;
+            } else {
+                throw new UnsupportedOperationException("Grid currently "
+                        + "supports only its own bundled selection models");
+            }
         }
     }
 
diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java
index 6ca0021817..3dcf7764ac 100644
--- a/shared/src/com/vaadin/shared/ui/grid/GridState.java
+++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java
@@ -30,6 +30,42 @@ import com.vaadin.shared.annotations.DelegateToWidget;
  */
 public class GridState extends AbstractComponentState {
 
+    /**
+     * A description of which of the three bundled SelectionModels is currently
+     * in use.
+     * 

+ * Used as a data transfer object instead of the client/server ones, because + * they don't know about each others classes. + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode + * @see com.vaadin.client.ui.grid.Grid.SelectionMode + */ + public enum SharedSelectionMode { + /** + * Representation of a single selection mode + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode#SINGLE + * @see com.vaadin.client.ui.grid.Grid.SelectionMode#SINGLE + */ + SINGLE, + + /** + * Representation of a multiselection mode + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode#MULTI + * @see com.vaadin.client.ui.grid.Grid.SelectionMode#MULTI + */ + MULTI, + + /** + * Representation of a no-selection mode + * + * @see com.vaadin.ui.components.grid.Grid.SelectionMode#NONE + * @see com.vaadin.client.ui.grid.Grid.SelectionMode#NONE + */ + NONE; + } + /** * The default value for height-by-rows for both GWT widgets * {@link com.vaadin.ui.components.grid Grid} and @@ -95,4 +131,5 @@ public class GridState extends AbstractComponentState { // instantiated just to avoid NPEs public List selectedKeys = new ArrayList(); + public SharedSelectionMode selectionMode; } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java index cfe1d0c00d..6af229bd28 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java @@ -31,6 +31,7 @@ import com.vaadin.tests.components.AbstractComponentTest; import com.vaadin.ui.components.grid.ColumnGroup; import com.vaadin.ui.components.grid.ColumnGroupRow; import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.Grid.SelectionMode; import com.vaadin.ui.components.grid.GridColumn; import com.vaadin.ui.components.grid.renderers.DateRenderer; import com.vaadin.ui.components.grid.renderers.HtmlRenderer; @@ -159,6 +160,19 @@ public class GridBasicFeatures extends AbstractComponentTest { } }, primaryStyleNames.get("v-grid")); + + LinkedHashMap selectionModes = new LinkedHashMap(); + selectionModes.put("single", SelectionMode.SINGLE); + selectionModes.put("multi", SelectionMode.MULTI); + selectionModes.put("none", SelectionMode.NONE); + createSelectAction("Selection mode", "State", selectionModes, "multi", + new Command() { + @Override + public void execute(Grid grid, SelectionMode selectionMode, + Object data) { + grid.setSelectionMode(selectionMode); + } + }); } protected void createHeaderActions() { -- cgit v1.2.3 From 07ef5c9d52c7e30ca5197fe43dbf301178c81a9d Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Tue, 8 Jul 2014 12:04:56 +0300 Subject: Removes the version number from @since in Grid classes (#13334) Change-Id: I36192c46b359b8307c5bb1faf71c3b9a20e77fb8 --- client/src/com/vaadin/client/data/AbstractRemoteDataSource.java | 2 +- client/src/com/vaadin/client/data/CacheStrategy.java | 2 +- client/src/com/vaadin/client/data/DataChangeHandler.java | 2 +- client/src/com/vaadin/client/data/DataSource.java | 2 +- client/src/com/vaadin/client/data/RpcDataSourceConnector.java | 2 +- client/src/com/vaadin/client/ui/grid/Cell.java | 1 + client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java | 2 +- client/src/com/vaadin/client/ui/grid/ColumnGroup.java | 2 +- client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java | 2 +- client/src/com/vaadin/client/ui/grid/Escalator.java | 2 +- client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java | 2 +- client/src/com/vaadin/client/ui/grid/FlyweightCell.java | 2 +- client/src/com/vaadin/client/ui/grid/FlyweightRow.java | 2 +- client/src/com/vaadin/client/ui/grid/Grid.java | 2 +- client/src/com/vaadin/client/ui/grid/GridColumn.java | 2 +- client/src/com/vaadin/client/ui/grid/GridConnector.java | 2 +- client/src/com/vaadin/client/ui/grid/PositionFunction.java | 2 +- client/src/com/vaadin/client/ui/grid/Renderer.java | 2 +- client/src/com/vaadin/client/ui/grid/Row.java | 2 +- client/src/com/vaadin/client/ui/grid/RowContainer.java | 2 +- client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java | 2 +- client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java | 2 +- client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java | 2 +- client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java | 2 +- client/src/com/vaadin/client/ui/grid/datasources/ListSorter.java | 2 +- .../vaadin/client/ui/grid/renderers/AbstractRendererConnector.java | 2 +- client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java | 2 +- client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java | 2 +- .../com/vaadin/client/ui/grid/renderers/DateRendererConnector.java | 2 +- client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java | 2 +- client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java | 2 +- .../com/vaadin/client/ui/grid/renderers/NumberRendererConnector.java | 2 +- client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java | 2 +- .../com/vaadin/client/ui/grid/renderers/TextRendererConnector.java | 2 +- .../vaadin/client/ui/grid/renderers/UnsafeHtmlRendererConnector.java | 2 +- client/src/com/vaadin/client/ui/grid/renderers/WidgetRenderer.java | 2 +- .../vaadin/client/ui/grid/selection/HasSelectionChangeHandlers.java | 2 +- .../com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java | 2 +- .../com/vaadin/client/ui/grid/selection/SelectionChangeHandler.java | 2 +- client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java | 2 +- .../src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java | 2 +- .../src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java | 2 +- .../com/vaadin/client/ui/grid/selection/SelectionModelSingle.java | 2 +- client/src/com/vaadin/client/ui/grid/sort/Sort.java | 2 +- client/src/com/vaadin/client/ui/grid/sort/SortEvent.java | 2 +- client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java | 2 +- client/src/com/vaadin/client/ui/grid/sort/SortOrder.java | 2 +- client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java | 5 ----- server/src/com/vaadin/data/RpcDataProviderExtension.java | 2 +- server/src/com/vaadin/ui/components/grid/AbstractRenderer.java | 2 +- server/src/com/vaadin/ui/components/grid/ColumnGroup.java | 2 +- server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java | 2 +- server/src/com/vaadin/ui/components/grid/Grid.java | 2 +- server/src/com/vaadin/ui/components/grid/GridColumn.java | 2 +- server/src/com/vaadin/ui/components/grid/Renderer.java | 2 +- server/src/com/vaadin/ui/components/grid/renderers/DateRenderer.java | 2 +- server/src/com/vaadin/ui/components/grid/renderers/HtmlRenderer.java | 2 +- .../src/com/vaadin/ui/components/grid/renderers/NumberRenderer.java | 2 +- server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java | 2 +- .../vaadin/ui/components/grid/selection/AbstractSelectionModel.java | 2 +- .../com/vaadin/ui/components/grid/selection/MultiSelectionModel.java | 2 +- .../com/vaadin/ui/components/grid/selection/NoSelectionModel.java | 2 +- .../vaadin/ui/components/grid/selection/SelectionChangeEvent.java | 2 +- .../vaadin/ui/components/grid/selection/SelectionChangeListener.java | 2 +- .../vaadin/ui/components/grid/selection/SelectionChangeNotifier.java | 2 +- .../src/com/vaadin/ui/components/grid/selection/SelectionModel.java | 2 +- .../vaadin/ui/components/grid/selection/SingleSelectionModel.java | 2 +- shared/src/com/vaadin/shared/data/DataProviderRpc.java | 2 +- shared/src/com/vaadin/shared/data/DataProviderState.java | 2 +- shared/src/com/vaadin/shared/data/DataRequestRpc.java | 2 +- shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridColumnState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridConstants.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/HeightMode.java | 2 +- shared/src/com/vaadin/shared/ui/grid/Range.java | 2 +- shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java | 2 +- shared/src/com/vaadin/shared/ui/grid/SortDirection.java | 2 +- uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java | 2 +- uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java | 2 +- uitest/src/com/vaadin/tests/components/grid/GridElement.java | 2 +- .../com/vaadin/tests/widgetset/client/grid/TestGridConnector.java | 2 +- uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java | 2 +- uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java | 2 +- 87 files changed, 86 insertions(+), 90 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index d6a609a3c8..bec6e330bc 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -35,7 +35,7 @@ import com.vaadin.shared.ui.grid.Range; * {@link #setRowData(int, List)}. {@link #setEstimatedSize(int)} should be used * based on estimations of how many rows are available. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param * the row type diff --git a/client/src/com/vaadin/client/data/CacheStrategy.java b/client/src/com/vaadin/client/data/CacheStrategy.java index 79ce537314..3448659e61 100644 --- a/client/src/com/vaadin/client/data/CacheStrategy.java +++ b/client/src/com/vaadin/client/data/CacheStrategy.java @@ -22,7 +22,7 @@ import com.vaadin.shared.ui.grid.Range; * Determines what data an {@link AbstractRemoteDataSource} should fetch and * keep cached. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface CacheStrategy { diff --git a/client/src/com/vaadin/client/data/DataChangeHandler.java b/client/src/com/vaadin/client/data/DataChangeHandler.java index 52065ee4d7..9553ef53c1 100644 --- a/client/src/com/vaadin/client/data/DataChangeHandler.java +++ b/client/src/com/vaadin/client/data/DataChangeHandler.java @@ -20,7 +20,7 @@ package com.vaadin.client.data; * Callback interface used by {@link DataSource} to inform its user about * updates to the data. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface DataChangeHandler { diff --git a/client/src/com/vaadin/client/data/DataSource.java b/client/src/com/vaadin/client/data/DataSource.java index 695a2a7c2f..33f60eadcc 100644 --- a/client/src/com/vaadin/client/data/DataSource.java +++ b/client/src/com/vaadin/client/data/DataSource.java @@ -21,7 +21,7 @@ package com.vaadin.client.data; * items (e.g. rows) of a specified type. The data source is a lazy view into a * larger data set. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param * the row type diff --git a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java index 3761ea92df..0bee9dc34d 100644 --- a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java @@ -39,7 +39,7 @@ import com.vaadin.shared.ui.grid.Range; * connector type. This will be changed once framework support for something * more flexible has been implemented. * - * @since 7.4 + * @since * @author Vaadin Ltd */ @Connect(com.vaadin.data.RpcDataProviderExtension.class) diff --git a/client/src/com/vaadin/client/ui/grid/Cell.java b/client/src/com/vaadin/client/ui/grid/Cell.java index 33495ebf87..45a7eef554 100644 --- a/client/src/com/vaadin/client/ui/grid/Cell.java +++ b/client/src/com/vaadin/client/ui/grid/Cell.java @@ -24,6 +24,7 @@ import com.google.gwt.dom.client.Element; * class is still under debate and the API is not final. Improve the description * when API has been finalized. * + * @since * @author Vaadin Ltd */ public class Cell { diff --git a/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java b/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java index c8d9a750bc..e69b5e7a48 100644 --- a/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java +++ b/client/src/com/vaadin/client/ui/grid/ColumnConfiguration.java @@ -19,7 +19,7 @@ package com.vaadin.client.ui.grid; /** * A representation of the columns in an instance of {@link Escalator}. * - * @since 7.4 + * @since * @author Vaadin Ltd * @see Escalator#getColumnConfiguration() */ diff --git a/client/src/com/vaadin/client/ui/grid/ColumnGroup.java b/client/src/com/vaadin/client/ui/grid/ColumnGroup.java index c425d1b07c..c58f90f10b 100644 --- a/client/src/com/vaadin/client/ui/grid/ColumnGroup.java +++ b/client/src/com/vaadin/client/ui/grid/ColumnGroup.java @@ -31,7 +31,7 @@ import com.vaadin.client.ui.grid.renderers.TextRenderer; * @param * The row type of the grid. The row type is the POJO type from where * the data is retrieved into the column cells. - * @since 7.4 + * @since * @author Vaadin Ltd */ public class ColumnGroup { diff --git a/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java b/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java index 10290a0639..90e7c1f887 100644 --- a/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java +++ b/client/src/com/vaadin/client/ui/grid/ColumnGroupRow.java @@ -30,7 +30,7 @@ import java.util.Set; * * @param * Row type - * @since 7.4 + * @since * @author Vaadin Ltd */ public class ColumnGroupRow { diff --git a/client/src/com/vaadin/client/ui/grid/Escalator.java b/client/src/com/vaadin/client/ui/grid/Escalator.java index c8feb6d18e..271e2509b7 100644 --- a/client/src/com/vaadin/client/ui/grid/Escalator.java +++ b/client/src/com/vaadin/client/ui/grid/Escalator.java @@ -230,7 +230,7 @@ abstract class JsniWorkaround { * A low-level table-like widget that features a scrolling virtual viewport and * lazily generated rows. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class Escalator extends Widget { diff --git a/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java b/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java index 481ddf707e..aae6b63d20 100644 --- a/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java +++ b/client/src/com/vaadin/client/ui/grid/EscalatorUpdater.java @@ -27,7 +27,7 @@ package com.vaadin.client.ui.grid; * This has a similar function to {@link Grid Grid's} {@link Renderer Renderers} * , although they operate on different abstraction levels. * - * @since 7.4 + * @since * @author Vaadin Ltd * @see RowContainer#setEscalatorUpdater(EscalatorUpdater) * @see Escalator#getHeader() diff --git a/client/src/com/vaadin/client/ui/grid/FlyweightCell.java b/client/src/com/vaadin/client/ui/grid/FlyweightCell.java index 3364f71bd1..17301214c8 100644 --- a/client/src/com/vaadin/client/ui/grid/FlyweightCell.java +++ b/client/src/com/vaadin/client/ui/grid/FlyweightCell.java @@ -32,7 +32,7 @@ import com.vaadin.client.ui.grid.FlyweightRow.CellIterator; * and so should not be stored anywhere outside of the method providing these * instances. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class FlyweightCell { diff --git a/client/src/com/vaadin/client/ui/grid/FlyweightRow.java b/client/src/com/vaadin/client/ui/grid/FlyweightRow.java index f89e2eed52..a5447715e5 100644 --- a/client/src/com/vaadin/client/ui/grid/FlyweightRow.java +++ b/client/src/com/vaadin/client/ui/grid/FlyweightRow.java @@ -29,7 +29,7 @@ import com.google.gwt.dom.client.Node; * There is only one instance per Escalator. This is designed to be re-used when * rendering rows. * - * @since 7.4 + * @since * @author Vaadin Ltd * @see Escalator.AbstractRowContainer#refreshRow(Node, int) */ diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 4bd07f1909..d1541ba505 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -94,7 +94,7 @@ import com.vaadin.shared.util.SharedUtil; * @param * The row type of the grid. The row type is the POJO type from where * the data is retrieved into the column cells. - * @since 7.4 + * @since * @author Vaadin Ltd */ public class Grid extends Composite implements diff --git a/client/src/com/vaadin/client/ui/grid/GridColumn.java b/client/src/com/vaadin/client/ui/grid/GridColumn.java index 47acd42be9..69be2d5532 100644 --- a/client/src/com/vaadin/client/ui/grid/GridColumn.java +++ b/client/src/com/vaadin/client/ui/grid/GridColumn.java @@ -24,7 +24,7 @@ package com.vaadin.client.ui.grid; * @param * The row type * - * @since 7.4 + * @since * @author Vaadin Ltd */ public abstract class GridColumn extends Grid.AbstractGridColumn { diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 9ba1e9f038..dd27f7f747 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -60,7 +60,7 @@ import com.vaadin.shared.ui.grid.ScrollDestination; * at {@link com.vaadin.shared.data.DataProviderRpc#setRowData(int, List) * DataProviderRpc.setRowData(int, List)}. * - * @since 7.4 + * @since * @author Vaadin Ltd */ @Connect(com.vaadin.ui.components.grid.Grid.class) diff --git a/client/src/com/vaadin/client/ui/grid/PositionFunction.java b/client/src/com/vaadin/client/ui/grid/PositionFunction.java index 65197126b2..4db5efd0fc 100644 --- a/client/src/com/vaadin/client/ui/grid/PositionFunction.java +++ b/client/src/com/vaadin/client/ui/grid/PositionFunction.java @@ -22,7 +22,7 @@ import com.google.gwt.dom.client.Style.Unit; /** * A functional interface that can be used for positioning elements in the DOM. * - * @since 7.4 + * @since * @author Vaadin Ltd */ interface PositionFunction { diff --git a/client/src/com/vaadin/client/ui/grid/Renderer.java b/client/src/com/vaadin/client/ui/grid/Renderer.java index e312a9da20..787a145326 100644 --- a/client/src/com/vaadin/client/ui/grid/Renderer.java +++ b/client/src/com/vaadin/client/ui/grid/Renderer.java @@ -26,7 +26,7 @@ package com.vaadin.client.ui.grid; * @param * The column type * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface Renderer { diff --git a/client/src/com/vaadin/client/ui/grid/Row.java b/client/src/com/vaadin/client/ui/grid/Row.java index a7cbeadd90..2a176f7e82 100644 --- a/client/src/com/vaadin/client/ui/grid/Row.java +++ b/client/src/com/vaadin/client/ui/grid/Row.java @@ -21,7 +21,7 @@ import com.google.gwt.dom.client.Element; /** * A representation of a row in an {@link Escalator}. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface Row { diff --git a/client/src/com/vaadin/client/ui/grid/RowContainer.java b/client/src/com/vaadin/client/ui/grid/RowContainer.java index b0b580d665..d0fb0db103 100644 --- a/client/src/com/vaadin/client/ui/grid/RowContainer.java +++ b/client/src/com/vaadin/client/ui/grid/RowContainer.java @@ -22,7 +22,7 @@ import com.google.gwt.dom.client.Element; * A representation of the rows in each of the sections (header, body and * footer) in an {@link Escalator}. * - * @since 7.4 + * @since * @author Vaadin Ltd * @see Escalator#getHeader() * @see Escalator#getBody() diff --git a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java b/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java index 8a3dc6ace8..c5c5e45ca8 100644 --- a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java +++ b/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeEvent.java @@ -21,7 +21,7 @@ import com.google.gwt.event.shared.GwtEvent; /** * Event fired when the range of visible rows changes e.g. because of scrolling. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class RowVisibilityChangeEvent extends diff --git a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java b/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java index 7e71fb5bdd..6aa165fe04 100644 --- a/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java +++ b/client/src/com/vaadin/client/ui/grid/RowVisibilityChangeHandler.java @@ -22,7 +22,7 @@ import com.google.gwt.event.shared.EventHandler; * Event handler that gets notified when the range of visible rows changes e.g. * because of scrolling. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface RowVisibilityChangeHandler extends EventHandler { diff --git a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java index efd64fff84..60b6fa27a3 100644 --- a/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java +++ b/client/src/com/vaadin/client/ui/grid/ScrollbarBundle.java @@ -29,7 +29,7 @@ import com.google.gwt.user.client.DOM; * An element-like bundle representing a configurable and visual scrollbar in * one axis. * - * @since 7.4 + * @since * @author Vaadin Ltd * @see VerticalScrollbarBundle * @see HorizontalScrollbarBundle diff --git a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java index 97b358a299..fc76955410 100644 --- a/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java +++ b/client/src/com/vaadin/client/ui/grid/datasources/ListDataSource.java @@ -49,7 +49,7 @@ import com.vaadin.shared.util.SharedUtil; * ds.asList().addAll(Arrays.asList(5, 6, 7)); * * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class ListDataSource implements DataSource { diff --git a/client/src/com/vaadin/client/ui/grid/datasources/ListSorter.java b/client/src/com/vaadin/client/ui/grid/datasources/ListSorter.java index 81ea3efd92..9e643825e9 100644 --- a/client/src/com/vaadin/client/ui/grid/datasources/ListSorter.java +++ b/client/src/com/vaadin/client/ui/grid/datasources/ListSorter.java @@ -33,7 +33,7 @@ import com.vaadin.shared.ui.grid.SortDirection; * Provides sorting facility from Grid for the {@link ListDataSource} in-memory * data source. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param * Grid row data type diff --git a/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java index e7cbd5bcd5..8a8712372f 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/AbstractRendererConnector.java @@ -36,7 +36,7 @@ import com.vaadin.client.ui.grid.Renderer; * {@link com.vaadin.shared.communication.SharedState SharedState} and no RPC * interfaces. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public abstract class AbstractRendererConnector extends diff --git a/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java index 289f3809be..6a1f1c3041 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/ComplexRenderer.java @@ -35,7 +35,7 @@ import com.vaadin.client.ui.grid.Renderer; * Also provides a helper method for hiding the cell contents by overriding * {@link #setContentVisible(FlyweightCell, boolean)} * - * @since 7.4 + * @since * @author Vaadin Ltd */ public abstract class ComplexRenderer implements Renderer { diff --git a/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java index 9d18ae9256..fc7d3ac833 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/DateRenderer.java @@ -26,7 +26,7 @@ import com.vaadin.client.ui.grid.Renderer; /** * A renderer for rendering dates into cells * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class DateRenderer implements Renderer { diff --git a/client/src/com/vaadin/client/ui/grid/renderers/DateRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/DateRendererConnector.java index 71c7ff78e3..52ae7d9b6b 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/DateRendererConnector.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/DateRendererConnector.java @@ -25,7 +25,7 @@ import com.vaadin.shared.ui.Connect; * string, and displayed as-is on the client side. This is to be able to support * the server's locale. * - * @since 7.4 + * @since * @author Vaadin Ltd */ @Connect(com.vaadin.ui.components.grid.renderers.DateRenderer.class) diff --git a/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java index 6cb9603d3c..36c5d2bb0f 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/HtmlRenderer.java @@ -28,7 +28,7 @@ import com.vaadin.client.ui.grid.Renderer; * contract. For more information see * {@link SafeHtmlUtils#fromSafeConstant(String)}. * - * @since 7.4 + * @since * @author Vaadin Ltd * @see SafeHtmlUtils#fromSafeConstant(String) */ diff --git a/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java index b1bf7083a5..aa23bc2370 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/NumberRenderer.java @@ -24,7 +24,7 @@ import com.vaadin.client.ui.grid.Renderer; * default uses the default number format returned by * {@link NumberFormat#getDecimalFormat()}. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param * The number type to render. diff --git a/client/src/com/vaadin/client/ui/grid/renderers/NumberRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/NumberRendererConnector.java index c698144d30..cba29d0690 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/NumberRendererConnector.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/NumberRendererConnector.java @@ -26,7 +26,7 @@ import com.vaadin.shared.ui.Connect; * string, and displayed as-is on the client side. This is to be able to support * the server's locale. * - * @since 7.4 + * @since * @author Vaadin Ltd */ @Connect(com.vaadin.ui.components.grid.renderers.NumberRenderer.class) diff --git a/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java index 36ffbae22d..d2f3520c43 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/TextRenderer.java @@ -21,7 +21,7 @@ import com.vaadin.client.ui.grid.Renderer; /** * Renderer that renders text into a cell. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class TextRenderer implements Renderer { diff --git a/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java index 76fce7b4b6..18cc84cd34 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/TextRendererConnector.java @@ -20,7 +20,7 @@ import com.vaadin.shared.ui.Connect; /** * A connector for {@link TextRenderer}. * - * @since 7.4 + * @since * @author Vaadin Ltd */ @Connect(com.vaadin.ui.components.grid.renderers.TextRenderer.class) diff --git a/client/src/com/vaadin/client/ui/grid/renderers/UnsafeHtmlRendererConnector.java b/client/src/com/vaadin/client/ui/grid/renderers/UnsafeHtmlRendererConnector.java index c0dcf0505d..1816ac974a 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/UnsafeHtmlRendererConnector.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/UnsafeHtmlRendererConnector.java @@ -22,7 +22,7 @@ import com.vaadin.shared.ui.Connect; /** * A connector for {@link UnsafeHtmlRenderer} * - * @since 7.4 + * @since * @author Vaadin Ltd */ @Connect(com.vaadin.ui.components.grid.renderers.HtmlRenderer.class) diff --git a/client/src/com/vaadin/client/ui/grid/renderers/WidgetRenderer.java b/client/src/com/vaadin/client/ui/grid/renderers/WidgetRenderer.java index 0937e8c1f2..b7cd72600a 100644 --- a/client/src/com/vaadin/client/ui/grid/renderers/WidgetRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/renderers/WidgetRenderer.java @@ -22,7 +22,7 @@ import com.vaadin.client.ui.grid.FlyweightCell; /** * A renderer for rendering widgets into cells. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param * the row data type diff --git a/client/src/com/vaadin/client/ui/grid/selection/HasSelectionChangeHandlers.java b/client/src/com/vaadin/client/ui/grid/selection/HasSelectionChangeHandlers.java index c531265590..342c426b55 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/HasSelectionChangeHandlers.java +++ b/client/src/com/vaadin/client/ui/grid/selection/HasSelectionChangeHandlers.java @@ -21,7 +21,7 @@ import com.google.gwt.event.shared.HandlerRegistration; * Marker interface for widgets that fires selection change events. * * @author Vaadin Ltd - * @since 7.4 + * @since */ public interface HasSelectionChangeHandlers { diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java index 86d9c97c36..5c5afef065 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java @@ -26,7 +26,7 @@ import com.vaadin.client.ui.grid.Grid; /** * Event object describing a change in Grid row selection state. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class SelectionChangeEvent extends GwtEvent { diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeHandler.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeHandler.java index aa61bdecdf..a469f5af1f 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeHandler.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeHandler.java @@ -20,7 +20,7 @@ import com.google.gwt.event.shared.EventHandler; /** * Handler for {@link SelectionChangeEvent}s. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param * The row data type diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java index 989a8946c7..cc2f2b06d9 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java @@ -27,7 +27,7 @@ import com.vaadin.client.ui.grid.Renderer; * dispatching events when the selection state changes. * * @author Vaadin Ltd - * @since 7.4 + * @since * @param * Grid's row type */ diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java index 6ebd7f4044..6f2896b43a 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java @@ -28,7 +28,7 @@ import com.vaadin.client.ui.grid.Renderer; * Multi-row selection model. * * @author Vaadin Ltd - * @since 7.4 + * @since */ public class SelectionModelMulti extends AbstractRowHandleSelectionModel implements SelectionModel.Multi { diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java index 59bf248032..8192237da0 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelNone.java @@ -26,7 +26,7 @@ import com.vaadin.client.ui.grid.Renderer; * No-row selection model. * * @author Vaadin Ltd - * @since 7.4 + * @since */ public class SelectionModelNone extends AbstractRowHandleSelectionModel implements SelectionModel.None { diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java index 4ef792f1c7..2647ae9c56 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java @@ -26,7 +26,7 @@ import com.vaadin.client.ui.grid.Renderer; * Single-row selection model. * * @author Vaadin Ltd - * @since 7.4 + * @since */ public class SelectionModelSingle extends AbstractRowHandleSelectionModel implements SelectionModel.Single { diff --git a/client/src/com/vaadin/client/ui/grid/sort/Sort.java b/client/src/com/vaadin/client/ui/grid/sort/Sort.java index 64fec445ae..00658c4375 100644 --- a/client/src/com/vaadin/client/ui/grid/sort/Sort.java +++ b/client/src/com/vaadin/client/ui/grid/sort/Sort.java @@ -24,7 +24,7 @@ import com.vaadin.shared.ui.grid.SortDirection; /** * Fluid Sort descriptor object. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param T * grid data type diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java b/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java index d39cdfc4f2..baa12ae224 100644 --- a/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java +++ b/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java @@ -25,7 +25,7 @@ import com.vaadin.client.ui.grid.Grid; * A sort event, fired by the Grid when it needs its data source to provide data * sorted in a specific manner. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class SortEvent extends GwtEvent> { diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java b/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java index 8895b43631..57e7fc2ead 100644 --- a/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java +++ b/client/src/com/vaadin/client/ui/grid/sort/SortEventHandler.java @@ -21,7 +21,7 @@ import com.google.gwt.event.shared.EventHandler; * Handler for a Grid sort event, called when the Grid needs its data source to * provide data sorted in a specific manner. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface SortEventHandler extends EventHandler { diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java b/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java index 09b1a48afa..34279bdc04 100644 --- a/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java +++ b/client/src/com/vaadin/client/ui/grid/sort/SortOrder.java @@ -21,7 +21,7 @@ import com.vaadin.shared.ui.grid.SortDirection; /** * Sort order descriptor. Contains column and direction references. * - * @since 7.4 + * @since * @author Vaadin Ltd * @param T * grid data type diff --git a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java index 823eb224ea..55a2b56ee2 100644 --- a/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java +++ b/client/tests/src/com/vaadin/client/ui/grid/ListDataSourceTest.java @@ -27,11 +27,6 @@ import org.junit.Test; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.ui.grid.datasources.ListDataSource; -/** - * - * @since 7.2 - * @author Vaadin Ltd - */ public class ListDataSourceTest { @Test diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 9768950621..e6b5050d90 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -61,7 +61,7 @@ import com.vaadin.ui.components.grid.Renderer; * This will be changed once framework support for something more flexible has * been implemented. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class RpcDataProviderExtension extends AbstractExtension { diff --git a/server/src/com/vaadin/ui/components/grid/AbstractRenderer.java b/server/src/com/vaadin/ui/components/grid/AbstractRenderer.java index 2f0a7e7ebb..d1cf77c24b 100644 --- a/server/src/com/vaadin/ui/components/grid/AbstractRenderer.java +++ b/server/src/com/vaadin/ui/components/grid/AbstractRenderer.java @@ -28,7 +28,7 @@ import com.vaadin.server.AbstractExtension; * @param * the type this renderer knows how to present * - * @since 7.4 + * @since * @author Vaadin Ltd */ public abstract class AbstractRenderer extends AbstractExtension implements diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java b/server/src/com/vaadin/ui/components/grid/ColumnGroup.java index 92cfd946a8..ec676dfb87 100644 --- a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java +++ b/server/src/com/vaadin/ui/components/grid/ColumnGroup.java @@ -27,7 +27,7 @@ import com.vaadin.shared.ui.grid.ColumnGroupState; * Column groups are used to group columns together for adding common auxiliary * headers and footers. Columns groups are added to {@link ColumnGroupRow}'s. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class ColumnGroup implements Serializable { diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java b/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java index 5e1ba1100f..a497b5a8a8 100644 --- a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java +++ b/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java @@ -30,7 +30,7 @@ import com.vaadin.shared.ui.grid.ColumnGroupState; * A column group row represents an auxiliary header or footer row added to the * grid. A column group row includes column groups that group columns together. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class ColumnGroupRow implements Serializable { diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 287bd1ddfd..bb5ff23da1 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -72,7 +72,7 @@ import com.vaadin.util.ReflectTools; *

Auxiliary headers and footers

TODO To be revised when column * grouping is implemented. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class Grid extends AbstractComponent implements SelectionChangeNotifier { diff --git a/server/src/com/vaadin/ui/components/grid/GridColumn.java b/server/src/com/vaadin/ui/components/grid/GridColumn.java index cadd621948..43b2003e35 100644 --- a/server/src/com/vaadin/ui/components/grid/GridColumn.java +++ b/server/src/com/vaadin/ui/components/grid/GridColumn.java @@ -29,7 +29,7 @@ import com.vaadin.ui.components.grid.renderers.TextRenderer; * A column in the grid. Can be obtained by calling * {@link Grid#getColumn(Object propertyId)}. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class GridColumn implements Serializable { diff --git a/server/src/com/vaadin/ui/components/grid/Renderer.java b/server/src/com/vaadin/ui/components/grid/Renderer.java index f3d502eb85..b9074fb9f7 100644 --- a/server/src/com/vaadin/ui/components/grid/Renderer.java +++ b/server/src/com/vaadin/ui/components/grid/Renderer.java @@ -28,7 +28,7 @@ import com.vaadin.server.Extension; * @param * the type this renderer knows how to present * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface Renderer extends Extension { diff --git a/server/src/com/vaadin/ui/components/grid/renderers/DateRenderer.java b/server/src/com/vaadin/ui/components/grid/renderers/DateRenderer.java index e7280f76aa..736b61d9e2 100644 --- a/server/src/com/vaadin/ui/components/grid/renderers/DateRenderer.java +++ b/server/src/com/vaadin/ui/components/grid/renderers/DateRenderer.java @@ -24,7 +24,7 @@ import com.vaadin.ui.components.grid.AbstractRenderer; /** * A renderer for presenting date values. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class DateRenderer extends AbstractRenderer { diff --git a/server/src/com/vaadin/ui/components/grid/renderers/HtmlRenderer.java b/server/src/com/vaadin/ui/components/grid/renderers/HtmlRenderer.java index 9c1fbf51d8..6439608c20 100644 --- a/server/src/com/vaadin/ui/components/grid/renderers/HtmlRenderer.java +++ b/server/src/com/vaadin/ui/components/grid/renderers/HtmlRenderer.java @@ -20,7 +20,7 @@ import com.vaadin.ui.components.grid.AbstractRenderer; /** * A renderer for presenting HTML content. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class HtmlRenderer extends AbstractRenderer { diff --git a/server/src/com/vaadin/ui/components/grid/renderers/NumberRenderer.java b/server/src/com/vaadin/ui/components/grid/renderers/NumberRenderer.java index d071e592ba..0d1c98d6dc 100644 --- a/server/src/com/vaadin/ui/components/grid/renderers/NumberRenderer.java +++ b/server/src/com/vaadin/ui/components/grid/renderers/NumberRenderer.java @@ -23,7 +23,7 @@ import com.vaadin.ui.components.grid.AbstractRenderer; /** * A renderer for presenting number values. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class NumberRenderer extends AbstractRenderer { diff --git a/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java b/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java index cdef4e17c8..61348a9e49 100644 --- a/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java +++ b/server/src/com/vaadin/ui/components/grid/renderers/TextRenderer.java @@ -20,7 +20,7 @@ import com.vaadin.ui.components.grid.AbstractRenderer; /** * A renderer for presenting simple plain-text string values. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class TextRenderer extends AbstractRenderer { diff --git a/server/src/com/vaadin/ui/components/grid/selection/AbstractSelectionModel.java b/server/src/com/vaadin/ui/components/grid/selection/AbstractSelectionModel.java index 246ef599b3..e153b8a4e4 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/AbstractSelectionModel.java +++ b/server/src/com/vaadin/ui/components/grid/selection/AbstractSelectionModel.java @@ -25,7 +25,7 @@ import com.vaadin.ui.components.grid.Grid; * A base class for SelectionModels that contains some of the logic that is * reusable. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public abstract class AbstractSelectionModel implements SelectionModel { diff --git a/server/src/com/vaadin/ui/components/grid/selection/MultiSelectionModel.java b/server/src/com/vaadin/ui/components/grid/selection/MultiSelectionModel.java index a196d6ea8c..602e5ca169 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/MultiSelectionModel.java +++ b/server/src/com/vaadin/ui/components/grid/selection/MultiSelectionModel.java @@ -25,7 +25,7 @@ import com.vaadin.data.Container.Indexed; /** * A default implementation of a {@link SelectionModel.Multi} * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class MultiSelectionModel extends AbstractSelectionModel implements diff --git a/server/src/com/vaadin/ui/components/grid/selection/NoSelectionModel.java b/server/src/com/vaadin/ui/components/grid/selection/NoSelectionModel.java index ff5573b522..89c31398ea 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/NoSelectionModel.java +++ b/server/src/com/vaadin/ui/components/grid/selection/NoSelectionModel.java @@ -23,7 +23,7 @@ import com.vaadin.ui.components.grid.Grid; /** * A default implementation for a {@link SelectionModel.None} * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class NoSelectionModel implements SelectionModel.None { diff --git a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java index f0e25405cc..af6a37dfde 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java +++ b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeEvent.java @@ -27,7 +27,7 @@ import com.vaadin.ui.components.grid.Grid; * An event that specifies what in a selection has changed, and where the * selection took place. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class SelectionChangeEvent extends EventObject { diff --git a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeListener.java b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeListener.java index 18fb53e19c..0d10e8c74d 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeListener.java +++ b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeListener.java @@ -21,7 +21,7 @@ import java.io.Serializable; * The listener interface for receiving {@link SelectionChangeEvent * SelectionChangeEvents}. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface SelectionChangeListener extends Serializable { diff --git a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeNotifier.java b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeNotifier.java index 1a3f0920db..40cef965dd 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeNotifier.java +++ b/server/src/com/vaadin/ui/components/grid/selection/SelectionChangeNotifier.java @@ -21,7 +21,7 @@ import java.io.Serializable; * The interface for adding and removing listeners for * {@link SelectionChangeEvent SelectionChangeEvents}. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface SelectionChangeNotifier extends Serializable { diff --git a/server/src/com/vaadin/ui/components/grid/selection/SelectionModel.java b/server/src/com/vaadin/ui/components/grid/selection/SelectionModel.java index 2862b8188c..60bb130ab1 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/SelectionModel.java +++ b/server/src/com/vaadin/ui/components/grid/selection/SelectionModel.java @@ -23,7 +23,7 @@ import com.vaadin.ui.components.grid.Grid; /** * The server-side interface that controls Grid's selection state. * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface SelectionModel extends Serializable { diff --git a/server/src/com/vaadin/ui/components/grid/selection/SingleSelectionModel.java b/server/src/com/vaadin/ui/components/grid/selection/SingleSelectionModel.java index 6eaf8d9883..0f6e8a296d 100644 --- a/server/src/com/vaadin/ui/components/grid/selection/SingleSelectionModel.java +++ b/server/src/com/vaadin/ui/components/grid/selection/SingleSelectionModel.java @@ -21,7 +21,7 @@ import java.util.Collections; /** * A default implementation of a {@link SelectionModel.Single} * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class SingleSelectionModel extends AbstractSelectionModel implements diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index 43469914e5..bfd9505c04 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -21,7 +21,7 @@ import com.vaadin.shared.communication.ClientRpc; /** * RPC interface used for pushing container data to the client. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public interface DataProviderRpc extends ClientRpc { diff --git a/shared/src/com/vaadin/shared/data/DataProviderState.java b/shared/src/com/vaadin/shared/data/DataProviderState.java index 2eabe0b0e1..13331c2a64 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderState.java +++ b/shared/src/com/vaadin/shared/data/DataProviderState.java @@ -21,7 +21,7 @@ import com.vaadin.shared.communication.SharedState; /** * Shared state used by client-side data sources. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public class DataProviderState extends SharedState { diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java index b2a3e6d2ba..80f320e356 100644 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -21,7 +21,7 @@ import com.vaadin.shared.communication.ServerRpc; /** * RPC interface used for requesting container data to the client. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public interface DataRequestRpc extends ServerRpc { diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java index a8e0f87457..d3d5ea2495 100644 --- a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java +++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java @@ -23,7 +23,7 @@ import java.util.List; /** * The column group row data shared between the server and client * - * @since 7.2 + * @since * @author Vaadin Ltd */ public class ColumnGroupRowState implements Serializable { diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java index 3992b6611f..8c2bde851b 100644 --- a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java +++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java @@ -23,7 +23,7 @@ import java.util.List; /** * The column group data shared between the server and the client * - * @since 7.2 + * @since * @author Vaadin Ltd */ public class ColumnGroupState implements Serializable { diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java index 00cc93d371..24a9996d40 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java @@ -20,7 +20,7 @@ import com.vaadin.shared.communication.ClientRpc; /** * Server-to-client RPC interface for the Grid component. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public interface GridClientRpc extends ClientRpc { diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java index d1df08c294..b9bae35db6 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java @@ -23,7 +23,7 @@ import com.vaadin.shared.Connector; * Column state DTO for transferring column properties from the server to the * client * - * @since 7.2 + * @since * @author Vaadin Ltd */ public class GridColumnState implements Serializable { diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java index 8b264bf426..b098946346 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -21,7 +21,7 @@ import java.io.Serializable; * Container class for common constants and default values used by the Grid * component. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public final class GridConstants implements Serializable { diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java index b763174e53..eec7b39482 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -22,7 +22,7 @@ import com.vaadin.shared.communication.ServerRpc; /** * Client-to-server RPC interface for the Grid component * - * @since 7.4 + * @since * @author Vaadin Ltd */ public interface GridServerRpc extends ServerRpc { diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 3dcf7764ac..f71fe0929a 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -25,7 +25,7 @@ import com.vaadin.shared.annotations.DelegateToWidget; /** * The shared state for the {@link com.vaadin.ui.components.grid.Grid} component * - * @since 7.2 + * @since * @author Vaadin Ltd */ public class GridState extends AbstractComponentState { diff --git a/shared/src/com/vaadin/shared/ui/grid/HeightMode.java b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java index 0146e53e73..09c46b039e 100644 --- a/shared/src/com/vaadin/shared/ui/grid/HeightMode.java +++ b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java @@ -21,7 +21,7 @@ package com.vaadin.shared.ui.grid; * {@link com.vaadin.ui.components.grid.Grid server}) / * {@link com.vaadin.client.ui.grid.Escalator Escalator}. * - * @since 7.2 + * @since * @author Vaadin Ltd * @see com.vaadin.client.ui.grid.Grid#setHeightMode(HeightMode) * @see com.vaadin.ui.components.grid.Grid#setHeightMode(HeightMode) diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java index c28502256c..a1d4d86103 100644 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -27,7 +27,7 @@ import java.io.Serializable; * The range is considered {@link #isEmpty() empty} if the start is the same as * the end. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public final class Range implements Serializable { diff --git a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java index decc2fab5f..5fd69de612 100644 --- a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java +++ b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java @@ -19,7 +19,7 @@ package com.vaadin.shared.ui.grid; * Enumeration, specifying the destinations that are supported when scrolling * rows or columns into view. * - * @since 7.2 + * @since * @author Vaadin Ltd */ public enum ScrollDestination { diff --git a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java index 3a1828992e..1a7c29f8a7 100644 --- a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java +++ b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java @@ -18,7 +18,7 @@ package com.vaadin.shared.ui.grid; /** * Describes sorting direction for a Grid column * - * @since 7.4 + * @since * @author Vaadin Ltd */ public enum SortDirection { diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java index 995188db5c..66eb9ec2d6 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeatures.java @@ -40,7 +40,7 @@ import com.vaadin.ui.components.grid.renderers.NumberRenderer; /** * Tests the basic features like columns, footers and headers * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class GridBasicFeatures extends AbstractComponentTest { diff --git a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java index bea941d6a3..e3d3c8c01a 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridClientRenderers.java @@ -36,7 +36,7 @@ import com.vaadin.tests.widgetset.server.grid.GridClientColumnRenderers; /** * Tests Grid client side renderers * - * @since 7.4 + * @since * @author Vaadin Ltd */ public class GridClientRenderers extends MultiBrowserTest { diff --git a/uitest/src/com/vaadin/tests/components/grid/GridElement.java b/uitest/src/com/vaadin/tests/components/grid/GridElement.java index f743c553d9..091c9db1ce 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridElement.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridElement.java @@ -27,7 +27,7 @@ import com.vaadin.testbench.elements.ServerClass; /** * TestBench Element API for Grid * - * @since 7.4 + * @since * @author Vaadin Ltd */ @ServerClass("com.vaadin.ui.components.grid.Grid") diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java index e2d88c57f2..6dbff5ca66 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridConnector.java @@ -22,7 +22,7 @@ import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.tests.widgetset.server.grid.TestGrid; /** - * @since 7.2 + * @since * @author Vaadin Ltd */ @Connect(TestGrid.class) diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java index 73d6ba311c..ecbc59552b 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/TestGridState.java @@ -18,7 +18,7 @@ package com.vaadin.tests.widgetset.client.grid; import com.vaadin.shared.AbstractComponentState; /** - * @since 7.2 + * @since * @author Vaadin Ltd */ public class TestGridState extends AbstractComponentState { diff --git a/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java b/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java index 4e218ebba1..0dbb60359d 100644 --- a/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java +++ b/uitest/src/com/vaadin/tests/widgetset/server/grid/TestGrid.java @@ -20,7 +20,7 @@ import com.vaadin.tests.widgetset.client.grid.TestGridState; import com.vaadin.ui.AbstractComponent; /** - * @since 7.2 + * @since * @author Vaadin Ltd */ public class TestGrid extends AbstractComponent { -- cgit v1.2.3 From c3026bec50d371d9fe172f76fd11528e95ffa88e Mon Sep 17 00:00:00 2001 From: Patrik Lindström Date: Mon, 23 Jun 2014 10:58:54 +0300 Subject: Implement Grid server-side Sorting API (#13334) Change-Id: Ie85cdaab8b942ed1bb60edac6b20bc1b9c47b445 --- server/src/com/vaadin/ui/components/grid/Grid.java | 170 ++++++++++++++++++++- .../com/vaadin/ui/components/grid/sort/Sort.java | 153 +++++++++++++++++++ .../vaadin/ui/components/grid/sort/SortOrder.java | 73 +++++++++ .../tests/server/component/grid/sort/SortTest.java | 149 ++++++++++++++++++ .../com/vaadin/shared/ui/grid/SortDirection.java | 4 +- 5 files changed, 544 insertions(+), 5 deletions(-) create mode 100644 server/src/com/vaadin/ui/components/grid/sort/Sort.java create mode 100644 server/src/com/vaadin/ui/components/grid/sort/SortOrder.java create mode 100644 server/tests/src/com/vaadin/tests/server/component/grid/sort/SortTest.java (limited to 'shared/src/com') diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index bb5ff23da1..1d9cb8ef10 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2014 Vaadin Ltd. - * + * * 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 @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -47,6 +48,7 @@ import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; +import com.vaadin.shared.ui.grid.SortDirection; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.components.grid.selection.MultiSelectionModel; import com.vaadin.ui.components.grid.selection.NoSelectionModel; @@ -55,6 +57,8 @@ import com.vaadin.ui.components.grid.selection.SelectionChangeListener; import com.vaadin.ui.components.grid.selection.SelectionChangeNotifier; import com.vaadin.ui.components.grid.selection.SelectionModel; import com.vaadin.ui.components.grid.selection.SingleSelectionModel; +import com.vaadin.ui.components.grid.sort.Sort; +import com.vaadin.ui.components.grid.sort.SortOrder; import com.vaadin.util.ReflectTools; /** @@ -138,6 +142,11 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { */ private final List columnGroupRows = new ArrayList(); + /** + * The current sort order + */ + private final List sortOrder = new ArrayList(); + /** * Property listener for listening to changes in data source properties. */ @@ -338,6 +347,36 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { } datasource = container; + + // + // Adjust sort order + // + + if (container instanceof Container.Sortable) { + + // If the container is sortable, go through the current sort order + // and match each item to the sortable properties of the new + // container. If the new container does not support an item in the + // current sort order, that item is removed from the current sort + // order list. + Collection sortableProps = ((Container.Sortable) getContainerDatasource()) + .getSortableContainerPropertyIds(); + + Iterator i = sortOrder.iterator(); + while (i.hasNext()) { + if (!sortableProps.contains(i.next().getPropertyId())) { + i.remove(); + } + } + + sort(); + } else { + + // If the new container is not sortable, we'll just re-set the sort + // order altogether. + clearSortOrder(); + } + datasourceExtension = new RpcDataProviderExtension(container); datasourceExtension.extend(this); @@ -379,7 +418,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { column.setHeaderCaption(String.valueOf(propertyId)); } } - } /** @@ -1081,4 +1119,128 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { void addRenderer(Renderer renderer) { addExtension(renderer); } + + /** + * Sets the current sort order using the fluid Sort API. Read the + * documentation for {@link Sort} for more information. + * + * @param s + * a sort instance + */ + public void sort(Sort s) { + setSortOrder(s.build()); + } + + /** + * Sort this Grid in ascending order by a specified property. + * + * @param propertyId + * a property ID + */ + public void sort(Object propertyId) { + sort(propertyId, SortDirection.ASCENDING); + } + + /** + * Sort this Grid in user-specified {@link SortOrder} by a property. + * + * @param propertyId + * a property ID + * @param direction + * a sort order value (ascending/descending) + */ + public void sort(Object propertyId, SortDirection direction) { + sort(Sort.by(propertyId, direction)); + } + + /** + * Clear the current sort order, and re-sort the grid. + */ + public void clearSortOrder() { + sortOrder.clear(); + sort(); + } + + /** + * Sets the sort order to use. This method throws + * {@link IllegalStateException} if the attached container is not a + * {@link Container.Sortable}, and {@link IllegalArgumentException} if a + * property in the list is not recognized by the container, or if the + * 'order' parameter is null. + * + * @param order + * a sort order list. + */ + public void setSortOrder(List order) { + if (!(getContainerDatasource() instanceof Container.Sortable)) { + throw new IllegalStateException( + "Attached container is not sortable (does not implement Container.Sortable)"); + } + + if (order == null) { + throw new IllegalArgumentException("Order list may not be null!"); + } + + sortOrder.clear(); + + Collection sortableProps = ((Container.Sortable) getContainerDatasource()) + .getSortableContainerPropertyIds(); + + for (SortOrder o : order) { + if (!sortableProps.contains(o.getPropertyId())) { + throw new IllegalArgumentException( + "Property " + + o.getPropertyId() + + " does not exist or is not sortable in the current container"); + } + } + + sortOrder.addAll(order); + sort(); + } + + /** + * Get the current sort order list. + * + * @return a sort order list + */ + public List getSortOrder() { + return Collections.unmodifiableList(sortOrder); + } + + /** + * Apply sorting to data source. + */ + private void sort() { + + Container c = getContainerDatasource(); + if (c instanceof Container.Sortable) { + Container.Sortable cs = (Container.Sortable) c; + + final int items = sortOrder.size(); + Object[] propertyIds = new Object[items]; + boolean[] directions = new boolean[items]; + + for (int i = 0; i < items; ++i) { + SortOrder order = sortOrder.get(i); + propertyIds[i] = order.getPropertyId(); + switch (order.getDirection()) { + case ASCENDING: + directions[i] = true; + break; + case DESCENDING: + directions[i] = false; + break; + default: + throw new IllegalArgumentException("getDirection() of " + + order + " returned an unexpected value"); + } + } + + cs.sort(propertyIds, directions); + } else { + throw new IllegalStateException( + "Container is not sortable (does not implement Container.Sortable)"); + } + } } diff --git a/server/src/com/vaadin/ui/components/grid/sort/Sort.java b/server/src/com/vaadin/ui/components/grid/sort/Sort.java new file mode 100644 index 0000000000..54831378b6 --- /dev/null +++ b/server/src/com/vaadin/ui/components/grid/sort/Sort.java @@ -0,0 +1,153 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.ui.components.grid.sort; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.shared.ui.grid.SortDirection; + +/** + * Fluid Sort API. Provides a convenient, human-readable way of specifying + * multi-column sort order. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class Sort implements Serializable { + + private final Sort previous; + private final SortOrder order; + + /** + * Initial constructor, called by the static by() methods. + * + * @param propertyId + * a property ID, corresponding to a property in the data source + * @param direction + * a sort direction value + */ + private Sort(Object propertyId, SortDirection direction) { + previous = null; + order = new SortOrder(propertyId, direction); + } + + /** + * Chaining constructor, called by the non-static then() methods. This + * constructor links to the previous Sort object. + * + * @param previous + * the sort marker that comes before this one + * @param propertyId + * a property ID, corresponding to a property in the data source + * @param direction + * a sort direction value + */ + private Sort(Sort previous, Object propertyId, SortDirection direction) { + this.previous = previous; + order = new SortOrder(propertyId, direction); + + Sort s = previous; + while (s != null) { + if (s.order.getPropertyId() == propertyId) { + throw new IllegalStateException( + "Can not sort along the same property (" + propertyId + + ") twice!"); + } + s = s.previous; + } + + } + + /** + * Start building a Sort order by sorting a provided column in ascending + * order. + * + * @param propertyId + * a property id, corresponding to a data source property + * @return a sort object + */ + public static Sort by(Object propertyId) { + return by(propertyId, SortDirection.ASCENDING); + } + + /** + * Start building a Sort order by sorting a provided column. + * + * @param propertyId + * a property id, corresponding to a data source property + * @param direction + * a sort direction value + * @return a sort object + */ + public static Sort by(Object propertyId, SortDirection direction) { + return new Sort(propertyId, direction); + } + + /** + * Continue building a Sort order. The provided property is sorted in + * ascending order if the previously added properties have been evaluated as + * equals. + * + * @param propertyId + * a property id, corresponding to a data source property + * @return a sort object + */ + public Sort then(Object propertyId) { + return then(propertyId, SortDirection.ASCENDING); + } + + /** + * Continue building a Sort order. The provided property is sorted in + * specified order if the previously added properties have been evaluated as + * equals. + * + * @param propertyId + * a property id, corresponding to a data source property + * @param direction + * a sort direction value + * @return a sort object + */ + public Sort then(Object propertyId, SortDirection direction) { + return new Sort(this, propertyId, direction); + } + + /** + * Build a sort order list, ready to be passed to Grid + * + * @return a sort order list. + */ + public List build() { + + int count = 1; + Sort s = this; + while (s.previous != null) { + s = s.previous; + ++count; + } + + List order = new ArrayList(count); + + s = this; + do { + order.add(0, s.order); + s = s.previous; + } while (s != null); + + return order; + } +} diff --git a/server/src/com/vaadin/ui/components/grid/sort/SortOrder.java b/server/src/com/vaadin/ui/components/grid/sort/SortOrder.java new file mode 100644 index 0000000000..f186333e0a --- /dev/null +++ b/server/src/com/vaadin/ui/components/grid/sort/SortOrder.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.ui.components.grid.sort; + +import java.io.Serializable; + +import com.vaadin.shared.ui.grid.SortDirection; + +/** + * Sort order descriptor. Links together a {@link SortDirection} value and a + * Vaadin container property ID. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class SortOrder implements Serializable { + + private final Object propertyId; + private final SortDirection direction; + + /** + * Create a SortOrder object. Both arguments must be non-null. + * + * @param propertyId + * id of the data source property to sort by + * @param direction + * value indicating whether the property id should be sorted in + * ascending or descending order + */ + public SortOrder(Object propertyId, SortDirection direction) { + if (propertyId == null) { + throw new IllegalArgumentException("Property ID can not be null!"); + } + if (direction == null) { + throw new IllegalArgumentException( + "Direction value can not be null!"); + } + this.propertyId = propertyId; + this.direction = direction; + } + + /** + * Returns the property ID. + * + * @return a property ID + */ + public Object getPropertyId() { + return propertyId; + } + + /** + * Returns the {@link SortDirection} value. + * + * @return a sort direction value + */ + public SortDirection getDirection() { + return direction; + } + +} diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/sort/SortTest.java b/server/tests/src/com/vaadin/tests/server/component/grid/sort/SortTest.java new file mode 100644 index 0000000000..844292265d --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/grid/sort/SortTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.grid.sort; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.shared.ui.grid.SortDirection; +import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.sort.Sort; + +public class SortTest { + + class DummySortingIndexedContainer extends IndexedContainer { + + private Object[] expectedProperties; + private boolean[] expectedAscending; + private boolean sorted = true; + + @Override + public void sort(Object[] propertyId, boolean[] ascending) { + Assert.assertEquals( + "Different amount of expected and actual properties,", + expectedProperties.length, propertyId.length); + Assert.assertEquals( + "Different amount of expected and actual directions", + expectedAscending.length, ascending.length); + for (int i = 0; i < propertyId.length; ++i) { + Assert.assertEquals("Sorting properties differ", + expectedProperties[i], propertyId[i]); + Assert.assertEquals("Sorting directions differ", + expectedAscending[i], ascending[i]); + } + sorted = true; + } + + public void expectedSort(Object[] properties, SortDirection[] directions) { + assert directions.length == properties.length : "Array dimensions differ"; + expectedProperties = properties; + expectedAscending = new boolean[directions.length]; + for (int i = 0; i < directions.length; ++i) { + expectedAscending[i] = (directions[i] == SortDirection.ASCENDING); + } + sorted = false; + } + + public boolean isSorted() { + return sorted; + } + } + + private DummySortingIndexedContainer container; + private Grid grid; + + @Before + public void setUp() { + container = createContainer(); + container.expectedSort(new Object[] {}, new SortDirection[] {}); + grid = new Grid(container); + } + + @After + public void tearDown() { + Assert.assertTrue("Container was not sorted after the test.", + container.isSorted()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidSortDirection() { + Sort.by("foo", null); + } + + @Test(expected = IllegalStateException.class) + public void testSortOneColumnMultipleTimes() { + Sort.by("foo").then("bar").then("foo"); + } + + @Test(expected = IllegalArgumentException.class) + public void testSortingByUnexistingProperty() { + grid.sort("foobar"); + } + + @Test(expected = IllegalArgumentException.class) + public void testSortingByUnsortableProperty() { + container.addContainerProperty("foobar", Object.class, null); + grid.sort("foobar"); + } + + @Test + public void testGridDirectSortAscending() { + container.expectedSort(new Object[] { "foo" }, + new SortDirection[] { SortDirection.ASCENDING }); + grid.sort("foo"); + } + + @Test + public void testGridDirectSortDescending() { + container.expectedSort(new Object[] { "foo" }, + new SortDirection[] { SortDirection.DESCENDING }); + grid.sort("foo", SortDirection.DESCENDING); + } + + @Test + public void testGridSortBy() { + container.expectedSort(new Object[] { "foo", "bar", "baz" }, + new SortDirection[] { SortDirection.ASCENDING, + SortDirection.ASCENDING, SortDirection.DESCENDING }); + grid.sort(Sort.by("foo").then("bar") + .then("baz", SortDirection.DESCENDING)); + } + + @Test + public void testChangeContainerAfterSorting() { + container.expectedSort(new Object[] { "foo", "bar", "baz" }, + new SortDirection[] { SortDirection.ASCENDING, + SortDirection.ASCENDING, SortDirection.DESCENDING }); + grid.sort(Sort.by("foo").then("bar") + .then("baz", SortDirection.DESCENDING)); + container = new DummySortingIndexedContainer(); + container.addContainerProperty("baz", String.class, ""); + container.expectedSort(new Object[] { "baz" }, + new SortDirection[] { SortDirection.DESCENDING }); + grid.setContainerDataSource(container); + } + + private DummySortingIndexedContainer createContainer() { + DummySortingIndexedContainer container = new DummySortingIndexedContainer(); + container.addContainerProperty("foo", Integer.class, 0); + container.addContainerProperty("bar", Integer.class, 0); + container.addContainerProperty("baz", Integer.class, 0); + return container; + } +} diff --git a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java index 1a7c29f8a7..0b4eafc37f 100644 --- a/shared/src/com/vaadin/shared/ui/grid/SortDirection.java +++ b/shared/src/com/vaadin/shared/ui/grid/SortDirection.java @@ -15,13 +15,15 @@ */ package com.vaadin.shared.ui.grid; +import java.io.Serializable; + /** * Describes sorting direction for a Grid column * * @since * @author Vaadin Ltd */ -public enum SortDirection { +public enum SortDirection implements Serializable { /** * Ascending (e.g. A-Z, 1..9) sort order -- cgit v1.2.3 From b36c0fc7e01def912430be5d9a42d344d8202046 Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Wed, 9 Jul 2014 19:20:19 +0300 Subject: GridConnector sends user sort events to the server (#13334) Change-Id: Ic5b1462ecf2e5a5cef6b08bea7a4c00a09c39c9a --- .../com/vaadin/client/ui/grid/GridConnector.java | 22 +++++++++ server/src/com/vaadin/ui/components/grid/Grid.java | 14 ++++++ .../com/vaadin/shared/ui/grid/GridServerRpc.java | 2 + .../components/grid/GridBasicFeaturesTest.java | 53 +++++++++++++++++++++- 4 files changed, 90 insertions(+), 1 deletion(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index dd27f7f747..095b049506 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -42,6 +42,9 @@ import com.vaadin.client.ui.grid.selection.SelectionChangeHandler; import com.vaadin.client.ui.grid.selection.SelectionModelMulti; import com.vaadin.client.ui.grid.selection.SelectionModelNone; import com.vaadin.client.ui.grid.selection.SelectionModelSingle; +import com.vaadin.client.ui.grid.sort.SortEvent; +import com.vaadin.client.ui.grid.sort.SortEventHandler; +import com.vaadin.client.ui.grid.sort.SortOrder; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.ColumnGroupState; @@ -51,6 +54,7 @@ import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; import com.vaadin.shared.ui.grid.ScrollDestination; +import com.vaadin.shared.ui.grid.SortDirection; /** * Connects the client side {@link Grid} widget with the server side @@ -257,6 +261,24 @@ public class GridConnector extends AbstractComponentConnector { getWidget().addSelectionChangeHandler(internalSelectionChangeHandler); + getWidget().addSortHandler(new SortEventHandler() { + @Override + public void sort(SortEvent event) { + List order = event.getOrder(); + String[] columnIds = new String[order.size()]; + SortDirection[] directions = new SortDirection[order.size()]; + for (int i = 0; i < order.size(); i++) { + SortOrder sortOrder = order.get(i); + CustomGridColumn column = (CustomGridColumn) sortOrder + .getColumn(); + columnIds[i] = column.id; + + directions[i] = sortOrder.getDirection(); + } + + getRpcProxy(GridServerRpc.class).sort(columnIds, directions); + } + }); } @Override diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 1d9cb8ef10..cc284841a1 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -315,6 +315,20 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { } } } + + @Override + public void sort(String[] columnIds, SortDirection[] directions) { + assert columnIds.length == directions.length; + + List order = new ArrayList( + columnIds.length); + for (int i = 0; i < columnIds.length; i++) { + Object propertyId = getPropertyIdByColumnId(columnIds[i]); + order.add(new SortOrder(propertyId, directions[i])); + } + + setSortOrder(order); + } }); } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java index eec7b39482..9ce094b092 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -27,4 +27,6 @@ import com.vaadin.shared.communication.ServerRpc; */ public interface GridServerRpc extends ServerRpc { void selectionChange(List newSelection); + + void sort(String[] columnIds, SortDirection[] directions); } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java index d09f55537d..59f6311995 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java @@ -374,7 +374,7 @@ public class GridBasicFeaturesTest extends MultiBrowserTest { } @Test - public void testSorting() throws IOException { + public void testProgrammaticSorting() throws IOException { openTestURL(); GridElement grid = getGridElement(); @@ -418,6 +418,57 @@ public class GridBasicFeaturesTest extends MultiBrowserTest { } } + @Test + public void testUserSorting() { + openTestURL(); + + GridElement grid = getGridElement(); + + // Sorting by column 9 is sorting by row index that is represented as a + // String. + // First cells for first 3 rows are (9, 0), (99, 0) and (999, 0) + + // Click header twice to sort descending + grid.getHeaderCell(0, 9).click(); + grid.getHeaderCell(0, 9).click(); + String row = ""; + for (int i = 0; i < 3; ++i) { + row += "9"; + assertEquals( + "Grid is not sorted by Column9 using descending direction.", + "(" + row + ", 0)", grid.getCell(i, 0).getText()); + } + + // Column 10 is random numbers from Random with seed 13334 + // Click header to sort ascending + grid.getHeaderCell(0, 10).click(); + + // Not cleaning up correctly causes exceptions when scrolling. + grid.scrollToRow(50); + assertFalse("Scrolling caused and exception when shuffled.", + getLogRow(0).contains("Exception")); + + for (int i = 0; i < 5; ++i) { + assertGreater( + "Grid is not sorted by Column10 using ascending direction", + Integer.parseInt(grid.getCell(i + 1, 10).getText()), + Integer.parseInt(grid.getCell(i, 10).getText())); + + } + + // Column 7 is row index as a number. Last three row are original rows + // 2, 1 and 0. + // Click header twice to sort descending + grid.getHeaderCell(0, 7).click(); + grid.getHeaderCell(0, 7).click(); + for (int i = 0; i < 3; ++i) { + assertEquals( + "Grid is not sorted by Column7 using descending direction", + "(" + i + ", 0)", + grid.getCell(GridBasicFeatures.ROWS - (i + 1), 0).getText()); + } + } + private void sortBy(String column) { selectMenuPath("Component", "State", "Sort by column", column); } -- cgit v1.2.3 From f27a00b7b9a487a66dd23a4d28aa56fe60a1ad4e Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Thu, 10 Jul 2014 15:41:24 +0300 Subject: Server-side programmatic sort updates sort indicators in the UI (#13334) Change-Id: Ia4bae5d88d265250d1bf4daa8d23561edb757872 --- .../com/vaadin/client/ui/grid/GridConnector.java | 23 +++++++++++++++++++++- server/src/com/vaadin/ui/components/grid/Grid.java | 10 ++++++++++ .../src/com/vaadin/shared/ui/grid/GridState.java | 6 ++++++ .../components/grid/GridBasicFeaturesTest.java | 22 +++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 095b049506..ca66ccc3d5 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -17,6 +17,7 @@ package com.vaadin.client.ui.grid; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -276,7 +277,12 @@ public class GridConnector extends AbstractComponentConnector { directions[i] = sortOrder.getDirection(); } - getRpcProxy(GridServerRpc.class).sort(columnIds, directions); + if (!Arrays.equals(columnIds, getState().sortColumns) + || !Arrays.equals(directions, getState().sortDirs)) { + // Report back to server if changed + getRpcProxy(GridServerRpc.class) + .sort(columnIds, directions); + } } }); } @@ -522,6 +528,21 @@ public class GridConnector extends AbstractComponentConnector { } + @OnStateChange({ "sortColumns", "sortDirs" }) + void onSortStateChange() { + List sortOrder = new ArrayList(); + + String[] sortColumns = getState().sortColumns; + SortDirection[] sortDirs = getState().sortDirs; + + for (int i = 0; i < sortColumns.length; i++) { + sortOrder.add(new SortOrder(columnIdToColumn.get(sortColumns[i]), + sortDirs[i])); + } + + getWidget().setSortOrder(sortOrder); + } + private Logger getLogger() { return Logger.getLogger(getClass().getName()); } diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 79effadab9..f18ca6045e 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -1239,8 +1239,15 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { Object[] propertyIds = new Object[items]; boolean[] directions = new boolean[items]; + String[] columnKeys = new String[items]; + SortDirection[] stateDirs = new SortDirection[items]; + for (int i = 0; i < items; ++i) { SortOrder order = sortOrder.get(i); + + columnKeys[i] = this.columnKeys.key(order.getPropertyId()); + stateDirs[i] = order.getDirection(); + propertyIds[i] = order.getPropertyId(); switch (order.getDirection()) { case ASCENDING: @@ -1259,6 +1266,9 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { fireEvent(new SortOrderChangeEvent(this, new ArrayList( sortOrder))); + + getState().sortColumns = columnKeys; + getState(false).sortDirs = stateDirs; } else { throw new IllegalStateException( "Container is not sortable (does not implement Container.Sortable)"); diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index f71fe0929a..ef1b9a806a 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -132,4 +132,10 @@ public class GridState extends AbstractComponentState { public List selectedKeys = new ArrayList(); public SharedSelectionMode selectionMode; + + /** Keys of the currently sorted columns */ + public String[] sortColumns = new String[0]; + + /** Directions for each sorted column */ + public SortDirection[] sortDirs = new SortDirection[0]; } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java index f15f45f97a..a94550721e 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridBasicFeaturesTest.java @@ -383,6 +383,11 @@ public class GridBasicFeaturesTest extends MultiBrowserTest { // String. // First cells for first 3 rows are (9, 0), (99, 0) and (999, 0) sortBy("Column9, DESC"); + + assertTrue("Column 9 should have the sort-desc stylename", grid + .getHeaderCell(0, 9).getAttribute("class") + .contains("sort-desc")); + String row = ""; for (int i = 0; i < 3; ++i) { row += "9"; @@ -394,6 +399,14 @@ public class GridBasicFeaturesTest extends MultiBrowserTest { // Column 10 is random numbers from Random with seed 13334 sortBy("Column10, ASC"); + assertFalse( + "Column 9 should no longer have the sort-desc stylename", + grid.getHeaderCell(0, 9).getAttribute("class") + .contains("sort-desc")); + assertTrue("Column 10 should have the sort-asc stylename", grid + .getHeaderCell(0, 10).getAttribute("class") + .contains("sort-asc")); + // Not cleaning up correctly causes exceptions when scrolling. grid.scrollToRow(50); assertFalse("Scrolling caused and exception when shuffled.", @@ -416,6 +429,15 @@ public class GridBasicFeaturesTest extends MultiBrowserTest { "(" + i + ", 0)", grid.getCell(GridBasicFeatures.ROWS - (i + 1), 0).getText()); } + + assertFalse( + "Column 10 should no longer have the sort-asc stylename", + grid.getHeaderCell(0, 10).getAttribute("class") + .contains("sort-asc")); + assertTrue("Column 7 should have the sort-desc stylename", grid + .getHeaderCell(0, 7).getAttribute("class") + .contains("sort-desc")); + } @Test -- cgit v1.2.3 From ff310e8bdbfc773153e4faa9eb5749bb3e44700e Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Fri, 18 Jul 2014 15:33:55 +0300 Subject: Grid header/footer rewrite: add partial shared state support (#13334) Currently supported: * Adding and removal of header and footer rows * Header is single-row by default * Footer is zero-row by default * Text captions * Showing and hiding the whole header or footer * Passing captions and visibility in shared state TODO: * Column spanning * HTML content * Widget content * Component content * Sorting/Indicators * Server side API * Rest of shared state handling Change-Id: Iddd1a596597c3b11ead50bd7d5d7011cd81e2c83 --- .../com/vaadin/client/ui/grid/GridConnector.java | 40 ++++++++++++++++----- server/src/com/vaadin/ui/components/grid/Grid.java | 21 ++++++++--- .../com/vaadin/ui/components/grid/GridColumn.java | 17 +++++++-- .../tests/server/component/grid/GridColumns.java | 12 +++---- .../com/vaadin/shared/ui/grid/GridColumnState.java | 2 ++ .../src/com/vaadin/shared/ui/grid/GridState.java | 10 ++---- .../shared/ui/grid/GridStaticSectionState.java | 41 ++++++++++++++++++++++ 7 files changed, 114 insertions(+), 29 deletions(-) create mode 100644 shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index a2563488d3..67f4dc4045 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -36,6 +36,7 @@ import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource; import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.grid.GridStaticSection.StaticRow; import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; import com.vaadin.client.ui.grid.selection.AbstractRowHandleSelectionModel; import com.vaadin.client.ui.grid.selection.SelectionChangeEvent; @@ -54,6 +55,9 @@ import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; +import com.vaadin.shared.ui.grid.GridStaticSectionState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.ui.grid.SortDirection; @@ -262,9 +266,6 @@ public class GridConnector extends AbstractComponentConnector { getWidget().addSelectionChangeHandler(internalSelectionChangeHandler); - // TODO: Remove this workaround once we have header/footer communication - getWidget().getFooter().appendRow(); - getWidget().addSortHandler(new SortEventHandler() { @Override public void sort(SortEvent event) { @@ -321,14 +322,12 @@ public class GridConnector extends AbstractComponentConnector { } } - // Header - if (stateChangeEvent.hasPropertyChanged("columnHeadersVisible")) { - getWidget().getHeader().setVisible(getState().columnHeadersVisible); + if (stateChangeEvent.hasPropertyChanged("header")) { + updateSectionFromState(getWidget().getHeader(), getState().header); } - // Footer - if (stateChangeEvent.hasPropertyChanged("columnFootersVisible")) { - getWidget().getFooter().setVisible(getState().columnFootersVisible); + if (stateChangeEvent.hasPropertyChanged("footer")) { + updateSectionFromState(getWidget().getFooter(), getState().footer); } // Column row groups @@ -353,6 +352,29 @@ public class GridConnector extends AbstractComponentConnector { } } + private void updateSectionFromState(GridStaticSection section, + GridStaticSectionState state) { + + while (section.getRowCount() != 0) { + section.removeRow(0); + } + + for (RowState rowState : state.rows) { + StaticRow row = section.appendRow(); + + assert rowState.cells.size() == getWidget().getColumnCount(); + + int i = 0; + for (CellState cellState : rowState.cells) { + row.getCell(i++).setText(cellState.text); + } + } + + section.setVisible(state.visible); + + section.refreshGrid(); + } + /** * Updates a column from a state change event. * diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index c91924f5a8..e4e6dcf4be 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -46,6 +46,8 @@ import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; +import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.ui.grid.SortDirection; @@ -222,6 +224,11 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * the data source for the grid */ public Grid(final Container.Indexed datasource) { + + getState().header.rows.add(new RowState()); + getState().footer.rows.add(new RowState()); + setColumnFootersVisible(false); + setContainerDataSource(datasource); setSelectionMode(SelectionMode.MULTI); addSelectionChangeListener(new SelectionChangeListener() { @@ -465,7 +472,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * true if the header rows should be visible */ public void setColumnHeadersVisible(boolean visible) { - getState().columnHeadersVisible = visible; + getState().header.visible = visible; } /** @@ -474,7 +481,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * @return true if the headers of the columns are visible */ public boolean isColumnHeadersVisible() { - return getState(false).columnHeadersVisible; + return getState(false).header.visible; } /** @@ -484,7 +491,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * true if the footer rows should be visible */ public void setColumnFootersVisible(boolean visible) { - getState().columnFootersVisible = visible; + getState().footer.visible = visible; } /** @@ -493,7 +500,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { * @return true if the footer rows should be visible */ public boolean isColumnFootersVisible() { - return getState(false).columnFootersVisible; + return getState(false).footer.visible; } /** @@ -623,6 +630,12 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { GridColumnState columnState = new GridColumnState(); columnState.id = columnKeys.key(datasourcePropertyId); getState().columns.add(columnState); + for (RowState row : getState().header.rows) { + row.cells.add(new CellState()); + } + for (RowState row : getState().footer.rows) { + row.cells.add(new CellState()); + } GridColumn column = new GridColumn(this, columnState); columns.put(datasourcePropertyId, column); diff --git a/server/src/com/vaadin/ui/components/grid/GridColumn.java b/server/src/com/vaadin/ui/components/grid/GridColumn.java index 43b2003e35..667b4f86db 100644 --- a/server/src/com/vaadin/ui/components/grid/GridColumn.java +++ b/server/src/com/vaadin/ui/components/grid/GridColumn.java @@ -22,6 +22,7 @@ import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.ConverterUtil; import com.vaadin.server.VaadinSession; import com.vaadin.shared.ui.grid.GridColumnState; +import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; import com.vaadin.ui.UI; import com.vaadin.ui.components.grid.renderers.TextRenderer; @@ -88,7 +89,7 @@ public class GridColumn implements Serializable { */ public String getHeaderCaption() throws IllegalStateException { checkColumnIsAttached(); - return state.header; + return getHeaderCellState().text; } /** @@ -102,6 +103,7 @@ public class GridColumn implements Serializable { */ public void setHeaderCaption(String caption) throws IllegalStateException { checkColumnIsAttached(); + getHeaderCellState().text = caption; state.header = caption; grid.markAsDirty(); } @@ -116,7 +118,7 @@ public class GridColumn implements Serializable { */ public String getFooterCaption() throws IllegalStateException { checkColumnIsAttached(); - return state.footer; + return getFooterCellState().text; } /** @@ -130,10 +132,21 @@ public class GridColumn implements Serializable { */ public void setFooterCaption(String caption) throws IllegalStateException { checkColumnIsAttached(); + getFooterCellState().text = caption; state.footer = caption; grid.markAsDirty(); } + private CellState getHeaderCellState() { + int index = grid.getState().columns.indexOf(state); + return grid.getState().header.rows.get(0).cells.get(index); + } + + private CellState getFooterCellState() { + int index = grid.getState().columns.indexOf(state); + return grid.getState().footer.rows.get(0).cells.get(index); + } + /** * Returns the width (in pixels). By default a column is 100px wide. * diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java index 381135d7ab..9d71d21d59 100644 --- a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java +++ b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumns.java @@ -175,30 +175,30 @@ public class GridColumns { public void testHeaderVisiblility() throws Exception { assertTrue(grid.isColumnHeadersVisible()); - assertTrue(state.columnHeadersVisible); + assertTrue(state.header.visible); grid.setColumnHeadersVisible(false); assertFalse(grid.isColumnHeadersVisible()); - assertFalse(state.columnHeadersVisible); + assertFalse(state.header.visible); grid.setColumnHeadersVisible(true); assertTrue(grid.isColumnHeadersVisible()); - assertTrue(state.columnHeadersVisible); + assertTrue(state.header.visible); } @Test public void testFooterVisibility() throws Exception { assertFalse(grid.isColumnFootersVisible()); - assertFalse(state.columnFootersVisible); + assertFalse(state.footer.visible); grid.setColumnFootersVisible(false); assertFalse(grid.isColumnFootersVisible()); - assertFalse(state.columnFootersVisible); + assertFalse(state.footer.visible); grid.setColumnFootersVisible(true); assertTrue(grid.isColumnFootersVisible()); - assertTrue(state.columnFootersVisible); + assertTrue(state.footer.visible); } @Test diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java index b9bae35db6..5c0d04d968 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java @@ -37,11 +37,13 @@ public class GridColumnState implements Serializable { /** * Header caption for the column */ + @Deprecated public String header; /** * Footer caption for the column */ + @Deprecated public String footer; /** diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index ef1b9a806a..68ee64dfe4 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -98,15 +98,9 @@ public class GridState extends AbstractComponentState { */ public List columns = new ArrayList(); - /** - * Is the column header row visible - */ - public boolean columnHeadersVisible = true; + public GridStaticSectionState header = new GridStaticSectionState(); - /** - * Is the column footer row visible - */ - public boolean columnFootersVisible = false; + public GridStaticSectionState footer = new GridStaticSectionState(); /** * The column groups added to the grid diff --git a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java new file mode 100644 index 0000000000..358c06d089 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.shared.ui.grid; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Shared state for Grid headers and footers. + * + * @since + * @author Vaadin Ltd + */ +public class GridStaticSectionState implements Serializable { + + public static class CellState implements Serializable { + public String text = ""; + } + + public static class RowState implements Serializable { + public List cells = new ArrayList(); + } + + public List rows = new ArrayList(); + + public boolean visible = true; +} -- cgit v1.2.3 From cfad6d54de44fead836ec9355bbc77c06fa26a40 Mon Sep 17 00:00:00 2001 From: Johannes Dahlström Date: Wed, 16 Jul 2014 11:16:38 +0300 Subject: Client-side Grid header/footer rewrite: add default header support (#13334) Currently supported: * Adding and removal of header and footer rows * Header is single-row by default * Footer is zero-row by default * Text captions * Showing and hiding the whole header or footer * Default header rows for sorting UI TODO: * Column spanning * HTML content * Widget content * Component content * Server side API * Shared state handling Change-Id: I3d6a2b75fad87780f83238ab792bbbcfe99a48fd --- client/src/com/vaadin/client/ui/grid/Grid.java | 74 +++++++++++++--------- .../com/vaadin/client/ui/grid/GridConnector.java | 5 ++ .../src/com/vaadin/client/ui/grid/GridHeader.java | 73 +++++++++++++++++++++ .../vaadin/client/ui/grid/GridStaticSection.java | 9 ++- server/src/com/vaadin/ui/components/grid/Grid.java | 7 +- .../shared/ui/grid/GridStaticSectionState.java | 2 + .../grid/basicfeatures/GridHeaderTest.java | 38 +++++++++++ .../client/grid/GridBasicClientFeatures.java | 30 +++++++++ 8 files changed, 206 insertions(+), 32 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index 403b0d1e3f..ff75417c12 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -45,6 +45,7 @@ import com.vaadin.client.Util; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.SubPartAware; +import com.vaadin.client.ui.grid.GridHeader.HeaderRow; import com.vaadin.client.ui.grid.renderers.ComplexRenderer; import com.vaadin.client.ui.grid.renderers.TextRenderer; import com.vaadin.client.ui.grid.renderers.WidgetRenderer; @@ -616,6 +617,13 @@ public class Grid extends Composite implements protected abstract SelectionModel createModel(); } + class SortableColumnHeaderRenderer extends + AbstractGridColumn.SortableColumnHeaderRenderer { + SortableColumnHeaderRenderer(Renderer cellRenderer) { + super(Grid.this, cellRenderer); + } + } + /** * Base class for grid columns internally used by the Grid. The user should * use {@link GridColumn} when creating new columns. @@ -633,9 +641,11 @@ public class Grid extends Composite implements * * FIXME Currently assumes multisorting */ - private class SortableColumnHeaderRenderer extends + static class SortableColumnHeaderRenderer extends ComplexRenderer { + private Grid grid; + /** * Delay before a long tap action is triggered. Number in * milliseconds. @@ -658,7 +668,8 @@ public class Grid extends Composite implements @Override public void run() { - SortOrder sortingOrder = getSortingOrder(); + SortOrder sortingOrder = getSortingOrder(grid + .getColumnFromVisibleIndex(cell.getColumn())); if (sortingOrder == null) { /* * No previous sorting, sort Ascending @@ -697,7 +708,9 @@ public class Grid extends Composite implements * @param cellRenderer * The actual cell renderer */ - public SortableColumnHeaderRenderer(Renderer cellRenderer) { + public SortableColumnHeaderRenderer(Grid grid, + Renderer cellRenderer) { + this.grid = grid; this.cellRenderer = cellRenderer; } @@ -715,9 +728,11 @@ public class Grid extends Composite implements * this is fixed. */ if (grid != null) { - SortOrder sortingOrder = getSortingOrder(); + GridColumn column = grid + .getColumnFromVisibleIndex(cell.getColumn()); + SortOrder sortingOrder = getSortingOrder(column); Element cellElement = cell.getElement(); - if (grid.getColumn(cell.getColumn()).isSortable()) { + if (column.isSortable()) { if (sortingOrder != null) { if (SortDirection.ASCENDING == sortingOrder .getDirection()) { @@ -836,6 +851,18 @@ public class Grid extends Composite implements } + protected void removeFromRow(HeaderRow row) { + row.setRenderer(new Renderer() { + @Override + public void render(FlyweightCell cell, String data) { + cleanup(cell); + } + }); + grid.refreshHeader(); + row.setRenderer(cellRenderer); + grid.refreshHeader(); + } + /** * Sorts the column in a direction */ @@ -844,13 +871,14 @@ public class Grid extends Composite implements TableCellElement th = TableCellElement.as(cell.getElement()); // Apply primary sorting on clicked column - GridColumn columnInstance = getColumnInstance(); + GridColumn columnInstance = grid + .getColumnFromVisibleIndex(cell.getColumn()); Sort sorting = Sort.by(columnInstance, direction); // Re-apply old sorting to the sort order if (multisort) { for (SortOrder order : grid.getSortOrder()) { - if (order.getColumn() != AbstractGridColumn.this) { + if (order.getColumn() != columnInstance) { sorting = sorting.then(order.getColumn(), order.getDirection()); } @@ -861,24 +889,12 @@ public class Grid extends Composite implements grid.sort(sorting); } - /** - * Resolves a GridColumn out of a AbstractGridColumn - */ - private GridColumn getColumnInstance() { - for (GridColumn column : grid.getColumns()) { - if (column == AbstractGridColumn.this) { - return (GridColumn) column; - } - } - return null; - } - /** * Finds the sorting order for this column */ - private SortOrder getSortingOrder() { + private SortOrder getSortingOrder(GridColumn column) { for (SortOrder order : grid.getSortOrder()) { - if (order.getColumn() == AbstractGridColumn.this) { + if (order.getColumn() == column) { return order; } } @@ -922,8 +938,7 @@ public class Grid extends Composite implements * Renderer for rendering the header cell value into the cell */ @Deprecated - private Renderer headerRenderer = new SortableColumnHeaderRenderer( - new TextRenderer()); + private Renderer headerRenderer = new TextRenderer(); /** * Renderer for rendering the footer cell value into the cell @@ -964,8 +979,7 @@ public class Grid extends Composite implements throw new IllegalArgumentException("Renderer cannot be null."); } - this.headerRenderer = new SortableColumnHeaderRenderer( - headerRenderer); + this.headerRenderer = headerRenderer; this.footerRenderer = footerRenderer; } @@ -1016,7 +1030,7 @@ public class Grid extends Composite implements if (renderer == null) { throw new IllegalArgumentException("Renderer cannot be null."); } - headerRenderer = new SortableColumnHeaderRenderer(headerRenderer); + this.headerRenderer = headerRenderer; if (grid != null) { grid.refreshHeader(); } @@ -1472,7 +1486,8 @@ public class Grid extends Composite implements escalator.getFooter().setEscalatorUpdater(createFooterUpdater()); header.setGrid(this); - header.appendRow(); + HeaderRow defaultRow = header.appendRow(); + header.setDefaultRow(defaultRow); footer.setGrid(this); @@ -2470,13 +2485,14 @@ public class Grid extends Composite implements if (container != null) { cell = container.getCell(e); if (cell != null) { + // FIXME getFromVisibleIndex??? GridColumn gridColumn = columns.get(cell.getColumn()); Renderer renderer; if (container == escalator.getHeader()) { - renderer = gridColumn.getHeaderRenderer(); + renderer = header.getRow(cell.getRow()).getRenderer(); } else if (container == escalator.getFooter()) { - renderer = gridColumn.getFooterRenderer(); + renderer = footer.getRow(cell.getRow()).getRenderer(); } else { renderer = gridColumn.getRenderer(); } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 67f4dc4045..3c7629a60d 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -36,6 +36,7 @@ import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource; import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.grid.GridHeader.HeaderRow; import com.vaadin.client.ui.grid.GridStaticSection.StaticRow; import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; import com.vaadin.client.ui.grid.selection.AbstractRowHandleSelectionModel; @@ -368,6 +369,10 @@ public class GridConnector extends AbstractComponentConnector { for (CellState cellState : rowState.cells) { row.getCell(i++).setText(cellState.text); } + + if (section instanceof GridHeader && rowState.defaultRow) { + ((GridHeader) section).setDefaultRow((HeaderRow) row); + } } section.setVisible(state.visible); diff --git a/client/src/com/vaadin/client/ui/grid/GridHeader.java b/client/src/com/vaadin/client/ui/grid/GridHeader.java index a2207c49c7..c5b0febeca 100644 --- a/client/src/com/vaadin/client/ui/grid/GridHeader.java +++ b/client/src/com/vaadin/client/ui/grid/GridHeader.java @@ -15,6 +15,8 @@ */ package com.vaadin.client.ui.grid; +import com.vaadin.client.ui.grid.Grid.AbstractGridColumn.SortableColumnHeaderRenderer; + /** * Represents the header section of a Grid. A header consists of a single header * row containing a header cell for each column. Each cell has a simple textual @@ -41,6 +43,16 @@ public class GridHeader extends GridStaticSection { */ public class HeaderRow extends GridStaticSection.StaticRow { + private boolean isDefault = false; + + protected void setDefault(boolean isDefault) { + this.isDefault = isDefault; + } + + public boolean isDefault() { + return isDefault; + } + @Override protected HeaderCell createCell() { return new HeaderCell(); @@ -54,6 +66,67 @@ public class GridHeader extends GridStaticSection { public class HeaderCell extends GridStaticSection.StaticCell { } + private HeaderRow defaultRow; + + @Override + public void removeRow(int index) { + HeaderRow removedRow = getRow(index); + super.removeRow(index); + if (removedRow == defaultRow) { + setDefaultRow(null); + } + } + + /** + * Sets the default row of this header. The default row is a special header + * row providing a user interface for sorting columns. + * + * @param row + * the new default row, or null for no default row + * + * @throws IllegalArgumentException + * this header does not contain the row + */ + public void setDefaultRow(HeaderRow row) { + if (row == defaultRow) { + return; + } + if (row != null && !getRows().contains(row)) { + throw new IllegalArgumentException( + "Cannot set a default row that does not exist in the container"); + } + if (defaultRow != null) { + assert defaultRow.getRenderer() instanceof SortableColumnHeaderRenderer; + + // Eclipse is wrong about this warning - javac does not accept the + // parameterized version + ((Grid.SortableColumnHeaderRenderer) defaultRow.getRenderer()) + .removeFromRow(defaultRow); + + defaultRow.setDefault(false); + } + if (row != null) { + assert !(row.getRenderer() instanceof SortableColumnHeaderRenderer); + + row.setRenderer(getGrid().new SortableColumnHeaderRenderer(row + .getRenderer())); + + row.setDefault(true); + } + defaultRow = row; + refreshGrid(); + } + + /** + * Returns the current default row of this header. The default row is a + * special header row providing a user interface for sorting columns. + * + * @return the default row or null if no default row set + */ + public HeaderRow getDefaultRow() { + return defaultRow; + } + @Override protected HeaderRow createRow() { return new HeaderRow(); diff --git a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java index f455acc92e..4318811ca2 100644 --- a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java +++ b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java @@ -111,6 +111,10 @@ abstract class GridStaticSection> cells.remove(index); } + protected void setRenderer(Renderer renderer) { + this.renderer = renderer; + } + protected Renderer getRenderer() { return renderer; } @@ -227,11 +231,12 @@ abstract class GridStaticSection> * if the row does not exist in this section */ public void removeRow(ROWTYPE row) { - if (!rows.remove(row)) { + try { + removeRow(rows.indexOf(row)); + } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException( "Section does not contain the given row"); } - refreshGrid(); } /** diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index e4e6dcf4be..67a97c74b7 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -225,8 +225,13 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { */ public Grid(final Container.Indexed datasource) { - getState().header.rows.add(new RowState()); + RowState headerDefaultRow = new RowState(); + headerDefaultRow.defaultRow = true; + getState().header.rows.add(headerDefaultRow); + + // FIXME By default there shouldn't be any footer row getState().footer.rows.add(new RowState()); + setColumnFootersVisible(false); setContainerDataSource(datasource); diff --git a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java index 358c06d089..859e01f089 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java @@ -33,6 +33,8 @@ public class GridStaticSectionState implements Serializable { public static class RowState implements Serializable { public List cells = new ArrayList(); + + public boolean defaultRow = false; } public List rows = new ArrayList(); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java index 2ced9e16d4..1b27350f25 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridHeaderTest.java @@ -16,12 +16,16 @@ package com.vaadin.tests.components.grid.basicfeatures; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import java.util.Arrays; import java.util.List; import org.junit.Test; import com.vaadin.testbench.TestBenchElement; +import com.vaadin.tests.components.grid.GridElement.GridCellElement; public class GridHeaderTest extends GridStaticSectionTest { @@ -126,7 +130,41 @@ public class GridHeaderTest extends GridStaticSectionTest { assertHeaderTexts(0, 0); } + @Test + public void testDefaultRow() throws Exception { + openTestURL(); + + selectMenuPath("Component", "Columns", "Column 0", "Sortable"); + + GridCellElement headerCell = getGridElement().getHeaderCell(0, 0); + + headerCell.click(); + + assertTrue(hasClassName(headerCell, "sort-asc")); + + headerCell.click(); + + assertFalse(hasClassName(headerCell, "sort-asc")); + assertTrue(hasClassName(headerCell, "sort-desc")); + + selectMenuPath("Component", "Header", "Prepend row"); + selectMenuPath("Component", "Header", "Default row", "Top"); + + assertFalse(hasClassName(headerCell, "sort-desc")); + headerCell = getGridElement().getHeaderCell(0, 0); + assertTrue(hasClassName(headerCell, "sort-desc")); + + selectMenuPath("Component", "Header", "Default row", "Unset"); + + assertFalse(hasClassName(headerCell, "sort-desc")); + } + private void assertHeaderCount(int count) { assertEquals("header count", count, getGridElement().getHeaderCount()); } + + private boolean hasClassName(TestBenchElement element, String name) { + return Arrays.asList(element.getAttribute("class").split(" ")) + .contains(name); + } } diff --git a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java index 21d65d56ac..e013306dc0 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/grid/GridBasicClientFeatures.java @@ -31,6 +31,7 @@ import com.vaadin.client.ui.grid.GridHeader; import com.vaadin.client.ui.grid.GridHeader.HeaderRow; import com.vaadin.client.ui.grid.Renderer; import com.vaadin.client.ui.grid.datasources.ListDataSource; +import com.vaadin.client.ui.grid.datasources.ListSorter; import com.vaadin.client.ui.grid.renderers.DateRenderer; import com.vaadin.client.ui.grid.renderers.HtmlRenderer; import com.vaadin.client.ui.grid.renderers.NumberRenderer; @@ -57,6 +58,7 @@ public class GridBasicClientFeatures extends private final Grid> grid; private final List> data; private final ListDataSource> ds; + private final ListSorter> sorter; /** * Our basic data object @@ -124,6 +126,8 @@ public class GridBasicClientFeatures extends grid.setDataSource(ds); grid.setSelectionMode(SelectionMode.NONE); + sorter = new ListSorter>(grid); + // Create a bunch of grid columns // Data source layout: @@ -247,6 +251,13 @@ public class GridBasicClientFeatures extends !grid.getColumn(index).isVisible()); } }, "Component", "Columns", "Column " + i); + addMenuCommand("Sortable", new ScheduledCommand() { + @Override + public void execute() { + grid.getColumn(index).setSortable( + !grid.getColumn(index).isSortable()); + } + }, "Component", "Columns", "Column " + i); } } @@ -278,6 +289,25 @@ public class GridBasicClientFeatures extends } }, menuPath); + addMenuCommand("Top", new ScheduledCommand() { + @Override + public void execute() { + header.setDefaultRow(header.getRow(0)); + } + }, "Component", "Header", "Default row"); + addMenuCommand("Bottom", new ScheduledCommand() { + @Override + public void execute() { + header.setDefaultRow(header.getRow(header.getRowCount() - 1)); + } + }, "Component", "Header", "Default row"); + addMenuCommand("Unset", new ScheduledCommand() { + @Override + public void execute() { + header.setDefaultRow(null); + } + }, "Component", "Header", "Default row"); + addMenuCommand("Prepend row", new ScheduledCommand() { @Override public void execute() { -- cgit v1.2.3 From e16a0fb28ec04266ab01b6b9ff001b19cf32944b Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Mon, 4 Aug 2014 12:51:37 +0300 Subject: Remove deprecated header/footer API (#13334) Change-Id: I9bddef300a817fd31054515e97bc6924370d3475 --- .../com/vaadin/ui/components/grid/ColumnGroup.java | 165 ----------- .../vaadin/ui/components/grid/ColumnGroupRow.java | 303 --------------------- server/src/com/vaadin/ui/components/grid/Grid.java | 125 --------- .../server/component/grid/GridColumnGroups.java | 260 ------------------ .../vaadin/shared/ui/grid/ColumnGroupRowState.java | 46 ---- .../src/com/vaadin/shared/ui/grid/GridState.java | 5 - .../tests/components/grid/GridColumnGroups.java | 106 ------- .../grid/basicfeatures/GridBasicFeatures.java | 70 ----- 8 files changed, 1080 deletions(-) delete mode 100644 server/src/com/vaadin/ui/components/grid/ColumnGroup.java delete mode 100644 server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java delete mode 100644 server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java delete mode 100644 shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java delete mode 100644 uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java (limited to 'shared/src/com') diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java b/server/src/com/vaadin/ui/components/grid/ColumnGroup.java deleted file mode 100644 index ec676dfb87..0000000000 --- a/server/src/com/vaadin/ui/components/grid/ColumnGroup.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * 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.vaadin.ui.components.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.vaadin.shared.ui.grid.ColumnGroupState; - -/** - * Column groups are used to group columns together for adding common auxiliary - * headers and footers. Columns groups are added to {@link ColumnGroupRow}'s. - * - * @since - * @author Vaadin Ltd - */ -public class ColumnGroup implements Serializable { - - /** - * List of property ids belonging to this group - */ - private List columns; - - /** - * The grid the column group is associated with - */ - private final Grid grid; - - /** - * The column group row the column group is attached to - */ - private final ColumnGroupRow row; - - /** - * The common state between the server and the client - */ - private final ColumnGroupState state; - - /** - * Constructs a new column group - * - * @param grid - * the grid the column group is associated with - * @param state - * the state representing the data of the grid. Sent to the - * client - * @param propertyIds - * the property ids of the columns that belongs to the group - * @param groups - * the sub groups who should be included in this group - * - */ - ColumnGroup(Grid grid, ColumnGroupRow row, ColumnGroupState state, - List propertyIds) { - if (propertyIds == null) { - throw new IllegalArgumentException( - "propertyIds cannot be null. Use empty list instead."); - } - - this.state = state; - this.row = row; - columns = Collections.unmodifiableList(new ArrayList( - propertyIds)); - this.grid = grid; - } - - /** - * Sets the text displayed in the header of the column group. - * - * @param header - * the text displayed in the header of the column - */ - public void setHeaderCaption(String header) { - checkGroupIsAttached(); - state.header = header; - grid.markAsDirty(); - } - - /** - * Sets the text displayed in the header of the column group. - * - * @return the text displayed in the header of the column - */ - public String getHeaderCaption() { - checkGroupIsAttached(); - return state.header; - } - - /** - * Sets the text displayed in the footer of the column group. - * - * @param footer - * the text displayed in the footer of the column - */ - public void setFooterCaption(String footer) { - checkGroupIsAttached(); - state.footer = footer; - grid.markAsDirty(); - } - - /** - * The text displayed in the footer of the column group. - * - * @return the text displayed in the footer of the column - */ - public String getFooterCaption() { - checkGroupIsAttached(); - return state.footer; - } - - /** - * Is a property id in this group or in some sub group of this group. - * - * @param propertyId - * the property id to check for - * @return true if the property id is included in this group. - */ - public boolean isColumnInGroup(Object propertyId) { - if (columns.contains(propertyId)) { - return true; - } - return false; - } - - /** - * Returns a list of property ids where all also the child groups property - * ids are included. - * - * @return a unmodifiable list with all the columns in the group. Includes - * any subgroup columns as well. - */ - public List getColumns() { - return columns; - } - - /** - * Checks if column group is attached to a row and throws an - * {@link IllegalStateException} if it is not. - * - * @throws IllegalStateException - * if the column is no longer attached to any grid - */ - protected void checkGroupIsAttached() throws IllegalStateException { - if (!row.getState().groups.contains(state)) { - throw new IllegalStateException( - "Column Group has been removed from the row."); - } - } -} diff --git a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java b/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java deleted file mode 100644 index a497b5a8a8..0000000000 --- a/server/src/com/vaadin/ui/components/grid/ColumnGroupRow.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * 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.vaadin.ui.components.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.ColumnGroupRowState; -import com.vaadin.shared.ui.grid.ColumnGroupState; - -/** - * A column group row represents an auxiliary header or footer row added to the - * grid. A column group row includes column groups that group columns together. - * - * @since - * @author Vaadin Ltd - */ -public class ColumnGroupRow implements Serializable { - - /** - * The common state shared between the client and server - */ - private final ColumnGroupRowState state; - - /** - * The column groups in this row - */ - private List groups = new ArrayList(); - - /** - * Grid that the group row belongs to - */ - private final Grid grid; - - /** - * The column keys used to identify the column on the client side - */ - private final KeyMapper columnKeys; - - /** - * Constructs a new column group - * - * @param grid - * The grid that the column group is associated to - * @param state - * The shared state which contains the data shared between server - * and client - * @param columnKeys - * The column key mapper for converting property ids to client - * side column identifiers - */ - ColumnGroupRow(Grid grid, ColumnGroupRowState state, - KeyMapper columnKeys) { - this.grid = grid; - this.columnKeys = columnKeys; - this.state = state; - } - - /** - * Gets the shared state for the column group row. Used internally to send - * the group row to the client. - * - * @return The current state of the row - */ - ColumnGroupRowState getState() { - return state; - } - - /** - * Add a new group to the row by using property ids for the columns. - * - * @param propertyIds - * The property ids of the columns that should be included in the - * group. A column can only belong in group on a row at a time. - * @return a column group representing the collection of columns added to - * the group - */ - public ColumnGroup addGroup(Object... propertyIds) - throws IllegalArgumentException { - assert propertyIds != null : "propertyIds cannot be null."; - - for (Object propertyId : propertyIds) { - if (hasColumnBeenGrouped(propertyId)) { - throw new IllegalArgumentException("Column " - + String.valueOf(propertyId) - + " already belongs to another group."); - } - } - - validateNewGroupProperties(Arrays.asList(propertyIds)); - - ColumnGroupState state = new ColumnGroupState(); - for (Object propertyId : propertyIds) { - assert propertyId != null : "null items in columns array not supported."; - state.columns.add(columnKeys.key(propertyId)); - } - this.state.groups.add(state); - - ColumnGroup group = new ColumnGroup(grid, this, state, - Arrays.asList(propertyIds)); - groups.add(group); - - grid.markAsDirty(); - return group; - } - - private void validateNewGroupProperties(List propertyIds) - throws IllegalArgumentException { - - /* - * Validate parent grouping - */ - int rowIndex = grid.getColumnGroupRows().indexOf(this); - int parentRowIndex = rowIndex - 1; - - // Get the parent row of this row. - ColumnGroupRow parentRow = null; - if (parentRowIndex > -1) { - parentRow = grid.getColumnGroupRows().get(parentRowIndex); - } - - if (parentRow == null) { - // A parentless row is always valid and is usually the first row - // added to the grid - return; - } - - for (Object id : propertyIds) { - if (parentRow.hasColumnBeenGrouped(id)) { - /* - * If a property has been grouped in the parent row then all of - * the properties in the parent group also needs to be included - * in the child group for the groups to be valid - */ - ColumnGroup parentGroup = parentRow.getGroupForProperty(id); - if (!propertyIds.containsAll(parentGroup.getColumns())) { - throw new IllegalArgumentException( - "Grouped properties overlaps previous grouping bounderies"); - } - } - } - } - - /** - * Add a new group to the row by using column instances. - * - * @param columns - * the columns that should belong to the group - * @return a column group representing the collection of columns added to - * the group - */ - public ColumnGroup addGroup(GridColumn... columns) - throws IllegalArgumentException { - assert columns != null : "columns cannot be null"; - - List propertyIds = new ArrayList(); - for (GridColumn column : columns) { - assert column != null : "null items in columns array not supported."; - - String columnId = column.getState().id; - Object propertyId = grid.getPropertyIdByColumnId(columnId); - propertyIds.add(propertyId); - } - return addGroup(propertyIds.toArray()); - } - - /** - * Add a new group to the row by using other already greated groups - * - * @param groups - * the subgroups of the group - * @return a column group representing the collection of columns added to - * the group - * - */ - public ColumnGroup addGroup(ColumnGroup... groups) - throws IllegalArgumentException { - assert groups != null : "groups cannot be null"; - - // Gather all groups columns into one list - List propertyIds = new ArrayList(); - for (ColumnGroup group : groups) { - propertyIds.addAll(group.getColumns()); - } - - validateNewGroupProperties(propertyIds); - - ColumnGroupState state = new ColumnGroupState(); - ColumnGroup group = new ColumnGroup(grid, this, state, propertyIds); - this.groups.add(group); - - // Update state - for (Object propertyId : group.getColumns()) { - state.columns.add(columnKeys.key(propertyId)); - } - this.state.groups.add(state); - - grid.markAsDirty(); - return group; - } - - /** - * Removes a group from the row. Does not remove the group from subgroups, - * to remove it from the subgroup invoke removeGroup on the subgroup. - * - * @param group - * the group to remove - */ - public void removeGroup(ColumnGroup group) { - int index = groups.indexOf(group); - groups.remove(index); - state.groups.remove(index); - grid.markAsDirty(); - } - - /** - * Get the groups in the row. - * - * @return unmodifiable list of groups in this row - */ - public List getGroups() { - return Collections.unmodifiableList(groups); - } - - /** - * Checks if a property id has been added to a group in this row. - * - * @param propertyId - * the property id to check for - * @return true if the column is included in a group - */ - private boolean hasColumnBeenGrouped(Object propertyId) { - return getGroupForProperty(propertyId) != null; - } - - private ColumnGroup getGroupForProperty(Object propertyId) { - for (ColumnGroup group : groups) { - if (group.isColumnInGroup(propertyId)) { - return group; - } - } - return null; - } - - /** - * Is the header visible for the row. - * - * @return true if header is visible - */ - public boolean isHeaderVisible() { - return state.headerVisible; - } - - /** - * Sets the header visible for the row. - * - * @param visible - * should the header be shown - */ - public void setHeaderVisible(boolean visible) { - state.headerVisible = visible; - grid.markAsDirty(); - } - - /** - * Is the footer visible for the row. - * - * @return true if footer is visible - */ - public boolean isFooterVisible() { - return state.footerVisible; - } - - /** - * Sets the footer visible for the row. - * - * @param visible - * should the footer be shown - */ - public void setFooterVisible(boolean visible) { - state.footerVisible = visible; - grid.markAsDirty(); - } - -} diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 514a0496e2..5e21c7b70a 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -40,7 +40,6 @@ import com.vaadin.data.Container.Sortable; import com.vaadin.data.RpcDataProviderExtension; import com.vaadin.data.RpcDataProviderExtension.DataProviderKeyMapper; import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.ColumnGroupRowState; import com.vaadin.shared.ui.grid.GridClientRpc; import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridServerRpc; @@ -138,11 +137,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { */ private final KeyMapper columnKeys = new KeyMapper(); - /** - * The column groups added to the grid - */ - private final List columnGroupRows = new ArrayList(); - /** * The current sort order */ @@ -464,125 +458,6 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { return columns.get(propertyId); } - /** - * Sets the header rows visible. - * - * @param visible - * true if the header rows should be visible - */ - @Deprecated - public void setColumnHeadersVisible(boolean visible) { - getHeader().setVisible(visible); - } - - /** - * Are the header rows visible? - * - * @return true if the headers of the columns are visible - */ - @Deprecated - public boolean isColumnHeadersVisible() { - return getHeader().isVisible(); - } - - /** - * Sets the footer rows visible. - * - * @param visible - * true if the footer rows should be visible - */ - @Deprecated - public void setColumnFootersVisible(boolean visible) { - getFooter().setVisible(visible); - } - - /** - * Are the footer rows visible. - * - * @return true if the footer rows should be visible - */ - @Deprecated - public boolean isColumnFootersVisible() { - return getFooter().isVisible(); - } - - /** - *

- * Adds a new column group to the grid. - * - *

- * Column group rows are rendered in the header and footer of the grid. - * Column group rows are made up of column groups which groups together - * columns for adding a common auxiliary header or footer for the columns. - *

- *

- * - *

- * Example usage: - * - *

-     * // Add a new column group row to the grid
-     * ColumnGroupRow row = grid.addColumnGroupRow();
-     * 
-     * // Group "Column1" and "Column2" together to form a header in the row
-     * ColumnGroup column12 = row.addGroup("Column1", "Column2");
-     * 
-     * // Set a common header for "Column1" and "Column2"
-     * column12.setHeader("Column 1&2");
-     * 
- * - *

- * - * @return a column group instance you can use to add column groups - */ - @Deprecated - public ColumnGroupRow addColumnGroupRow() { - ColumnGroupRowState state = new ColumnGroupRowState(); - ColumnGroupRow row = new ColumnGroupRow(this, state, columnKeys); - columnGroupRows.add(row); - getState().columnGroupRows.add(state); - return row; - } - - /** - * Adds a new column group to the grid at a specific index - * - * @param rowIndex - * the index of the row - * @return a column group instance you can use to add column groups - */ - @Deprecated - public ColumnGroupRow addColumnGroupRow(int rowIndex) { - ColumnGroupRowState state = new ColumnGroupRowState(); - ColumnGroupRow row = new ColumnGroupRow(this, state, columnKeys); - columnGroupRows.add(rowIndex, row); - getState().columnGroupRows.add(rowIndex, state); - return row; - } - - /** - * Removes a column group. - * - * @param row - * the row to remove - */ - @Deprecated - public void removeColumnGroupRow(ColumnGroupRow row) { - columnGroupRows.remove(row); - getState().columnGroupRows.remove(row.getState()); - } - - /** - * Gets the column group rows. - * - * @return an unmodifiable list of column group rows - */ - @Deprecated - public List getColumnGroupRows() { - return Collections.unmodifiableList(new ArrayList( - columnGroupRows)); - } - /** * Used internally by the {@link Grid} to get a {@link GridColumn} by * referencing its generated state id. Also used by {@link GridColumn} to diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java b/server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java deleted file mode 100644 index 21bfbbb37e..0000000000 --- a/server/tests/src/com/vaadin/tests/server/component/grid/GridColumnGroups.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * 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.vaadin.tests.server.component.grid; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.KeyMapper; -import com.vaadin.shared.ui.grid.GridState; -import com.vaadin.ui.components.grid.ColumnGroup; -import com.vaadin.ui.components.grid.ColumnGroupRow; -import com.vaadin.ui.components.grid.Grid; - -public class GridColumnGroups { - - private Grid grid; - - private GridState state; - - private Method getStateMethod; - - private Field columnIdGeneratorField; - - private KeyMapper columnIdMapper; - - @Before - public void setup() throws Exception { - IndexedContainer ds = new IndexedContainer(); - for (int c = 0; c < 10; c++) { - ds.addContainerProperty("column" + c, String.class, ""); - } - grid = new Grid(ds); - - getStateMethod = Grid.class.getDeclaredMethod("getState"); - getStateMethod.setAccessible(true); - - state = (GridState) getStateMethod.invoke(grid); - - columnIdGeneratorField = Grid.class.getDeclaredField("columnKeys"); - columnIdGeneratorField.setAccessible(true); - - columnIdMapper = (KeyMapper) columnIdGeneratorField.get(grid); - } - - @Test - public void testColumnGroupRows() throws Exception { - - // No column group rows by default - List rows = grid.getColumnGroupRows(); - assertEquals(0, rows.size()); - - // Add some rows - ColumnGroupRow row1 = grid.addColumnGroupRow(); - ColumnGroupRow row3 = grid.addColumnGroupRow(); - ColumnGroupRow row2 = grid.addColumnGroupRow(1); - - rows = grid.getColumnGroupRows(); - assertEquals(3, rows.size()); - assertEquals(row1, rows.get(0)); - assertEquals(row2, rows.get(1)); - assertEquals(row3, rows.get(2)); - - // Header should be visible by default, footer should not - assertTrue(row1.isHeaderVisible()); - assertFalse(row1.isFooterVisible()); - - row1.setHeaderVisible(false); - assertFalse(row1.isHeaderVisible()); - row1.setHeaderVisible(true); - assertTrue(row1.isHeaderVisible()); - - row1.setFooterVisible(true); - assertTrue(row1.isFooterVisible()); - row1.setFooterVisible(false); - assertFalse(row1.isFooterVisible()); - - row1.setHeaderVisible(true); - row1.setFooterVisible(true); - assertTrue(row1.isHeaderVisible()); - assertTrue(row1.isFooterVisible()); - - row1.setHeaderVisible(false); - row1.setFooterVisible(false); - assertFalse(row1.isHeaderVisible()); - assertFalse(row1.isFooterVisible()); - } - - @Test - public void testColumnGroupsInState() throws Exception { - - // Add a new row - ColumnGroupRow row = grid.addColumnGroupRow(); - assertTrue(state.columnGroupRows.size() == 1); - - // Add a group by property id - ColumnGroup columns12 = row.addGroup("column1", "column2"); - assertTrue(state.columnGroupRows.get(0).groups.size() == 1); - - // Set header of column - columns12.setHeaderCaption("Column12"); - assertEquals("Column12", - state.columnGroupRows.get(0).groups.get(0).header); - - // Set footer of column - columns12.setFooterCaption("Footer12"); - assertEquals("Footer12", - state.columnGroupRows.get(0).groups.get(0).footer); - - // Add another group by column instance - ColumnGroup columns34 = row.addGroup(grid.getColumn("column3"), - grid.getColumn("column4")); - assertTrue(state.columnGroupRows.get(0).groups.size() == 2); - - // add another group row - ColumnGroupRow row2 = grid.addColumnGroupRow(); - assertTrue(state.columnGroupRows.size() == 2); - - // add a group by combining the two previous groups - ColumnGroup columns1234 = row2.addGroup(columns12, columns34); - assertTrue(columns1234.getColumns().size() == 4); - - // Insert a group as the second group - ColumnGroupRow newRow2 = grid.addColumnGroupRow(1); - assertTrue(state.columnGroupRows.size() == 3); - } - - @Test - public void testAddingColumnGroups() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - - // By property id - ColumnGroup columns01 = row.addGroup("column0", "column1"); - assertEquals(2, columns01.getColumns().size()); - assertEquals("column0", columns01.getColumns().get(0)); - assertTrue(columns01.isColumnInGroup("column0")); - assertEquals("column1", columns01.getColumns().get(1)); - assertTrue(columns01.isColumnInGroup("column1")); - - // By grid column - ColumnGroup columns23 = row.addGroup(grid.getColumn("column2"), - grid.getColumn("column3")); - assertEquals(2, columns23.getColumns().size()); - assertEquals("column2", columns23.getColumns().get(0)); - assertTrue(columns23.isColumnInGroup("column2")); - assertEquals("column3", columns23.getColumns().get(1)); - assertTrue(columns23.isColumnInGroup("column3")); - - // Combine groups - ColumnGroupRow row2 = grid.addColumnGroupRow(); - ColumnGroup columns0123 = row2.addGroup(columns01, columns23); - assertEquals(4, columns0123.getColumns().size()); - assertEquals("column0", columns0123.getColumns().get(0)); - assertTrue(columns0123.isColumnInGroup("column0")); - assertEquals("column1", columns0123.getColumns().get(1)); - assertTrue(columns0123.isColumnInGroup("column1")); - assertEquals("column2", columns0123.getColumns().get(2)); - assertTrue(columns0123.isColumnInGroup("column2")); - assertEquals("column3", columns0123.getColumns().get(3)); - assertTrue(columns0123.isColumnInGroup("column3")); - } - - @Test - public void testColumnGroupHeadersAndFooters() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - ColumnGroup group = row.addGroup("column1", "column2"); - - // Header - assertNull(group.getHeaderCaption()); - group.setHeaderCaption("My header"); - assertEquals("My header", group.getHeaderCaption()); - group.setHeaderCaption(null); - assertNull(group.getHeaderCaption()); - - // Footer - assertNull(group.getFooterCaption()); - group.setFooterCaption("My footer"); - assertEquals("My footer", group.getFooterCaption()); - group.setFooterCaption(null); - assertNull(group.getFooterCaption()); - } - - @Test - public void testColumnGroupDetachment() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - ColumnGroup group = row.addGroup("column1", "column2"); - - // Remove group - row.removeGroup(group); - - try { - group.setHeaderCaption("Header"); - fail("Should throw exception for setting header caption on detached group"); - } catch (IllegalStateException ise) { - - } - - try { - group.setFooterCaption("Footer"); - fail("Should throw exception for setting footer caption on detached group"); - } catch (IllegalStateException ise) { - - } - } - - @Test - public void testColumnGroupLimits() throws Exception { - - ColumnGroupRow row = grid.addColumnGroupRow(); - row.addGroup("column1", "column2"); - row.addGroup("column3", "column4"); - - try { - row.addGroup("column2", "column3"); - fail("Adding a group with already grouped properties should throw exception"); - } catch (IllegalArgumentException iae) { - - } - - ColumnGroupRow row2 = grid.addColumnGroupRow(); - - try { - row2.addGroup("column2", "column3"); - fail("Adding a group that breaks previous grouping boundaries should throw exception"); - } catch (IllegalArgumentException iae) { - - } - - // This however should not throw an exception as it spans completely - // over the parent rows groups - row2.addGroup("column1", "column2", "column3", "column4"); - - } -} diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java deleted file mode 100644 index d3d5ea2495..0000000000 --- a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupRowState.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.shared.ui.grid; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -/** - * The column group row data shared between the server and client - * - * @since - * @author Vaadin Ltd - */ -public class ColumnGroupRowState implements Serializable { - - /** - * The groups that has been added to the row - */ - public List groups = new ArrayList(); - - /** - * Is the header shown - */ - public boolean headerVisible = true; - - /** - * Is the footer shown - */ - public boolean footerVisible = false; - -} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 68ee64dfe4..54acc80127 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -102,11 +102,6 @@ public class GridState extends AbstractComponentState { public GridStaticSectionState footer = new GridStaticSectionState(); - /** - * The column groups added to the grid - */ - public List columnGroupRows = new ArrayList(); - /** * The id for the last frozen column. * diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java b/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java deleted file mode 100644 index f1199301d9..0000000000 --- a/uitest/src/com/vaadin/tests/components/grid/GridColumnGroups.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2000-2014 Vaadin Ltd. - * - * 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.vaadin.tests.components.grid; - -import com.vaadin.data.util.IndexedContainer; -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.components.grid.ColumnGroup; -import com.vaadin.ui.components.grid.ColumnGroupRow; -import com.vaadin.ui.components.grid.Grid; -import com.vaadin.ui.components.grid.GridColumn; - -public class GridColumnGroups extends AbstractTestUI { - - private final int COLUMNS = 4; - - @Override - protected void setup(VaadinRequest request) { - - // Setup grid - IndexedContainer ds = new IndexedContainer(); - for (int col = 0; col < COLUMNS; col++) { - ds.addContainerProperty("Column" + col, String.class, ""); - } - Grid grid = new Grid(ds); - addComponent(grid); - - /*- - * --------------------------------------------- - * | Header 1 | <- Auxiliary row 2 - * |-------------------------------------------| - * | Header 2 | Header 3 | <- Auxiliary row 1 - * |-------------------------------------------| - * | Column 1 | Column 2 | Column 3 | Column 4 | <- Column headers - * --------------------------------------------| - * | ... | ... | ... | ... | - * | ... | ... | ... | ... | - * --------------------------------------------| - * | Column 1 | Column 2 | Column 3 | Column 4 | <- Column footers - * --------------------------------------------| - * | Footer 2 | Footer 3 | <- Auxiliary row 1 - * --------------------------------------------| - * | Footer 1 | <- Auxiliary row 2 - * --------------------------------------------- - -*/ - - // Set column footers (headers are generated automatically) - grid.setColumnFootersVisible(true); - for (Object propertyId : ds.getContainerPropertyIds()) { - GridColumn column = grid.getColumn(propertyId); - column.setFooterCaption(String.valueOf(propertyId)); - } - - // First auxiliary row - ColumnGroupRow auxRow1 = grid.addColumnGroupRow(); - - // Using property id to create a column group - ColumnGroup columns12 = auxRow1.addGroup("Column0", "Column1"); - columns12.setHeaderCaption("Header 2"); - columns12.setFooterCaption("Footer 2"); - - // Using grid columns to create a column group - GridColumn column3 = grid.getColumn("Column2"); - GridColumn column4 = grid.getColumn("Column3"); - ColumnGroup columns34 = auxRow1.addGroup(column3, column4); - columns34.setHeaderCaption("Header 3"); - columns34.setFooterCaption("Footer 3"); - - // Second auxiliary row - ColumnGroupRow auxRow2 = grid.addColumnGroupRow(); - - // Using previous groups to create a column group - ColumnGroup columns1234 = auxRow2.addGroup(columns12, columns34); - columns1234.setHeaderCaption("Header 1"); - columns1234.setFooterCaption("Footer 1"); - - } - - @Override - protected String getTestDescription() { - return "Grid should support headers and footer groups"; - } - - @Override - protected Integer getTicketNumber() { - return 12894; - } - -} diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index bff16d8db7..9ab2c98bdb 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -31,8 +31,6 @@ import com.vaadin.data.util.IndexedContainer; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.SortDirection; import com.vaadin.tests.components.AbstractComponentTest; -import com.vaadin.ui.components.grid.ColumnGroup; -import com.vaadin.ui.components.grid.ColumnGroupRow; import com.vaadin.ui.components.grid.Grid; import com.vaadin.ui.components.grid.Grid.SelectionMode; import com.vaadin.ui.components.grid.GridColumn; @@ -178,8 +176,6 @@ public class GridBasicFeatures extends AbstractComponentTest { createFooterActions(); - createColumnGroupActions(); - createRowActions(); addHeightActions(); @@ -451,72 +447,6 @@ public class GridBasicFeatures extends AbstractComponentTest { return "Column " + c; } - protected void createColumnGroupActions() { - createCategory("Column groups", null); - - createClickAction("Add group row", "Column groups", - new Command() { - - @Override - public void execute(Grid grid, String value, Object data) { - final ColumnGroupRow row = grid.addColumnGroupRow(); - columnGroupRows++; - createCategory("Column group row " + columnGroupRows, - "Column groups"); - - createBooleanAction("Header Visible", - "Column group row " + columnGroupRows, true, - new Command() { - - @Override - public void execute(Grid grid, - Boolean value, Object columnIndex) { - row.setHeaderVisible(value); - } - }, row); - - createBooleanAction("Footer Visible", - "Column group row " + columnGroupRows, false, - new Command() { - - @Override - public void execute(Grid grid, - Boolean value, Object columnIndex) { - row.setFooterVisible(value); - } - }, row); - - for (int i = 0; i < COLUMNS; i++) { - final int columnIndex = i; - createClickAction("Group Column " + columnIndex - + " & " + (columnIndex + 1), - "Column group row " + columnGroupRows, - new Command() { - - @Override - public void execute(Grid c, - Integer value, Object data) { - final ColumnGroup group = row - .addGroup( - "Column" + value, - "Column" - + (value + 1)); - - group.setHeaderCaption("Column " - + value + " & " - + (value + 1)); - - group.setFooterCaption("Column " - + value + " & " - + (value + 1)); - } - }, i, row); - } - } - }, null, null); - - } - protected void createRowActions() { createCategory("Body rows", null); -- cgit v1.2.3 From df68ce33bde680de12cdf6457311c35662252da2 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Wed, 30 Jul 2014 15:06:36 +0300 Subject: Add support for server side colspans in Headers and Footers (#13334) Change-Id: I311aade38f4e725405a190ca1b6f114b4ac07053 --- .../com/vaadin/client/ui/grid/FlyweightCell.java | 8 ++ client/src/com/vaadin/client/ui/grid/Grid.java | 7 +- .../com/vaadin/client/ui/grid/GridConnector.java | 19 +++- .../com/vaadin/ui/components/grid/GridFooter.java | 2 +- .../com/vaadin/ui/components/grid/GridHeader.java | 4 +- .../ui/components/grid/GridStaticSection.java | 109 +++++++++++++++++++-- .../server/component/grid/GridStaticSection.java | 105 ++++++++++++++++++++ .../shared/ui/grid/GridStaticSectionState.java | 2 + .../vaadin/tests/components/grid/GridColspans.java | 81 +++++++++++++++ .../tests/components/grid/GridColspansTest.java | 73 ++++++++++++++ .../vaadin/tests/components/grid/GridElement.java | 5 + 11 files changed, 403 insertions(+), 12 deletions(-) create mode 100644 server/tests/src/com/vaadin/tests/server/component/grid/GridStaticSection.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/GridColspans.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/GridColspansTest.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/FlyweightCell.java b/client/src/com/vaadin/client/ui/grid/FlyweightCell.java index 17301214c8..de003f865c 100644 --- a/client/src/com/vaadin/client/ui/grid/FlyweightCell.java +++ b/client/src/com/vaadin/client/ui/grid/FlyweightCell.java @@ -78,6 +78,14 @@ public class FlyweightCell { return element; } + /** + * Return the colspan attribute of the element of the cell. + */ + public int getColSpan() { + assertSetup(); + return element.getPropertyInt(COLSPAN_ATTR); + } + /** * Sets the DOM element for this FlyweightCell, either a TD or * a TH. It is the caller's responsibility to actually insert diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index f800a8cb3b..f280b26493 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -123,10 +123,13 @@ public class Grid extends Composite implements RowContainer cellContainer) { int cellRow = cell.getRow(); int cellColumn = cell.getColumn(); + int colSpan = cell.getColSpan(); + boolean columnActive = Range.withLength(cellColumn, colSpan) + .contains(activeColumn); if (cellContainer == container) { // Cell is in the current container - if (cellRow == activeRow && cellColumn == activeColumn) { + if (cellRow == activeRow && columnActive) { if (cellWithActiveStyle != cell.getElement()) { // Cell is correct but it does not have active style if (cellWithActiveStyle != null) { @@ -152,7 +155,7 @@ public class Grid extends Composite implements || cellContainer == escalator.getFooter()) { // Correct header and footer column also needs highlighting setStyleName(cell.getElement(), headerFooterActiveStyleName, - cellColumn == activeColumn); + columnActive); } } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 2a06aba3e6..2bbedaaecf 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -36,11 +36,13 @@ import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.grid.GridHeader.HeaderRow; +import com.vaadin.client.ui.grid.GridStaticSection.StaticCell; import com.vaadin.client.ui.grid.GridStaticSection.StaticRow; import com.vaadin.client.ui.grid.renderers.AbstractRendererConnector; import com.vaadin.client.ui.grid.selection.AbstractRowHandleSelectionModel; import com.vaadin.client.ui.grid.selection.SelectionChangeEvent; import com.vaadin.client.ui.grid.selection.SelectionChangeHandler; +import com.vaadin.client.ui.grid.selection.SelectionModel; import com.vaadin.client.ui.grid.selection.SelectionModelMulti; import com.vaadin.client.ui.grid.selection.SelectionModelNone; import com.vaadin.client.ui.grid.selection.SelectionModelSingle; @@ -288,9 +290,24 @@ public class GridConnector extends AbstractComponentConnector { assert rowState.cells.size() == getWidget().getColumnCount(); + int diff = 1; + if (getWidget().getSelectionModel() instanceof SelectionModel.None) { + diff = 0; + } + int i = 0; for (CellState cellState : rowState.cells) { - row.getCell(i++).setText(cellState.text); + StaticCell cell = row.getCell(diff + (i++)); + cell.setText(cellState.text); + } + + for (List group : rowState.cellGroups) { + GridColumn[] columns = new GridColumn[group.size()]; + i = 0; + for (Integer colIndex : group) { + columns[i++] = getWidget().getColumn(diff + colIndex); + } + row.join(columns); } if (section instanceof GridHeader && rowState.defaultRow) { diff --git a/server/src/com/vaadin/ui/components/grid/GridFooter.java b/server/src/com/vaadin/ui/components/grid/GridFooter.java index e4a7eab5d1..84b2b70090 100644 --- a/server/src/com/vaadin/ui/components/grid/GridFooter.java +++ b/server/src/com/vaadin/ui/components/grid/GridFooter.java @@ -54,7 +54,7 @@ public class GridFooter extends GridStaticSection { } @Override - protected GridStaticSectionState getState() { + protected GridStaticSectionState getSectionState() { return footerState; } diff --git a/server/src/com/vaadin/ui/components/grid/GridHeader.java b/server/src/com/vaadin/ui/components/grid/GridHeader.java index f8bd3c6642..67f7bfdf69 100644 --- a/server/src/com/vaadin/ui/components/grid/GridHeader.java +++ b/server/src/com/vaadin/ui/components/grid/GridHeader.java @@ -57,7 +57,7 @@ public class GridHeader extends GridStaticSection { HeaderRow row = createRow(); rows.add(row); setDefaultRow(row); - getState().rows.add(row.getRowState()); + getSectionState().rows.add(row.getRowState()); } /** @@ -103,7 +103,7 @@ public class GridHeader extends GridStaticSection { } @Override - protected GridStaticSectionState getState() { + protected GridStaticSectionState getSectionState() { return headerState; } diff --git a/server/src/com/vaadin/ui/components/grid/GridStaticSection.java b/server/src/com/vaadin/ui/components/grid/GridStaticSection.java index 80d822e7bc..8c983052ea 100644 --- a/server/src/com/vaadin/ui/components/grid/GridStaticSection.java +++ b/server/src/com/vaadin/ui/components/grid/GridStaticSection.java @@ -17,6 +17,10 @@ package com.vaadin.ui.components.grid; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -49,6 +53,7 @@ abstract class GridStaticSection> private RowState rowState = new RowState(); protected GridStaticSection section; private Map cells = new LinkedHashMap(); + private Collection> cellGroups = new HashSet>(); protected StaticRow(GridStaticSection section) { this.section = section; @@ -83,6 +88,98 @@ abstract class GridStaticSection> public CELLTYPE getCell(Object propertyId) { return cells.get(propertyId); } + + /** + * Merges cells in a row + * + * @param cells + * The cells to be merged + * @return The first cell of the merged cells + */ + protected CELLTYPE join(List cells) { + assert cells.size() > 1 : "You cannot merge less than 2 cells together"; + + // Ensure no cell is already grouped + for (CELLTYPE cell : cells) { + if (getCellGroupForCell(cell) != null) { + throw new IllegalStateException("Cell " + cell.getText() + + " is already grouped."); + } + } + + // Ensure continuous range + Iterator cellIterator = this.cells.values().iterator(); + CELLTYPE current = null; + int firstIndex = 0; + + while (cellIterator.hasNext()) { + current = cellIterator.next(); + if (current == cells.get(0)) { + break; + } + firstIndex++; + } + + for (int i = 1; i < cells.size(); ++i) { + current = cellIterator.next(); + + if (current != cells.get(i)) { + throw new IllegalStateException( + "Cell range must be a continous range"); + } + } + + // Create a new group + final ArrayList cellGroup = new ArrayList(cells); + cellGroups.add(cellGroup); + + // Add group to state + List stateGroup = new ArrayList(); + for (int i = 0; i < cells.size(); ++i) { + stateGroup.add(firstIndex + i); + } + rowState.cellGroups.add(stateGroup); + section.markAsDirty(); + + // Returns first cell of group + return cells.get(0); + } + + /** + * Merges columns cells in a row + * + * @param properties + * The column properties which header should be merged + * @return The remaining visible cell after the merge + */ + public CELLTYPE join(Object... properties) { + List cells = new ArrayList(); + for (int i = 0; i < properties.length; ++i) { + cells.add(getCell(properties[i])); + } + + return join(cells); + } + + /** + * Merges columns cells in a row + * + * @param cells + * The cells to merge. Must be from the same row. + * @return The remaining visible cell after the merge + */ + public CELLTYPE join(CELLTYPE... cells) { + return join(Arrays.asList(cells)); + } + + private List getCellGroupForCell(CELLTYPE cell) { + for (List group : cellGroups) { + if (group.contains(cell)) { + return group; + } + } + return null; + } } /** @@ -148,8 +245,8 @@ abstract class GridStaticSection> * true to show this section, false to hide */ public void setVisible(boolean visible) { - if (getState().visible != visible) { - getState().visible = visible; + if (getSectionState().visible != visible) { + getSectionState().visible = visible; markAsDirty(); } } @@ -160,7 +257,7 @@ abstract class GridStaticSection> * @return true if visible, false otherwise. */ public boolean isVisible() { - return getState().visible; + return getSectionState().visible; } /** @@ -174,7 +271,7 @@ abstract class GridStaticSection> */ public ROWTYPE removeRow(int rowIndex) { ROWTYPE row = rows.remove(rowIndex); - getState().rows.remove(rowIndex); + getSectionState().rows.remove(rowIndex); markAsDirty(); return row; @@ -240,7 +337,7 @@ abstract class GridStaticSection> public ROWTYPE addRowAt(int index) { ROWTYPE row = createRow(); rows.add(index, row); - getState().rows.add(index, row.getRowState()); + getSectionState().rows.add(index, row.getRowState()); Indexed dataSource = grid.getContainerDatasource(); for (Object id : dataSource.getContainerPropertyIds()) { @@ -260,7 +357,7 @@ abstract class GridStaticSection> return rows.size(); } - protected abstract GridStaticSectionState getState(); + protected abstract GridStaticSectionState getSectionState(); protected abstract ROWTYPE createRow(); diff --git a/server/tests/src/com/vaadin/tests/server/component/grid/GridStaticSection.java b/server/tests/src/com/vaadin/tests/server/component/grid/GridStaticSection.java new file mode 100644 index 0000000000..e89f6a8c6e --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/grid/GridStaticSection.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.GridFooter; +import com.vaadin.ui.components.grid.GridFooter.FooterRow; +import com.vaadin.ui.components.grid.GridHeader; +import com.vaadin.ui.components.grid.GridHeader.HeaderRow; + +public class GridStaticSection { + + private Indexed dataSource = new IndexedContainer(); + private Grid grid; + + @Before + public void setUp() { + dataSource.addContainerProperty("firstName", String.class, ""); + dataSource.addContainerProperty("lastName", String.class, ""); + dataSource.addContainerProperty("streetAddress", String.class, ""); + dataSource.addContainerProperty("zipCode", Integer.class, null); + grid = new Grid(dataSource); + } + + @Test + public void testAddAndRemoveHeaders() { + + final GridHeader section = grid.getHeader(); + assertEquals(1, section.getRowCount()); + section.prependRow(); + assertEquals(2, section.getRowCount()); + section.removeRow(0); + assertEquals(1, section.getRowCount()); + section.removeRow(0); + assertEquals(0, section.getRowCount()); + assertEquals(null, section.getDefaultRow()); + HeaderRow row = section.appendRow(); + assertEquals(1, section.getRowCount()); + assertEquals(null, section.getDefaultRow()); + section.setDefaultRow(row); + assertEquals(row, section.getDefaultRow()); + } + + @Test + public void testAddAndRemoveFooters() { + final GridFooter section = grid.getFooter(); + + // By default there are no footer rows + assertEquals(0, section.getRowCount()); + FooterRow row = section.appendRow(); + + assertEquals(1, section.getRowCount()); + section.prependRow(); + assertEquals(2, section.getRowCount()); + assertEquals(row, section.getRow(1)); + section.removeRow(0); + assertEquals(1, section.getRowCount()); + section.removeRow(0); + assertEquals(0, section.getRowCount()); + } + + @Test + public void testJoinHeaderCells() { + final GridHeader section = grid.getHeader(); + HeaderRow mergeRow = section.prependRow(); + mergeRow.join("firstName", "lastName").setText("Name"); + mergeRow.join(mergeRow.getCell("streetAddress"), + mergeRow.getCell("zipCode")); + } + + @Test(expected = IllegalStateException.class) + public void testJoinHeaderCellsIncorrectly() { + final GridHeader section = grid.getHeader(); + HeaderRow mergeRow = section.prependRow(); + mergeRow.join("firstName", "zipCode").setText("Name"); + } + + @Test + public void testJoinAllFooterrCells() { + final GridFooter section = grid.getFooter(); + FooterRow mergeRow = section.prependRow(); + mergeRow.join(dataSource.getContainerPropertyIds().toArray()).setText( + "All the stuff."); + } +} diff --git a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java index 859e01f089..41f56199da 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java @@ -35,6 +35,8 @@ public class GridStaticSectionState implements Serializable { public List cells = new ArrayList(); public boolean defaultRow = false; + + public List> cellGroups = new ArrayList>(); } public List rows = new ArrayList(); diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColspans.java b/uitest/src/com/vaadin/tests/components/grid/GridColspans.java new file mode 100644 index 0000000000..be12c2bcb2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridColspans.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.grid; + +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Item; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.components.grid.Grid; +import com.vaadin.ui.components.grid.GridFooter; +import com.vaadin.ui.components.grid.GridFooter.FooterRow; +import com.vaadin.ui.components.grid.GridHeader; +import com.vaadin.ui.components.grid.GridHeader.HeaderRow; +import com.vaadin.ui.components.grid.renderers.NumberRenderer; + +public class GridColspans extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Indexed dataSource = new IndexedContainer(); + Grid grid; + + dataSource.addContainerProperty("firstName", String.class, ""); + dataSource.addContainerProperty("lastName", String.class, ""); + dataSource.addContainerProperty("streetAddress", String.class, ""); + dataSource.addContainerProperty("zipCode", Integer.class, null); + dataSource.addContainerProperty("city", String.class, ""); + Item i = dataSource.addItem(0); + i.getItemProperty("firstName").setValue("Rudolph"); + i.getItemProperty("lastName").setValue("Reindeer"); + i.getItemProperty("streetAddress").setValue("Ruukinkatu 2-4"); + i.getItemProperty("zipCode").setValue(20540); + i.getItemProperty("city").setValue("Turku"); + grid = new Grid(dataSource); + grid.setWidth("600px"); + grid.getColumn("zipCode").setRenderer(new NumberRenderer()); + addComponent(grid); + + GridHeader header = grid.getHeader(); + HeaderRow row = header.prependRow(); + row.join("firstName", "lastName").setText("Full Name"); + row.join("streetAddress", "zipCode", "city").setText("Address"); + header.prependRow() + .join(dataSource.getContainerPropertyIds().toArray()) + .setText("All the stuff"); + + GridFooter footer = grid.getFooter(); + FooterRow footerRow = footer.appendRow(); + footerRow.join("firstName", "lastName").setText("Full Name"); + footerRow.join("streetAddress", "zipCode", "city").setText("Address"); + footer.appendRow().join(dataSource.getContainerPropertyIds().toArray()) + .setText("All the stuff"); + + footer.setVisible(true); + } + + @Override + protected String getTestDescription() { + return "Grid header and footer colspans"; + } + + @Override + protected Integer getTicketNumber() { + return 13334; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridColspansTest.java b/uitest/src/com/vaadin/tests/components/grid/GridColspansTest.java new file mode 100644 index 0000000000..dad9399466 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridColspansTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.grid; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.tests.annotations.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +@TestCategory("grid") +public class GridColspansTest extends MultiBrowserTest { + + @Test + public void testHeaderColSpans() { + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + assertEquals("5", grid.getHeaderCell(0, 1).getAttribute("colspan")); + assertEquals("2", grid.getHeaderCell(1, 1).getAttribute("colspan")); + assertEquals("3", grid.getHeaderCell(1, 3).getAttribute("colspan")); + } + + @Test + public void testFooterColSpans() { + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + assertEquals("5", grid.getFooterCell(1, 1).getAttribute("colspan")); + assertEquals("2", grid.getFooterCell(0, 1).getAttribute("colspan")); + assertEquals("3", grid.getFooterCell(0, 3).getAttribute("colspan")); + } + + @Test + public void testActiveHeaderColumnsWithNavigation() throws IOException { + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + grid.getCell(0, 1).click(); + + compareScreen("beforeNavigation"); + + for (int i = 1; i <= 6; ++i) { + assertEquals(true, grid.getFooterCell(1, 1).isActiveHeader()); + assertEquals(i < 3, grid.getFooterCell(0, 1).isActiveHeader()); + assertEquals(i >= 3, grid.getFooterCell(0, 3).isActiveHeader()); + assertEquals(true, grid.getHeaderCell(0, 1).isActiveHeader()); + assertEquals(i < 3, grid.getHeaderCell(1, 1).isActiveHeader()); + assertEquals(i >= 3, grid.getHeaderCell(1, 3).isActiveHeader()); + new Actions(getDriver()).sendKeys(Keys.ARROW_RIGHT).perform(); + } + + compareScreen("afterNavigation"); + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridElement.java b/uitest/src/com/vaadin/tests/components/grid/GridElement.java index 5027c603d9..3b28c4eaa2 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridElement.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridElement.java @@ -38,10 +38,15 @@ public class GridElement extends AbstractComponentElement { public static class GridCellElement extends AbstractElement { private String ACTIVE_CLASS_NAME = "-cell-active"; + private String ACTIVE_HEADER_CLASS_NAME = "-header-active"; public boolean isActive() { return getAttribute("class").contains(ACTIVE_CLASS_NAME); } + + public boolean isActiveHeader() { + return getAttribute("class").contains(ACTIVE_HEADER_CLASS_NAME); + } } public static class GridRowElement extends AbstractElement { -- cgit v1.2.3 From a9fc5d5be75c8ca33231909d1a44f471b5dcaaf9 Mon Sep 17 00:00:00 2001 From: John Ahlroos Date: Thu, 7 Aug 2014 11:11:03 +0300 Subject: Fixes removing all rows causing javascript error #13334 Change-Id: If3850d2248c6731bf3ee55d73c4cba2999ff9882 --- shared/src/com/vaadin/shared/ui/grid/Range.java | 6 ++++++ .../tests/components/grid/basicfeatures/GridBasicFeatures.java | 9 +++++++++ .../tests/components/grid/basicfeatures/GridStructureTest.java | 10 ++++++++++ 3 files changed, 25 insertions(+) (limited to 'shared/src/com') diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java index a1d4d86103..38b2ff2a60 100644 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -369,6 +369,12 @@ public final class Range implements Serializable { * if the two ranges aren't connected */ public Range combineWith(Range other) throws IllegalArgumentException { + if (other.isEmpty()) { + return this; + } else if (isEmpty()) { + return other; + } + if (getStart() > other.getEnd() || other.getStart() > getEnd()) { throw new IllegalArgumentException("There is a gap between " + this + " and " + other); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 9ab2c98bdb..8b3391253b 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -521,6 +521,15 @@ public class GridBasicFeatures extends AbstractComponentTest { } } }); + + createClickAction("Remove all rows", "Body rows", + new Command() { + @SuppressWarnings("unchecked") + @Override + public void execute(Grid c, String value, Object data) { + ds.removeAllItems(); + } + }, null); } @SuppressWarnings("boxing") diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStructureTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStructureTest.java index ced6963c32..d52f512b4f 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStructureTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStructureTest.java @@ -197,6 +197,16 @@ public class GridStructureTest extends GridBasicFeaturesTest { "modified: Column 0", getBodyCellByRowAndColumn(0, 0).getText()); } + @Test + public void testRemovingAllItems() throws Exception { + openTestURL(); + + selectMenuPath("Component", "Body rows", "Remove all rows"); + + assertEquals(0, getGridElement().findElement(By.tagName("tbody")) + .findElements(By.tagName("tr")).size()); + } + private void assertPrimaryStylename(String stylename) { assertTrue(getGridElement().getAttribute("class").contains(stylename)); -- cgit v1.2.3 From c8e0335ccd4e2af54efe752fc98b20c52fafabcf Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Fri, 1 Aug 2014 15:07:30 +0300 Subject: Add server side support for Components in Headers and Footers (#13334) Change-Id: Ic5a6b4c68bc2d09840cbd7faffebae8991a5fff4 --- client/src/com/vaadin/client/ui/grid/Grid.java | 5 +- .../com/vaadin/client/ui/grid/GridConnector.java | 42 +++++++++- .../vaadin/client/ui/grid/GridStaticSection.java | 21 +++-- server/src/com/vaadin/ui/components/grid/Grid.java | 38 ++++++++- .../com/vaadin/ui/components/grid/GridFooter.java | 2 +- .../com/vaadin/ui/components/grid/GridHeader.java | 2 +- .../ui/components/grid/GridStaticSection.java | 93 +++++++++++++++++----- .../vaadin/shared/ui/grid/GridStaticCellType.java | 39 +++++++++ .../shared/ui/grid/GridStaticSectionState.java | 8 ++ .../grid/basicfeatures/GridBasicFeatures.java | 88 ++++++++++++++++++++ .../GridStaticSectionComponentTest.java | 56 +++++++++++++ 11 files changed, 354 insertions(+), 40 deletions(-) create mode 100644 shared/src/com/vaadin/shared/ui/grid/GridStaticCellType.java create mode 100644 uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStaticSectionComponentTest.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index cf0606a38c..d1858a8f3f 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -62,6 +62,7 @@ import com.vaadin.client.ui.grid.sort.SortEvent; import com.vaadin.client.ui.grid.sort.SortEventHandler; import com.vaadin.client.ui.grid.sort.SortOrder; import com.vaadin.shared.ui.grid.GridConstants; +import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.ui.grid.ScrollDestination; @@ -1101,7 +1102,7 @@ public class Grid extends Composite implements * If the cell contains widgets that are not currently attach * then attach them now. */ - if (StaticCell.Type.WIDGET.equals(metadata.getType())) { + if (GridStaticCellType.WIDGET.equals(metadata.getType())) { final Widget widget = metadata.getWidget(); final Element cellElement = cell.getElement(); @@ -1129,7 +1130,7 @@ public class Grid extends Composite implements int index = columnIndices.get(cell.getColumn()); StaticCell metadata = gridRow.getCell(index); - if (StaticCell.Type.WIDGET.equals(metadata.getType()) + if (GridStaticCellType.WIDGET.equals(metadata.getType()) && metadata.getWidget().isAttached()) { Widget widget = metadata.getWidget(); diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 56affc75d1..12952c7db8 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -30,11 +30,13 @@ import java.util.logging.Logger; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONValue; +import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.annotations.OnStateChange; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.data.DataSource.RowHandle; import com.vaadin.client.data.RpcDataSourceConnector.RpcDataSource; -import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.AbstractHasComponentsConnector; import com.vaadin.client.ui.grid.GridHeader.HeaderRow; import com.vaadin.client.ui.grid.GridStaticSection.StaticCell; import com.vaadin.client.ui.grid.GridStaticSection.StaticRow; @@ -73,7 +75,7 @@ import com.vaadin.shared.ui.grid.SortDirection; * @author Vaadin Ltd */ @Connect(com.vaadin.ui.components.grid.Grid.class) -public class GridConnector extends AbstractComponentConnector { +public class GridConnector extends AbstractHasComponentsConnector { /** * Custom implementation of the custom grid column using a JSONObject to @@ -297,8 +299,22 @@ public class GridConnector extends AbstractComponentConnector { int i = 0; for (CellState cellState : rowState.cells) { - StaticCell cell = row.getCell(diff + (i++)); - cell.setText(cellState.text); + StaticCell cell = row.getCell(i++); + switch (cellState.type) { + case TEXT: + cell.setText(cellState.text); + break; + case HTML: + cell.setHtml(cellState.text); + break; + case WIDGET: + ComponentConnector connector = (ComponentConnector) cellState.connector; + cell.setWidget(connector.getWidget()); + break; + default: + throw new IllegalStateException("unexpected cell type: " + + cellState.type); + } } for (List group : rowState.cellGroups) { @@ -570,4 +586,22 @@ public class GridConnector extends AbstractComponentConnector { + key.getClass().getSimpleName() + " (" + key + ")"; return (String) key; } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin.client + * .ComponentConnector) + */ + @Override + public void updateCaption(ComponentConnector connector) { + // TODO Auto-generated method stub + + } + + @Override + public void onConnectorHierarchyChange( + ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { + } } diff --git a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java index 14e4d6de9c..1be0a92b8f 100644 --- a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java +++ b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.List; import com.google.gwt.user.client.ui.Widget; +import com.vaadin.shared.ui.grid.GridStaticCellType; /** * Abstract base class for Grid header and footer sections. @@ -39,17 +40,13 @@ abstract class GridStaticSection> */ static class StaticCell { - public enum Type { - TEXT, HTML, WIDGET; - } - private Object content = null; private int colspan = 1; private GridStaticSection section; - private Type type = Type.TEXT; + private GridStaticCellType type = GridStaticCellType.TEXT; /** * Sets the text displayed in this cell. @@ -59,7 +56,7 @@ abstract class GridStaticSection> */ public void setText(String text) { this.content = text; - this.type = Type.TEXT; + this.type = GridStaticCellType.TEXT; section.requestSectionRefresh(); } @@ -69,7 +66,7 @@ abstract class GridStaticSection> * @return the plain text caption */ public String getText() { - if (type != Type.TEXT) { + if (type != GridStaticCellType.TEXT) { throw new IllegalStateException( "Cannot fetch Text from a cell with type " + type); } @@ -120,7 +117,7 @@ abstract class GridStaticSection> * @return the html content of the cell. */ public String getHtml() { - if (type != Type.HTML) { + if (type != GridStaticCellType.HTML) { throw new IllegalStateException( "Cannot fetch HTML from a cell with type " + type); } @@ -136,7 +133,7 @@ abstract class GridStaticSection> */ public void setHtml(String html) { this.content = html; - this.type = Type.HTML; + this.type = GridStaticCellType.HTML; section.requestSectionRefresh(); } @@ -149,7 +146,7 @@ abstract class GridStaticSection> * @return the widget in the cell */ public Widget getWidget() { - if (type != Type.WIDGET) { + if (type != GridStaticCellType.WIDGET) { throw new IllegalStateException( "Cannot fetch Widget from a cell with type " + type); } @@ -166,7 +163,7 @@ abstract class GridStaticSection> */ public void setWidget(Widget widget) { this.content = widget; - this.type = Type.WIDGET; + this.type = GridStaticCellType.WIDGET; section.requestSectionRefresh(); } @@ -175,7 +172,7 @@ abstract class GridStaticSection> * * @return the type of content the cell contains. */ - public Type getType() { + public GridStaticCellType getType() { return type; } } diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index 2e0ac6bb31..d365d3e0cc 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -45,10 +45,16 @@ import com.vaadin.shared.ui.grid.GridColumnState; import com.vaadin.shared.ui.grid.GridServerRpc; import com.vaadin.shared.ui.grid.GridState; import com.vaadin.shared.ui.grid.GridState.SharedSelectionMode; +import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.ui.grid.SortDirection; import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Component; +import com.vaadin.ui.HasComponents; +import com.vaadin.ui.components.grid.GridFooter.FooterCell; +import com.vaadin.ui.components.grid.GridFooter.FooterRow; +import com.vaadin.ui.components.grid.GridHeader.HeaderCell; import com.vaadin.ui.components.grid.GridHeader.HeaderRow; import com.vaadin.ui.components.grid.selection.MultiSelectionModel; import com.vaadin.ui.components.grid.selection.NoSelectionModel; @@ -123,7 +129,8 @@ import com.vaadin.util.ReflectTools; * @since * @author Vaadin Ltd */ -public class Grid extends AbstractComponent implements SelectionChangeNotifier { +public class Grid extends AbstractComponent implements SelectionChangeNotifier, + HasComponents { /** * Selection modes representing built-in {@link SelectionModel @@ -1259,4 +1266,33 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier { public GridFooter getFooter() { return footer; } + + @Override + public Iterator iterator() { + List componentList = new ArrayList(); + + GridHeader header = getHeader(); + for (int i = 0; i < header.getRowCount(); ++i) { + HeaderRow row = header.getRow(i); + for (Object propId : datasource.getContainerPropertyIds()) { + HeaderCell cell = row.getCell(propId); + if (cell.getCellState().type == GridStaticCellType.WIDGET) { + componentList.add(cell.getComponent()); + } + } + } + + GridFooter footer = getFooter(); + for (int i = 0; i < footer.getRowCount(); ++i) { + FooterRow row = footer.getRow(i); + for (Object propId : datasource.getContainerPropertyIds()) { + FooterCell cell = row.getCell(propId); + if (cell.getCellState().type == GridStaticCellType.WIDGET) { + componentList.add(cell.getComponent()); + } + } + } + + return componentList.iterator(); + } } diff --git a/server/src/com/vaadin/ui/components/grid/GridFooter.java b/server/src/com/vaadin/ui/components/grid/GridFooter.java index 84b2b70090..0a28a481cf 100644 --- a/server/src/com/vaadin/ui/components/grid/GridFooter.java +++ b/server/src/com/vaadin/ui/components/grid/GridFooter.java @@ -38,7 +38,7 @@ public class GridFooter extends GridStaticSection { } - public class FooterCell extends GridStaticSection.StaticCell { + public class FooterCell extends GridStaticSection.StaticCell { protected FooterCell(FooterRow row) { super(row); diff --git a/server/src/com/vaadin/ui/components/grid/GridHeader.java b/server/src/com/vaadin/ui/components/grid/GridHeader.java index 67f7bfdf69..9d7ec24a97 100644 --- a/server/src/com/vaadin/ui/components/grid/GridHeader.java +++ b/server/src/com/vaadin/ui/components/grid/GridHeader.java @@ -41,7 +41,7 @@ public class GridHeader extends GridStaticSection { } } - public class HeaderCell extends GridStaticSection.StaticCell { + public class HeaderCell extends GridStaticSection.StaticCell { protected HeaderCell(HeaderRow row) { super(row); diff --git a/server/src/com/vaadin/ui/components/grid/GridStaticSection.java b/server/src/com/vaadin/ui/components/grid/GridStaticSection.java index 8c983052ea..eb098d0d4e 100644 --- a/server/src/com/vaadin/ui/components/grid/GridStaticSection.java +++ b/server/src/com/vaadin/ui/components/grid/GridStaticSection.java @@ -26,9 +26,11 @@ import java.util.List; import java.util.Map; import com.vaadin.data.Container.Indexed; +import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.GridStaticSectionState; import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState; import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; +import com.vaadin.ui.Component; /** * Abstract base class for Grid header and footer sections. @@ -47,7 +49,7 @@ abstract class GridStaticSection> * @param * the type of the cells in the row */ - abstract static class StaticRow> implements + abstract static class StaticRow implements Serializable { private RowState rowState = new RowState(); @@ -184,17 +186,13 @@ abstract class GridStaticSection> /** * A header or footer cell. Has a simple textual caption. - * - * @param - * the type of row this cells is in */ - abstract static class StaticCell> implements - Serializable { + abstract static class StaticCell implements Serializable { private CellState cellState = new CellState(); - private ROWTYPE row; + private StaticRow row; - protected StaticCell(ROWTYPE row) { + protected StaticCell(StaticRow row) { this.row = row; } @@ -203,7 +201,7 @@ abstract class GridStaticSection> * * @return row for this cell */ - public ROWTYPE getRow() { + public StaticRow getRow() { return row; } @@ -212,26 +210,83 @@ abstract class GridStaticSection> } /** - * Gets the current text content of this cell. Text is null if HTML or - * Component content is used. + * Sets the text displayed in this cell. * - * @return text content or null + * @param text + * a plain text caption + */ + public void setText(String text) { + cellState.text = text; + cellState.type = GridStaticCellType.TEXT; + row.section.markAsDirty(); + } + + /** + * Returns the text displayed in this cell. + * + * @return the plain text caption */ public String getText() { + if (cellState.type != GridStaticCellType.TEXT) { + throw new IllegalStateException( + "Cannot fetch Text from a cell with type " + + cellState.type); + } return cellState.text; } /** - * Sets the current text content of this cell. + * Returns the HTML content displayed in this cell. + * + * @return the html * - * @param text - * new text content */ - public void setText(String text) { - if (text != null && !text.equals(getCellState().text)) { - getCellState().text = text; - row.section.markAsDirty(); + public String getHtml() { + if (cellState.type != GridStaticCellType.HTML) { + throw new IllegalStateException( + "Cannot fetch HTML from a cell with type " + + cellState.type); } + return cellState.html; + } + + /** + * Sets the HTML content displayed in this cell. + * + * @param html + * the html to set + */ + public void setHtml(String html) { + cellState.html = html; + cellState.type = GridStaticCellType.HTML; + row.section.markAsDirty(); + } + + /** + * Returns the component displayed in this cell. + * + * @return the component + */ + public Component getComponent() { + if (cellState.type != GridStaticCellType.WIDGET) { + throw new IllegalStateException( + "Cannot fetch Component from a cell with type " + + cellState.type); + } + return (Component) cellState.connector; + } + + /** + * Sets the component displayed in this cell. + * + * @param component + * the component to set + */ + public void setComponent(Component component) { + component.setParent(row.section.grid); + cellState.connector = component; + cellState.type = GridStaticCellType.WIDGET; + row.section.markAsDirty(); } } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridStaticCellType.java b/shared/src/com/vaadin/shared/ui/grid/GridStaticCellType.java new file mode 100644 index 0000000000..eae4bc8da4 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/GridStaticCellType.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.shared.ui.grid; + +/** + * Enumeration, specifying the content type of a Cell in a GridStaticSection. + * + * @since + * @author Vaadin Ltd + */ +public enum GridStaticCellType { + /** + * Text content + */ + TEXT, + + /** + * HTML content + */ + HTML, + + /** + * Widget content + */ + WIDGET; +} \ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java index 41f56199da..c3c373b5af 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridStaticSectionState.java @@ -19,6 +19,8 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import com.vaadin.shared.Connector; + /** * Shared state for Grid headers and footers. * @@ -29,6 +31,12 @@ public class GridStaticSectionState implements Serializable { public static class CellState implements Serializable { public String text = ""; + + public String html = ""; + + public Connector connector = null; + + public GridStaticCellType type = GridStaticCellType.TEXT; } public static class RowState implements Serializable { diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 8b3391253b..031ebf7fa5 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -28,14 +28,20 @@ import java.util.Random; import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.util.IndexedContainer; +import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.SortDirection; import com.vaadin.tests.components.AbstractComponentTest; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.components.grid.Grid; import com.vaadin.ui.components.grid.Grid.SelectionMode; import com.vaadin.ui.components.grid.GridColumn; import com.vaadin.ui.components.grid.GridFooter; +import com.vaadin.ui.components.grid.GridFooter.FooterCell; import com.vaadin.ui.components.grid.GridHeader; +import com.vaadin.ui.components.grid.GridHeader.HeaderCell; import com.vaadin.ui.components.grid.GridHeader.HeaderRow; import com.vaadin.ui.components.grid.SortOrderChangeEvent; import com.vaadin.ui.components.grid.SortOrderChangeListener; @@ -440,6 +446,88 @@ public class GridBasicFeatures extends AbstractComponentTest { } }, w, c); } + + LinkedHashMap defaultRows = new LinkedHashMap(); + defaultRows.put("Text Header", GridStaticCellType.TEXT); + defaultRows.put("Html Header ", GridStaticCellType.HTML); + defaultRows.put("Widget Header", GridStaticCellType.WIDGET); + + createMultiClickAction("Header Type", getColumnProperty(c), + defaultRows, new Command() { + + @Override + public void execute(Grid grid, + GridStaticCellType value, Object columnIndex) { + final Object propertyId = (new ArrayList(grid + .getContainerDatasource() + .getContainerPropertyIds()) + .get((Integer) columnIndex)); + final HeaderCell cell = grid.getHeader() + .getDefaultRow().getCell(propertyId); + switch (value) { + case TEXT: + cell.setText("Text Header"); + break; + case HTML: + cell.setHtml("HTML Header"); + break; + case WIDGET: + cell.setComponent(new Button("Button Header", + new ClickListener() { + + @Override + public void buttonClick( + ClickEvent event) { + log("Button clicked!"); + } + })); + default: + break; + } + } + + }, c); + + defaultRows = new LinkedHashMap(); + defaultRows.put("Text Footer", GridStaticCellType.TEXT); + defaultRows.put("Html Footer", GridStaticCellType.HTML); + defaultRows.put("Widget Footer", GridStaticCellType.WIDGET); + + createMultiClickAction("Footer Type", getColumnProperty(c), + defaultRows, new Command() { + + @Override + public void execute(Grid grid, + GridStaticCellType value, Object columnIndex) { + final Object propertyId = (new ArrayList(grid + .getContainerDatasource() + .getContainerPropertyIds()) + .get((Integer) columnIndex)); + final FooterCell cell = grid.getFooter().getRow(0) + .getCell(propertyId); + switch (value) { + case TEXT: + cell.setText("Text Footer"); + break; + case HTML: + cell.setHtml("HTML Footer"); + break; + case WIDGET: + cell.setComponent(new Button("Button Footer", + new ClickListener() { + + @Override + public void buttonClick( + ClickEvent event) { + log("Button clicked!"); + } + })); + default: + break; + } + } + + }, c); } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStaticSectionComponentTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStaticSectionComponentTest.java new file mode 100644 index 0000000000..fe32825d75 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridStaticSectionComponentTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.tests.components.grid.basicfeatures; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; + +public class GridStaticSectionComponentTest extends GridBasicFeaturesTest { + + @Test + public void testNativeButtonInHeader() throws IOException { + openTestURL(); + + selectMenuPath("Component", "Columns", "Column 1", "Header Type", + "Widget Header"); + + getGridElement().$(ButtonElement.class).first().click(); + + // Clicking also triggers sorting + assertEquals("2. Button clicked!", getLogRow(2)); + + compareScreen("button"); + } + + @Test + public void testNativeButtonInFooter() throws IOException { + openTestURL(); + + selectMenuPath("Component", "Footer", "Visible"); + selectMenuPath("Component", "Footer", "Append row"); + selectMenuPath("Component", "Columns", "Column 1", "Footer Type", + "Widget Footer"); + + getGridElement().$(ButtonElement.class).first().click(); + + assertEquals("4. Button clicked!", getLogRow(0)); + } +} -- cgit v1.2.3 From 22a57cd110380c988a6dd3d510f9773c19755aa0 Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Fri, 8 Aug 2014 10:38:27 +0300 Subject: Fixes copyright headers in shared package (#13334) Change-Id: I53be5f824acb552033107174cc5b8c1072675446 --- shared/src/com/vaadin/shared/data/DataProviderRpc.java | 2 +- shared/src/com/vaadin/shared/data/DataProviderState.java | 2 +- shared/src/com/vaadin/shared/data/DataRequestRpc.java | 2 +- shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridColumnState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridConstants.java | 2 +- shared/src/com/vaadin/shared/ui/grid/GridState.java | 2 +- shared/src/com/vaadin/shared/ui/grid/HeightMode.java | 2 +- shared/src/com/vaadin/shared/ui/grid/Range.java | 2 +- shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) (limited to 'shared/src/com') diff --git a/shared/src/com/vaadin/shared/data/DataProviderRpc.java b/shared/src/com/vaadin/shared/data/DataProviderRpc.java index bfd9505c04..21e299e68b 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderRpc.java +++ b/shared/src/com/vaadin/shared/data/DataProviderRpc.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/data/DataProviderState.java b/shared/src/com/vaadin/shared/data/DataProviderState.java index 13331c2a64..76d68e8352 100644 --- a/shared/src/com/vaadin/shared/data/DataProviderState.java +++ b/shared/src/com/vaadin/shared/data/DataProviderState.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java index 80f320e356..8b0bd4adcb 100644 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java index 8c2bde851b..2ef0dfc3f8 100644 --- a/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java +++ b/shared/src/com/vaadin/shared/ui/grid/ColumnGroupState.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java index 24a9996d40..ade9e87f36 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridClientRpc.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java index 5c0d04d968..b73e7cffd5 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridColumnState.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java index b098946346..346e85b994 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/GridState.java b/shared/src/com/vaadin/shared/ui/grid/GridState.java index 54acc80127..d687dd8e48 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridState.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/HeightMode.java b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java index 09c46b039e..228fcbf0f4 100644 --- a/shared/src/com/vaadin/shared/ui/grid/HeightMode.java +++ b/shared/src/com/vaadin/shared/ui/grid/HeightMode.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java index 38b2ff2a60..4f7e37b9ad 100644 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 diff --git a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java index 5fd69de612..43d5fcc21b 100644 --- a/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java +++ b/shared/src/com/vaadin/shared/ui/grid/ScrollDestination.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 Vaadin Ltd. + * Copyright 2000-2014 Vaadin Ltd. * * 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 -- cgit v1.2.3 From 1102d5fb4eec9a712cd8f770dfa023e842abd830 Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Fri, 8 Aug 2014 09:55:22 +0300 Subject: Re-fixes "remove all rows" bug without breaking unit tests (#13334) This partly reverts a9fc5d5be Change-Id: I1d2f81e1d88c0057e6ba869358845ce4e9466a28 --- client/src/com/vaadin/client/data/AbstractRemoteDataSource.java | 4 +++- shared/src/com/vaadin/shared/ui/grid/Range.java | 6 ------ 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index bec6e330bc..1ce68ced17 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -381,7 +381,9 @@ public abstract class AbstractRemoteDataSource implements DataSource { } Range removedRange = Range.withLength(firstRowIndex, count); - if (removedRange.intersects(cached)) { + if (cached.isSubsetOf(removedRange)) { + cached = Range.withLength(0, 0); + } else if (removedRange.intersects(cached)) { Range[] partitions = cached.partitionWith(removedRange); Range remainsBefore = partitions[0]; Range transposedRemainsAfter = partitions[2].offsetBy(-removedRange diff --git a/shared/src/com/vaadin/shared/ui/grid/Range.java b/shared/src/com/vaadin/shared/ui/grid/Range.java index 4f7e37b9ad..2054845320 100644 --- a/shared/src/com/vaadin/shared/ui/grid/Range.java +++ b/shared/src/com/vaadin/shared/ui/grid/Range.java @@ -369,12 +369,6 @@ public final class Range implements Serializable { * if the two ranges aren't connected */ public Range combineWith(Range other) throws IllegalArgumentException { - if (other.isEmpty()) { - return this; - } else if (isEmpty()) { - return other; - } - if (getStart() > other.getEnd() || other.getStart() > getEnd()) { throw new IllegalArgumentException("There is a gap between " + this + " and " + other); -- cgit v1.2.3 From 5dfdd40d00dc0f7a2eea19f8c77485f1afcf77ad Mon Sep 17 00:00:00 2001 From: John Ahlroos Date: Thu, 7 Aug 2014 16:04:17 +0300 Subject: Removed SortableColumnHeaderRenderer #13334 Change-Id: I3f15765228428049febfe3a8d5966c3631d010a9 --- client/src/com/vaadin/client/ui/grid/Grid.java | 524 +++++++++------------ .../src/com/vaadin/client/ui/grid/GridHeader.java | 28 +- .../vaadin/client/ui/grid/GridStaticSection.java | 19 - .../com/vaadin/shared/ui/grid/GridConstants.java | 10 + 4 files changed, 260 insertions(+), 321 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index aae7f046b6..a407038917 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -30,6 +30,7 @@ import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.TableCellElement; import com.google.gwt.dom.client.Touch; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.shared.HandlerRegistration; @@ -463,6 +464,83 @@ public class Grid extends Composite implements } } + /** + * Class for sorting at a later time + */ + private class LazySorter extends Timer { + + private Cell cell; + + private boolean multisort; + + @Override + public void run() { + SortOrder sortingOrder = getSortOrder(getColumnFromVisibleIndex(cell + .getColumn())); + if (sortingOrder == null) { + /* + * No previous sorting, sort Ascending + */ + sort(cell, SortDirection.ASCENDING, multisort); + + } else { + // Toggle sorting + SortDirection direction = sortingOrder.getDirection(); + if (direction == SortDirection.ASCENDING) { + sort(cell, SortDirection.DESCENDING, multisort); + } else { + sort(cell, SortDirection.ASCENDING, multisort); + } + } + } + + /** + * Set the cell reference to the primary cell that sorting should be + * done for. + * + * @param cell + * + */ + public void setCellReference(Cell cell) { + this.cell = cell; + } + + /** + * Is multiple column sorting is enabled/disabled + * + * @param multisort + * true if multiple column sorting is enabled + */ + public void setMultisort(boolean multisort) { + this.multisort = multisort; + } + + /** + * Sorts the column in a direction + */ + private void sort(Cell cell, SortDirection direction, boolean multisort) { + TableCellElement th = TableCellElement.as(cell.getElement()); + + // Apply primary sorting on clicked column + GridColumn columnInstance = getColumnFromVisibleIndex(cell + .getColumn()); + Sort sorting = Sort.by(columnInstance, direction); + + // Re-apply old sorting to the sort order + if (multisort) { + for (SortOrder order : getSortOrder()) { + if (order.getColumn() != columnInstance) { + sorting = sorting.then(order.getColumn(), + order.getDirection()); + } + } + } + + // Perform sorting + Grid.this.sort(sorting); + } + } + /** * Escalator used internally by grid to render the rows */ @@ -511,6 +589,8 @@ public class Grid extends Composite implements private final ActiveCellHandler activeCellHandler; + private final LazySorter lazySorter = new LazySorter(); + /** * Enumeration for easy setting of selection mode. */ @@ -552,13 +632,6 @@ public class Grid extends Composite implements protected abstract SelectionModel createModel(); } - class SortableColumnHeaderRenderer extends - AbstractGridColumn.SortableColumnHeaderRenderer { - SortableColumnHeaderRenderer(Renderer cellRenderer) { - super(Grid.this, cellRenderer); - } - } - /** * Base class for grid columns internally used by the Grid. The user should * use {@link GridColumn} when creating new columns. @@ -572,274 +645,7 @@ public class Grid extends Composite implements static abstract class AbstractGridColumn implements HasVisibility { /** - * Renderer for columns which are sortable - * - * FIXME Currently assumes multisorting - */ - static class SortableColumnHeaderRenderer extends - ComplexRenderer { - - private Grid grid; - - /** - * Delay before a long tap action is triggered. Number in - * milliseconds. - */ - private static final int LONG_TAP_DELAY = 500; - - /** - * The threshold in pixels a finger can move while long tapping. - */ - private static final int LONG_TAP_THRESHOLD = 3; - - /** - * Class for sorting at a later time - */ - private class LazySorter extends Timer { - - private Cell cell; - - private boolean multisort; - - @Override - public void run() { - SortOrder sortingOrder = getSortingOrder(grid - .getColumnFromVisibleIndex(cell.getColumn())); - if (sortingOrder == null) { - /* - * No previous sorting, sort Ascending - */ - sort(cell, SortDirection.ASCENDING, multisort); - - } else { - // Toggle sorting - SortDirection direction = sortingOrder.getDirection(); - if (direction == SortDirection.ASCENDING) { - sort(cell, SortDirection.DESCENDING, multisort); - } else { - sort(cell, SortDirection.ASCENDING, multisort); - } - } - } - - public void setCurrentCell(Cell cell) { - this.cell = cell; - } - - public void setMultisort(boolean multisort) { - this.multisort = multisort; - } - } - - private final LazySorter lazySorter = new LazySorter(); - - private Renderer cellRenderer; - - private Point touchStartPoint; - - /** - * Creates column renderer with sort indicators - * - * @param cellRenderer - * The actual cell renderer - */ - public SortableColumnHeaderRenderer(Grid grid, - Renderer cellRenderer) { - this.grid = grid; - this.cellRenderer = cellRenderer; - } - - @Override - public void render(FlyweightCell cell, String data) { - - // Render cell - this.cellRenderer.render(cell, data); - - /* - * FIXME This grid null check is needed since Grid.addColumns() - * is invoking Escalator.insertColumn() before the grid instance - * for the column is set resulting in the first render() being - * done without a grid instance. Remove the if statement when - * this is fixed. - */ - if (grid != null) { - GridColumn column = grid - .getColumnFromVisibleIndex(cell.getColumn()); - SortOrder sortingOrder = getSortingOrder(column); - Element cellElement = cell.getElement(); - if (column.isSortable()) { - if (sortingOrder != null) { - if (SortDirection.ASCENDING == sortingOrder - .getDirection()) { - cellElement.replaceClassName("sort-desc", - "sort-asc"); - } else { - cellElement.replaceClassName("sort-asc", - "sort-desc"); - } - - int sortIndex = grid.getSortOrder().indexOf( - sortingOrder); - if (sortIndex > -1 - && grid.getSortOrder().size() > 1) { - // Show sort order indicator if column is sorted - // and other sorted columns also exists. - cellElement.setAttribute("sort-order", - String.valueOf(sortIndex + 1)); - - } else { - cellElement.removeAttribute("sort-order"); - } - } else { - cleanup(cell); - } - } else { - cleanup(cell); - } - } - } - - private void cleanup(FlyweightCell cell) { - Element cellElement = cell.getElement(); - cellElement.removeAttribute("sort-order"); - cellElement.removeClassName("sort-desc"); - cellElement.removeClassName("sort-asc"); - } - - @Override - public Collection getConsumedEvents() { - return Arrays.asList(BrowserEvents.TOUCHSTART, - BrowserEvents.TOUCHMOVE, BrowserEvents.TOUCHEND, - BrowserEvents.TOUCHCANCEL, BrowserEvents.CLICK); - } - - @Override - public boolean onBrowserEvent(final Cell cell, NativeEvent event) { - - // Handle sorting events if column is sortable - if (grid.getColumn(cell.getColumn()).isSortable()) { - - if (BrowserEvents.TOUCHSTART.equals(event.getType())) { - if (event.getTouches().length() > 1) { - return false; - } - - event.preventDefault(); - - Touch touch = event.getChangedTouches().get(0); - touchStartPoint = new Point(touch.getClientX(), - touch.getClientY()); - - lazySorter.setCurrentCell(cell); - lazySorter.setMultisort(true); - lazySorter.schedule(LONG_TAP_DELAY); - - } else if (BrowserEvents.TOUCHMOVE.equals(event.getType())) { - if (event.getTouches().length() > 1) { - return false; - } - - event.preventDefault(); - - Touch touch = event.getChangedTouches().get(0); - double diffX = Math.abs(touch.getClientX() - - touchStartPoint.getX()); - double diffY = Math.abs(touch.getClientY() - - touchStartPoint.getY()); - - // Cancel long tap if finger strays too far from - // starting point - if (diffX > LONG_TAP_THRESHOLD - || diffY > LONG_TAP_THRESHOLD) { - lazySorter.cancel(); - } - - } else if (BrowserEvents.TOUCHEND.equals(event.getType())) { - if (event.getTouches().length() > 0) { - return false; - } - - if (lazySorter.isRunning()) { - // Not a long tap yet, perform single sort - lazySorter.cancel(); - lazySorter.setMultisort(false); - lazySorter.run(); - } - - } else if (BrowserEvents.TOUCHCANCEL - .equals(event.getType())) { - if (event.getChangedTouches().length() > 1) { - return false; - } - - lazySorter.cancel(); - - } else if (BrowserEvents.CLICK.equals(event.getType())) { - lazySorter.setCurrentCell(cell); - lazySorter.setMultisort(event.getShiftKey()); - lazySorter.run(); - - // Active cell handling is also monitoring the click - // event so we allow event to propagate for it - return false; - } - return true; - } - return false; - - } - - protected void removeFromRow(HeaderRow row) { - row.setRenderer(new Renderer() { - @Override - public void render(FlyweightCell cell, String data) { - cleanup(cell); - } - }); - grid.refreshHeader(); - row.setRenderer(cellRenderer); - grid.refreshHeader(); - } - - /** - * Sorts the column in a direction - */ - private void sort(Cell cell, SortDirection direction, - boolean multisort) { - // Apply primary sorting on clicked column - GridColumn columnInstance = grid - .getColumnFromVisibleIndex(cell.getColumn()); - Sort sorting = Sort.by(columnInstance, direction); - - // Re-apply old sorting to the sort order - if (multisort) { - for (SortOrder order : grid.getSortOrder()) { - if (order.getColumn() != columnInstance) { - sorting = sorting.then(order.getColumn(), - order.getDirection()); - } - } - } - - // Perform sorting - grid.sort(sorting); - } - - /** - * Finds the sorting order for this column - */ - private SortOrder getSortingOrder(GridColumn column) { - for (SortOrder order : grid.getSortOrder()) { - if (order.getColumn() == column) { - return order; - } - } - return null; - } - } - - /** - * The grid the column is associated with + * the column is associated with */ private Grid grid; @@ -1187,13 +993,14 @@ public class Grid extends Composite implements int index = columnIndices.get(cell.getColumn()); final StaticCell metadata = staticRow.getCell(index); + // Decorate default row with sorting indicators + if (staticRow instanceof HeaderRow) { + addSortingIndicatorsToHeaderRow((HeaderRow) staticRow, cell); + } + // Assign colspan to cell before rendering cell.setColSpan(metadata.getColspan()); - // Decorates cell with possible indicators onto the cell. - // Actual content is rendered below. - staticRow.getRenderer().render(cell, null); - switch (metadata.getType()) { case TEXT: cell.getElement().setInnerText(metadata.getText()); @@ -1212,6 +1019,57 @@ public class Grid extends Composite implements } } + private void addSortingIndicatorsToHeaderRow(HeaderRow headerRow, + FlyweightCell cell) { + + cleanup(cell); + + GridColumn column = getColumnFromVisibleIndex(cell + .getColumn()); + SortOrder sortingOrder = getSortOrder(column); + if (!headerRow.isDefault() || !column.isSortable() + || sortingOrder == null) { + // Only apply sorting indicators to sortable header columns in + // the default header row + return; + } + + Element cellElement = cell.getElement(); + + if (SortDirection.ASCENDING == sortingOrder.getDirection()) { + cellElement.addClassName("sort-asc"); + } else { + cellElement.addClassName("sort-desc"); + } + + int sortIndex = Grid.this.getSortOrder().indexOf(sortingOrder); + if (sortIndex > -1 && Grid.this.getSortOrder().size() > 1) { + // Show sort order indicator if column is + // sorted and other sorted columns also exists. + cellElement.setAttribute("sort-order", + String.valueOf(sortIndex + 1)); + } + } + + /** + * Finds the sort order for this column + */ + private SortOrder getSortOrder(GridColumn column) { + for (SortOrder order : Grid.this.getSortOrder()) { + if (order.getColumn() == column) { + return order; + } + } + return null; + } + + private void cleanup(FlyweightCell cell) { + Element cellElement = cell.getElement(); + cellElement.removeAttribute("sort-order"); + cellElement.removeClassName("sort-desc"); + cellElement.removeClassName("sort-asc"); + } + @Override public void preAttach(Row row, Iterable cellsToAttach) { } @@ -1333,6 +1191,9 @@ public class Grid extends Composite implements refreshBody(); } }); + + // Sink header events + sinkEvents(getHeader().getConsumedEvents()); } @Override @@ -1371,7 +1232,7 @@ public class Grid extends Composite implements * The updater is invoked when body rows or columns are added or removed, * the content of body cells is changed, or the body is scrolled to expose * previously hidden content. - * + * * @return the new body updater instance */ protected EscalatorUpdater createBodyUpdater() { @@ -1997,17 +1858,15 @@ public class Grid extends Composite implements // FIXME getFromVisibleIndex??? GridColumn gridColumn = columns.get(cell.getColumn()); - Renderer renderer; if (container == escalator.getHeader()) { - renderer = header.getRow(cell.getRow()).getRenderer(); + if (getHeader().getRow(cell.getRow()).isDefault()) { + handleDefaultRowEvent(cell, event); + } } else if (container == escalator.getFooter()) { - renderer = footer.getRow(cell.getRow()).getRenderer(); - } else { - renderer = gridColumn.getRenderer(); - } - - if (renderer instanceof ComplexRenderer) { - ComplexRenderer cplxRenderer = (ComplexRenderer) renderer; + // NOP + } else if (gridColumn.getRenderer() instanceof ComplexRenderer) { + ComplexRenderer cplxRenderer = (ComplexRenderer) gridColumn + .getRenderer(); if (cplxRenderer.getConsumedEvents().contains( event.getType())) { if (cplxRenderer.onBrowserEvent(cell, event)) { @@ -2027,6 +1886,81 @@ public class Grid extends Composite implements } } + private Point rowEventTouchStartingPoint; + + private boolean handleDefaultRowEvent(final Cell cell, NativeEvent event) { + if (!getColumn(cell.getColumn()).isSortable()) { + // Only handle sorting events if the column is sortable + return false; + } + + if (BrowserEvents.TOUCHSTART.equals(event.getType())) { + if (event.getTouches().length() > 1) { + return false; + } + + event.preventDefault(); + + Touch touch = event.getChangedTouches().get(0); + rowEventTouchStartingPoint = new Point(touch.getClientX(), + touch.getClientY()); + + lazySorter.setCellReference(cell); + lazySorter.setMultisort(true); + lazySorter.schedule(GridConstants.LONG_TAP_DELAY); + + } else if (BrowserEvents.TOUCHMOVE.equals(event.getType())) { + if (event.getTouches().length() > 1) { + return false; + } + + event.preventDefault(); + + Touch touch = event.getChangedTouches().get(0); + double diffX = Math.abs(touch.getClientX() + - rowEventTouchStartingPoint.getX()); + double diffY = Math.abs(touch.getClientY() + - rowEventTouchStartingPoint.getY()); + + // Cancel long tap if finger strays too far from + // starting point + if (diffX > GridConstants.LONG_TAP_THRESHOLD + || diffY > GridConstants.LONG_TAP_THRESHOLD) { + lazySorter.cancel(); + } + + } else if (BrowserEvents.TOUCHEND.equals(event.getType())) { + if (event.getTouches().length() > 0) { + return false; + } + + if (lazySorter.isRunning()) { + // Not a long tap yet, perform single sort + lazySorter.cancel(); + lazySorter.setMultisort(false); + lazySorter.run(); + } + + } else if (BrowserEvents.TOUCHCANCEL.equals(event.getType())) { + if (event.getChangedTouches().length() > 1) { + return false; + } + + lazySorter.cancel(); + + } else if (BrowserEvents.CLICK.equals(event.getType())) { + lazySorter.setCellReference(cell); + lazySorter.setMultisort(event.getShiftKey()); + lazySorter.run(); + + // Active cell handling is also monitoring the click + // event so we allow event to propagate for it + return false; + } + + return true; + } + @Override public com.google.gwt.user.client.Element getSubPartElement(String subPart) { // Parse SubPart string to type and indices @@ -2358,6 +2292,18 @@ public class Grid extends Composite implements return Collections.unmodifiableList(sortOrder); } + /** + * Finds the sorting order for this column + */ + private SortOrder getSortOrder(GridColumn column) { + for (SortOrder order : getSortOrder()) { + if (order.getColumn() == column) { + return order; + } + } + return null; + } + /** * Register a GWT event handler for a sorting event. This handler gets * called whenever this Grid needs its data source to provide data sorted in diff --git a/client/src/com/vaadin/client/ui/grid/GridHeader.java b/client/src/com/vaadin/client/ui/grid/GridHeader.java index f714848618..2867ae4d1c 100644 --- a/client/src/com/vaadin/client/ui/grid/GridHeader.java +++ b/client/src/com/vaadin/client/ui/grid/GridHeader.java @@ -15,8 +15,11 @@ */ package com.vaadin.client.ui.grid; +import java.util.Arrays; +import java.util.Collection; + import com.google.gwt.core.client.Scheduler; -import com.vaadin.client.ui.grid.Grid.AbstractGridColumn.SortableColumnHeaderRenderer; +import com.google.gwt.dom.client.BrowserEvents; /** * Represents the header section of a Grid. A header consists of a single header @@ -89,21 +92,9 @@ public class GridHeader extends GridStaticSection { "Cannot set a default row that does not exist in the container"); } if (defaultRow != null) { - assert defaultRow.getRenderer() instanceof SortableColumnHeaderRenderer; - - // Eclipse is wrong about this warning - javac does not accept the - // parameterized version - ((Grid.SortableColumnHeaderRenderer) defaultRow.getRenderer()) - .removeFromRow(defaultRow); - defaultRow.setDefault(false); } if (row != null) { - assert !(row.getRenderer() instanceof SortableColumnHeaderRenderer); - - row.setRenderer(getGrid().new SortableColumnHeaderRenderer(row - .getRenderer())); - row.setDefault(true); } defaultRow = row; @@ -145,4 +136,15 @@ public class GridHeader extends GridStaticSection { } }); } + + /** + * Returns the events consumed by the header + * + * @return a collection of BrowserEvents + */ + public Collection getConsumedEvents() { + return Arrays.asList(BrowserEvents.TOUCHSTART, BrowserEvents.TOUCHMOVE, + BrowserEvents.TOUCHEND, BrowserEvents.TOUCHCANCEL, + BrowserEvents.CLICK); + } } diff --git a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java index 1be0a92b8f..8c9ada46d0 100644 --- a/client/src/com/vaadin/client/ui/grid/GridStaticSection.java +++ b/client/src/com/vaadin/client/ui/grid/GridStaticSection.java @@ -187,17 +187,6 @@ abstract class GridStaticSection> private List cells = new ArrayList(); - private Renderer renderer = new Renderer() { - - @Override - public void render(FlyweightCell cell, String data) { - /* - * The rendering into the cell is done directly from the updater - * since it needs to handle multiple types of data. - */ - } - }; - private GridStaticSection section; private Collection> cellGroups = new HashSet>(); @@ -359,14 +348,6 @@ abstract class GridStaticSection> cells.remove(index); } - protected void setRenderer(Renderer renderer) { - this.renderer = renderer; - } - - protected Renderer getRenderer() { - return renderer; - } - protected abstract CELLTYPE createCell(); protected GridStaticSection getSection() { diff --git a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java index 346e85b994..1ee79a5d37 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridConstants.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridConstants.java @@ -31,4 +31,14 @@ public final class GridConstants implements Serializable { * explicitly defined padding value. */ public static final int DEFAULT_PADDING = 0; + + /** + * Delay before a long tap action is triggered. Number in milliseconds. + */ + public static final int LONG_TAP_DELAY = 500; + + /** + * The threshold in pixels a finger can move while long tapping. + */ + public static final int LONG_TAP_THRESHOLD = 3; } -- cgit v1.2.3 From 448619c5168603c134ad4d578bae1d577b00b543 Mon Sep 17 00:00:00 2001 From: Henrik Paul Date: Tue, 5 Aug 2014 11:27:08 +0300 Subject: Sending a drag select over the wire in a batch (#13334) Change-Id: I49a518b484557d232542e999a2f41ffad3cc7568 --- .../client/data/AbstractRemoteDataSource.java | 32 ++++++++ .../vaadin/client/data/RpcDataSourceConnector.java | 31 ++++++- .../com/vaadin/client/ui/grid/GridConnector.java | 5 +- .../ui/grid/selection/MultiSelectionRenderer.java | 54 +++++++++++- .../ui/grid/selection/SelectionChangeEvent.java | 93 +++++++++++++-------- .../client/ui/grid/selection/SelectionModel.java | 53 ++++++++++++ .../ui/grid/selection/SelectionModelMulti.java | 96 +++++++++++++++++++--- .../ui/grid/selection/SelectionModelSingle.java | 5 +- .../com/vaadin/data/RpcDataProviderExtension.java | 69 +++++++++++++++- server/src/com/vaadin/ui/components/grid/Grid.java | 4 +- .../src/com/vaadin/shared/data/DataRequestRpc.java | 14 +++- 11 files changed, 400 insertions(+), 56 deletions(-) (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java index 1ce68ced17..d7d590dc42 100644 --- a/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java +++ b/client/src/com/vaadin/client/data/AbstractRemoteDataSource.java @@ -16,6 +16,8 @@ package com.vaadin.client.data; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -156,6 +158,7 @@ public abstract class AbstractRemoteDataSource implements DataSource { private Map pinnedCounts = new HashMap(); private Map pinnedRows = new HashMap(); + protected Collection temporarilyPinnedRows = Collections.emptySet(); /** * Sets the estimated number of rows in the data source. @@ -533,4 +536,33 @@ public abstract class AbstractRemoteDataSource implements DataSource { * row object */ abstract public Object getRowKey(T row); + + /** + * Marks rows as pinned when fetching new rows. + *

+ * This collection of rows are intended to remain pinned if new rows are + * fetched from the data source, even if some of the pinned rows would fall + * off the cache and become inactive. + *

+ * This method does nothing by itself, other than it stores the rows into a + * field. The implementation needs to make all the adjustments for itself. + * Check {@link RpcDataSourceConnector.RpcDataSource#requestRows(int, int)} + * for an implementation example. + * + * @param keys + * a collection of rows to keep pinned + * + * @see #temporarilyPinnedRows + * @see RpcDataSourceConnector.RpcDataSource#requestRows(int, int) + * @deprecated You probably don't want to call this method unless you're + * writing a Renderer for a selection model. Even if you are, be + * very aware what this method does and how it behaves. + */ + @Deprecated + public void transactionPin(Collection rows) { + if (rows == null) { + throw new IllegalArgumentException("argument may not be null"); + } + temporarilyPinnedRows = rows; + } } diff --git a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java index 55c37185e0..2819837504 100644 --- a/client/src/com/vaadin/client/data/RpcDataSourceConnector.java +++ b/client/src/com/vaadin/client/data/RpcDataSourceConnector.java @@ -17,6 +17,9 @@ package com.vaadin.client.data; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONObject; @@ -47,12 +50,27 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { public class RpcDataSource extends AbstractRemoteDataSource { + private Collection prevRows = Collections.emptySet(); + @Override protected void requestRows(int firstRowIndex, int numberOfRows) { Range cached = getCachedRange(); + Collection newRows = new ArrayList( + temporarilyPinnedRows); + newRows.removeAll(prevRows); + + List temporarilyPinnedKeys = new ArrayList( + newRows.size()); + for (JSONObject row : newRows) { + temporarilyPinnedKeys.add((String) getRowKey(row)); + } + getRpcProxy(DataRequestRpc.class).requestRows(firstRowIndex, - numberOfRows, cached.getStart(), cached.length()); + numberOfRows, cached.getStart(), cached.length(), + temporarilyPinnedKeys); + + prevRows = temporarilyPinnedRows; } @Override @@ -70,6 +88,17 @@ public class RpcDataSourceConnector extends AbstractExtensionConnector { row.put(GridState.JSONKEY_ROWKEY, new JSONString((String) key)); return new RowHandleImpl(row, key); } + + @Override + @SuppressWarnings("deprecation") + public void transactionPin(Collection keys) { + super.transactionPin(keys); + if (keys.isEmpty() && !prevRows.isEmpty()) { + prevRows = Collections.emptySet(); + getRpcProxy(DataRequestRpc.class) + .releaseTemporarilyPinnedKeys(); + } + } } private final RpcDataSource dataSource = new RpcDataSource(); diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 17a9d22d77..9d93c81d82 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -150,6 +150,9 @@ public class GridConnector extends AbstractHasComponentsConnector { private SelectionChangeHandler internalSelectionChangeHandler = new SelectionChangeHandler() { @Override public void onSelectionChange(SelectionChangeEvent event) { + if (event.isBatchedSelection()) { + return; + } if (!updatedFromState) { for (JSONObject row : event.getRemoved()) { selectedKeys.remove(dataSource.getRowKey(row)); @@ -514,7 +517,7 @@ public class GridConnector extends AbstractHasComponentsConnector { updatedFromState = true; getWidget().fireEvent( new SelectionChangeEvent(getWidget(), - (List) null, null)); + (List) null, null, false)); } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java b/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java index 0204a8862b..1033b00623 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java +++ b/client/src/com/vaadin/client/ui/grid/selection/MultiSelectionRenderer.java @@ -16,6 +16,7 @@ package com.vaadin.client.ui.grid.selection; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import com.google.gwt.animation.client.AnimationScheduler; @@ -32,10 +33,13 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.vaadin.client.Util; +import com.vaadin.client.data.AbstractRemoteDataSource; +import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.grid.Cell; import com.vaadin.client.ui.grid.FlyweightCell; import com.vaadin.client.ui.grid.Grid; import com.vaadin.client.ui.grid.renderers.ComplexRenderer; +import com.vaadin.client.ui.grid.selection.SelectionModel.Multi.Batched; /* This class will probably not survive the final merge of all selection functionality. */ public class MultiSelectionRenderer extends ComplexRenderer { @@ -219,11 +223,14 @@ public class MultiSelectionRenderer extends ComplexRenderer { private boolean scrollAreaShouldRebound = false; + private AbstractRemoteDataSource remoteDataSource = null; + private Batched batchedSelectionModel = null; + public AutoScrollerAndSelector(final int topBound, final int bottomBound, final int gradientArea, final boolean selectionPaint) { - this.finalTopBound = topBound; - this.finalBottomBound = bottomBound; + finalTopBound = topBound; + finalBottomBound = bottomBound; this.gradientArea = gradientArea; this.selectionPaint = selectionPaint; } @@ -249,6 +256,14 @@ public class MultiSelectionRenderer extends ComplexRenderer { if (logicalRow != -1 && logicalRow != this.logicalRow) { this.logicalRow = logicalRow; setSelected(logicalRow, selectionPaint); + + if (remoteDataSource != null && batchedSelectionModel != null) { + Collection pinneds = batchedSelectionModel + .getSelectedRowsBatch(); + pinneds.addAll(batchedSelectionModel + .getDeselectedRowsBatch()); + remoteDataSource.transactionPin(pinneds); + } } reschedule(); @@ -296,16 +311,38 @@ public class MultiSelectionRenderer extends ComplexRenderer { scrollSpeed = ratio * SCROLL_TOP_SPEED_PX_SEC; } + @SuppressWarnings("deprecation") public void start(int logicalRowIndex) { running = true; setSelected(logicalRowIndex, selectionPaint); logicalRow = logicalRowIndex; reschedule(); + + DataSource dataSource = grid.getDataSource(); + SelectionModel selectionModel = grid.getSelectionModel(); + if (dataSource instanceof AbstractRemoteDataSource + && selectionModel instanceof Batched) { + this.remoteDataSource = (AbstractRemoteDataSource) dataSource; + this.batchedSelectionModel = (Batched) selectionModel; + + Collection pinneds = batchedSelectionModel + .getSelectedRowsBatch(); + pinneds.addAll(batchedSelectionModel.getDeselectedRowsBatch()); + remoteDataSource.transactionPin(pinneds); + } } + @SuppressWarnings("deprecation") public void stop() { running = false; + // split into two lines because of Java generics not playing nice. + @SuppressWarnings("unchecked") + Collection emptySet = (Collection) Collections.emptySet(); + remoteDataSource.transactionPin(emptySet); + remoteDataSource = null; + batchedSelectionModel = null; + if (handle != null) { handle.cancel(); handle = null; @@ -435,6 +472,13 @@ public class MultiSelectionRenderer extends ComplexRenderer { private int gradientArea; public void start(int logicalRowIndex) { + + SelectionModel model = grid.getSelectionModel(); + if (model instanceof Batched) { + Batched batchedModel = (Batched) model; + batchedModel.startBatchSelect(); + } + /* * bounds are updated whenever the autoscroll cycle starts, to make * sure that the widget hasn't changed in size, moved around, or @@ -522,6 +566,12 @@ public class MultiSelectionRenderer extends ComplexRenderer { autoScroller = null; } + SelectionModel model = grid.getSelectionModel(); + if (model instanceof Batched) { + Batched batchedModel = (Batched) model; + batchedModel.commitBatchSelect(); + } + removeNativeHandler(); } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java index 5c5afef065..4bebaf0fbb 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionChangeEvent.java @@ -25,10 +25,11 @@ import com.vaadin.client.ui.grid.Grid; /** * Event object describing a change in Grid row selection state. - * + * * @since * @author Vaadin Ltd */ +@SuppressWarnings("rawtypes") public class SelectionChangeEvent extends GwtEvent { private static final Type eventType = new Type(); @@ -36,66 +37,77 @@ public class SelectionChangeEvent extends GwtEvent { private final Grid grid; private final List added; private final List removed; - - /** - * Basic constructor. - * - * @param grid - * Grid reference, used for getSource - */ - private SelectionChangeEvent(Grid grid) { - if (grid == null) { - throw new IllegalArgumentException("grid parameter can not be null"); - } - this.grid = grid; - added = new ArrayList(); - removed = new ArrayList(); - } + private final boolean batched; /** * Creates an event with a single added or removed row. - * + * * @param grid - * Grid reference, used for getSource + * grid reference, used for getSource * @param added - * Added row + * the added row, or null if a row was not added * @param removed - * Removed row + * the removed row, or null if a row was not removed + * @param batched + * whether or not this selection change event is triggered during + * a batched selection/deselection action + * @see SelectionModel.Multi.Batched */ - public SelectionChangeEvent(Grid grid, T added, T removed) { - this(grid); + public SelectionChangeEvent(Grid grid, T added, T removed, + boolean batched) { + this.grid = grid; + this.batched = batched; + if (added != null) { - this.added.add(added); + this.added = Collections.singletonList(added); + } else { + this.added = Collections.emptyList(); } + if (removed != null) { - this.removed.add(removed); + this.removed = Collections.singletonList(removed); + } else { + this.removed = Collections.emptyList(); } } /** * Creates an event where several rows have been added or removed. - * + * * @param grid * Grid reference, used for getSource * @param added - * collection of added rows + * a collection of added rows, or null if no rows + * were added * @param removed - * collection of removed rows + * a collection of removed rows, or null if no rows + * were removed + * @param batched + * whether or not this selection change event is triggered during + * a batched selection/deselection action + * @see SelectionModel.Multi.Batched */ public SelectionChangeEvent(Grid grid, Collection added, - Collection removed) { - this(grid); + Collection removed, boolean batched) { + this.grid = grid; + this.batched = batched; + if (added != null) { - this.added.addAll(added); + this.added = new ArrayList(added); + } else { + this.added = Collections.emptyList(); } + if (removed != null) { - this.removed.addAll(removed); + this.removed = new ArrayList(removed); + } else { + this.removed = Collections.emptyList(); } } /** * Get a reference to the Grid object that fired this event. - * + * * @return a grid reference */ @Override @@ -106,7 +118,7 @@ public class SelectionChangeEvent extends GwtEvent { /** * Get all rows added to the selection since the last * {@link SelectionChangeEvent}. - * + * * @return a collection of added rows. Empty collection if no rows were * added. */ @@ -117,7 +129,7 @@ public class SelectionChangeEvent extends GwtEvent { /** * Get all rows removed from the selection since the last * {@link SelectionChangeEvent}. - * + * * @return a collection of removed rows. Empty collection if no rows were * removed. */ @@ -127,7 +139,7 @@ public class SelectionChangeEvent extends GwtEvent { /** * Gets a type identifier for this event. - * + * * @return a {@link Type} identifier. */ public static Type getType() { @@ -140,8 +152,19 @@ public class SelectionChangeEvent extends GwtEvent { } @Override + @SuppressWarnings("unchecked") protected void dispatch(SelectionChangeHandler handler) { handler.onSelectionChange(this); } + /** + * Checks if this selection change event is fired during a batched + * selection/deselection operation. + * + * @return true iff this event is fired during a batched + * selection/deselection operation + */ + public boolean isBatchedSelection() { + return batched; + } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java index cc2f2b06d9..55336ce3da 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModel.java @@ -125,6 +125,59 @@ public interface SelectionModel { */ public interface Multi extends SelectionModel { + /** + * A multi selection model that can send selections and deselections in + * a batch, instead of committing them one-by-one. + * + * @param + * type parameter corresponding with Grid row type + */ + public interface Batched extends Multi { + /** + * Starts a batch selection. + *

+ * Any commands to any select or deselect method will be batched + * into one, and a final selection event will be fired when + * {@link #commitBatchSelect()} is called. + *

+ * Note: {@link SelectionChangeEvent SelectionChangeEvents} + * will still be fired for each selection/deselection. You should + * check whether the event is a part of a batch or not with + * {@link SelectionChangeEvent#isBatchedSelection()}. + */ + public void startBatchSelect(); + + /** + * Commits and ends a batch selection. + *

+ * Any and all selections and deselections since the last invocation + * of {@link #startBatchSelect()} will be fired at once as one + * collated {@link SelectionChangeEvent}. + */ + public void commitBatchSelect(); + + /** + * Checks whether or not a batch has been started. + * + * @return true iff a batch has been started + */ + public boolean isBeingBatchSelected(); + + /** + * Gets all the rows that would become selected in this batch. + * + * @return a collection of the rows that would become selected + */ + public Collection getSelectedRowsBatch(); + + /** + * Gets all the rows that would become deselected in this batch. + * + * @return a collection of the rows that would become deselected + */ + public Collection getDeselectedRowsBatch(); + } + /** * Selects one or more rows. * diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java index 6f2896b43a..b6ecc945e2 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelMulti.java @@ -15,8 +15,10 @@ */ package com.vaadin.client.ui.grid.selection; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -31,12 +33,16 @@ import com.vaadin.client.ui.grid.Renderer; * @since */ public class SelectionModelMulti extends AbstractRowHandleSelectionModel - implements SelectionModel.Multi { + implements SelectionModel.Multi.Batched { - private final Set> selectedRows; + private final LinkedHashSet> selectedRows; private Renderer renderer; private Grid grid; + private boolean batchStarted = false; + private final LinkedHashSet> selectionBatch = new LinkedHashSet>(); + private final LinkedHashSet> deselectionBatch = new LinkedHashSet>(); + public SelectionModelMulti() { grid = null; renderer = null; @@ -89,11 +95,20 @@ public class SelectionModelMulti extends AbstractRowHandleSelectionModel public boolean deselectAll() { if (selectedRows.size() > 0) { + @SuppressWarnings("unchecked") + final LinkedHashSet> selectedRowsClone = (LinkedHashSet>) selectedRows + .clone(); SelectionChangeEvent event = new SelectionChangeEvent(grid, - null, getSelectedRows()); + null, getSelectedRows(), isBeingBatchSelected()); selectedRows.clear(); - grid.fireEvent(event); + if (isBeingBatchSelected()) { + selectionBatch.clear(); + deselectionBatch.clear(); + deselectionBatch.addAll(selectedRowsClone); + } + + grid.fireEvent(event); return true; } return false; @@ -115,7 +130,8 @@ public class SelectionModelMulti extends AbstractRowHandleSelectionModel } if (added.size() > 0) { - grid.fireEvent(new SelectionChangeEvent(grid, added, null)); + grid.fireEvent(new SelectionChangeEvent(grid, added, null, + isBeingBatchSelected())); return true; } @@ -131,14 +147,15 @@ public class SelectionModelMulti extends AbstractRowHandleSelectionModel Set removed = new LinkedHashSet(); for (T row : rows) { - if (deselectByHandle(grid.getDataSource().getHandle(row))) { + RowHandle handle = grid.getDataSource().getHandle(row); + if (deselectByHandle(handle)) { removed.add(row); } } if (removed.size() > 0) { - grid.fireEvent(new SelectionChangeEvent(grid, null, removed)); - + grid.fireEvent(new SelectionChangeEvent(grid, null, removed, + isBeingBatchSelected())); return true; } return false; @@ -152,6 +169,12 @@ public class SelectionModelMulti extends AbstractRowHandleSelectionModel protected boolean selectByHandle(RowHandle handle) { if (selectedRows.add(handle)) { handle.pin(); + + if (isBeingBatchSelected()) { + deselectionBatch.remove(handle); + selectionBatch.add(handle); + } + return true; } return false; @@ -160,7 +183,12 @@ public class SelectionModelMulti extends AbstractRowHandleSelectionModel @Override protected boolean deselectByHandle(RowHandle handle) { if (selectedRows.remove(handle)) { - handle.unpin(); + if (!isBeingBatchSelected()) { + handle.unpin(); + } else { + selectionBatch.remove(handle); + deselectionBatch.add(handle); + } return true; } return false; @@ -172,11 +200,59 @@ public class SelectionModelMulti extends AbstractRowHandleSelectionModel for (RowHandle handle : selectedRows) { selected.add(handle.getRow()); } - return selected; + return Collections.unmodifiableSet(selected); } @Override public void reset() { deselectAll(); } + + @Override + public void startBatchSelect() { + assert !isBeingBatchSelected() : "Batch has already been started"; + batchStarted = true; + } + + @Override + public void commitBatchSelect() { + assert isBeingBatchSelected() : "Batch was never started"; + if (!isBeingBatchSelected()) { + return; + } + + batchStarted = false; + + final Collection added = getSelectedRowsBatch(); + selectionBatch.clear(); + + final Collection removed = getDeselectedRowsBatch(); + deselectionBatch.clear(); + + grid.fireEvent(new SelectionChangeEvent(grid, added, removed, + isBeingBatchSelected())); + } + + @Override + public boolean isBeingBatchSelected() { + return batchStarted; + } + + @Override + public Collection getSelectedRowsBatch() { + return rowHandlesToRows(selectionBatch); + } + + @Override + public Collection getDeselectedRowsBatch() { + return rowHandlesToRows(deselectionBatch); + } + + private ArrayList rowHandlesToRows(Collection> rowHandles) { + ArrayList rows = new ArrayList(rowHandles.size()); + for (RowHandle handle : rowHandles) { + rows.add(handle.getRow()); + } + return rows; + } } diff --git a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java index 2942538d81..d63b371c4d 100644 --- a/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java +++ b/client/src/com/vaadin/client/ui/grid/selection/SelectionModelSingle.java @@ -70,7 +70,8 @@ public class SelectionModelSingle extends AbstractRowHandleSelectionModel T removed = getSelectedRow(); if (selectByHandle(grid.getDataSource().getHandle(row))) { - grid.fireEvent(new SelectionChangeEvent(grid, row, removed)); + grid.fireEvent(new SelectionChangeEvent(grid, row, removed, + false)); return true; } @@ -86,7 +87,7 @@ public class SelectionModelSingle extends AbstractRowHandleSelectionModel if (isSelected(row)) { deselectByHandle(selectedRow); - grid.fireEvent(new SelectionChangeEvent(grid, null, row)); + grid.fireEvent(new SelectionChangeEvent(grid, null, row, false)); return true; } diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index f731e4575d..86fa11f62e 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -53,6 +53,8 @@ import com.vaadin.shared.ui.grid.Range; import com.vaadin.ui.components.grid.Grid; import com.vaadin.ui.components.grid.GridColumn; import com.vaadin.ui.components.grid.Renderer; +import com.vaadin.ui.components.grid.selection.SelectionChangeEvent; +import com.vaadin.ui.components.grid.selection.SelectionChangeListener; /** * Provides Vaadin server-side container data source to a @@ -107,10 +109,10 @@ public class RpcDataProviderExtension extends AbstractExtension { final Integer ii = Integer.valueOf(i); final Object itemId = indexToItemId.get(ii); - if (!pinnedItemIds.contains(itemId)) { + if (!isPinned(itemId)) { itemIdToKey.remove(itemId); + indexToItemId.remove(ii); } - indexToItemId.remove(ii); } } @@ -610,9 +612,21 @@ public class RpcDataProviderExtension extends AbstractExtension { this.container = container; registerRpc(new DataRequestRpc() { + private Collection allTemporarilyPinnedKeys = new ArrayList(); + @Override public void requestRows(int firstRow, int numberOfRows, - int firstCachedRowIndex, int cacheSize) { + int firstCachedRowIndex, int cacheSize, + List temporarilyPinnedKeys) { + + for (String key : temporarilyPinnedKeys) { + Object itemId = keyMapper.getItemId(key); + if (!keyMapper.isPinned(itemId)) { + keyMapper.pin(itemId); + } + } + allTemporarilyPinnedKeys.addAll(temporarilyPinnedKeys); + Range active = Range.withLength(firstRow, numberOfRows); if (cacheSize != 0) { Range cached = Range.withLength(firstCachedRowIndex, @@ -628,6 +642,55 @@ public class RpcDataProviderExtension extends AbstractExtension { activeRowHandler.setActiveRows(active.getStart(), active.length()); } + + @Override + public void releaseTemporarilyPinnedKeys() { + /* + * This needs to be done deferredly since the selection event + * comes after this RPC call. + */ + + final SelectionChangeListener listener = new SelectionChangeListener() { + @Override + public void selectionChange(SelectionChangeEvent event) { + for (String tempPinnedKey : allTemporarilyPinnedKeys) { + /* + * TODO: this could be moved into a field instead of + * inline to reduce indentations. + */ + + /* + * This works around the fact that when deselecting + * and leaping through the cache, the client tries + * to send a deselect event even though a row never + * was selected. So, it tries to unpin something + * that never was temporarily pinned. + * + * If the same thing would happen while selecting + * (instead of deselecting), the row would be + * pinned, not because of the temporary pinning, but + * because it's selected. + */ + if (!keyMapper.isPinned(tempPinnedKey)) { + continue; + } + + Object itemId = keyMapper.getItemId(tempPinnedKey); + Integer index = keyMapper.indexToItemId.inverse() + .get(itemId); + if (!getGrid().isSelected(itemId) + && !activeRowHandler.activeRange + .contains(index.intValue())) { + keyMapper.unpin(itemId); + } + } + allTemporarilyPinnedKeys = new ArrayList(); + getGrid().removeSelectionChangeListener(this); + } + }; + + getGrid().addSelectionChangeListener(listener); + } }); getState().containerSize = container.size(); diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index d365d3e0cc..fba6eed462 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -282,7 +282,9 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, } for (Object addedItemId : event.getAdded()) { - getKeyMapper().pin(addedItemId); + if (!getKeyMapper().isPinned(addedItemId)) { + getKeyMapper().pin(addedItemId); + } } List keys = getKeyMapper().getKeys(getSelectedRows()); diff --git a/shared/src/com/vaadin/shared/data/DataRequestRpc.java b/shared/src/com/vaadin/shared/data/DataRequestRpc.java index 8b0bd4adcb..000d196be9 100644 --- a/shared/src/com/vaadin/shared/data/DataRequestRpc.java +++ b/shared/src/com/vaadin/shared/data/DataRequestRpc.java @@ -16,6 +16,8 @@ package com.vaadin.shared.data; +import java.util.List; + import com.vaadin.shared.communication.ServerRpc; /** @@ -37,7 +39,17 @@ public interface DataRequestRpc extends ServerRpc { * the index of the first cached row * @param cacheSize * the number of cached rows + * @param temporarilyPinnedKeys + * the keys that should remain pinned, even if some of these + * would fall out of the cache range */ public void requestRows(int firstRowIndex, int numberOfRows, - int firstCachedRowIndex, int cacheSize); + int firstCachedRowIndex, int cacheSize, + List temporarilyPinnedKeys); + + /** + * Informs the back-end that the temporarily pinned keys in + * {@link #requestRows(int, int, int, int, List)} may be released. + */ + public void releaseTemporarilyPinnedKeys(); } -- cgit v1.2.3 From 32842a06a64d4351018ffb720d12aa8cd6011743 Mon Sep 17 00:00:00 2001 From: Patrik Lindström Date: Mon, 18 Aug 2014 16:04:28 +0300 Subject: Add event originator information to Sort Events (#13334) Change-Id: I9f8a295d6944807ccf89ea535d5500fac013e127 --- client/src/com/vaadin/client/ui/grid/Grid.java | 16 ++++++--- .../com/vaadin/client/ui/grid/GridConnector.java | 4 +-- .../com/vaadin/client/ui/grid/sort/SortEvent.java | 22 ++++++++++-- server/src/com/vaadin/ui/components/grid/Grid.java | 21 +++++++---- .../ui/components/grid/SortOrderChangeEvent.java | 21 ++++++++++- .../com/vaadin/shared/ui/grid/GridServerRpc.java | 3 +- .../vaadin/shared/ui/grid/SortEventOriginator.java | 41 ++++++++++++++++++++++ .../grid/basicfeatures/GridBasicFeatures.java | 19 +++++++++- .../grid/basicfeatures/server/GridSortingTest.java | 20 ++++++++--- 9 files changed, 142 insertions(+), 25 deletions(-) create mode 100644 shared/src/com/vaadin/shared/ui/grid/SortEventOriginator.java (limited to 'shared/src/com') diff --git a/client/src/com/vaadin/client/ui/grid/Grid.java b/client/src/com/vaadin/client/ui/grid/Grid.java index a2bb0d3f09..22c3604bf8 100644 --- a/client/src/com/vaadin/client/ui/grid/Grid.java +++ b/client/src/com/vaadin/client/ui/grid/Grid.java @@ -87,6 +87,7 @@ import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.Range; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.ui.grid.SortDirection; +import com.vaadin.shared.ui.grid.SortEventOriginator; /** * A data grid view that supports columns and lazy loading of data rows from a @@ -622,8 +623,8 @@ public class Grid extends Composite implements } } - // Perform sorting - Grid.this.sort(sorting); + // Perform sorting; indicate originator as user + Grid.this.setSortOrder(sorting.build(), SortEventOriginator.USER); } } @@ -2376,11 +2377,16 @@ public class Grid extends Composite implements * a sort order list. If set to null, the sort order is cleared. */ public void setSortOrder(List order) { + setSortOrder(order, SortEventOriginator.API); + } + + private void setSortOrder(List order, + SortEventOriginator originator) { sortOrder.clear(); if (order != null) { sortOrder.addAll(order); } - sort(); + sort(originator); } /** @@ -2505,9 +2511,9 @@ public class Grid extends Composite implements /** * Apply sorting to data source. */ - private void sort() { + private void sort(SortEventOriginator originator) { refreshHeader(); fireEvent(new SortEvent(this, - Collections.unmodifiableList(sortOrder))); + Collections.unmodifiableList(sortOrder), originator)); } } diff --git a/client/src/com/vaadin/client/ui/grid/GridConnector.java b/client/src/com/vaadin/client/ui/grid/GridConnector.java index 9d93c81d82..73440232b1 100644 --- a/client/src/com/vaadin/client/ui/grid/GridConnector.java +++ b/client/src/com/vaadin/client/ui/grid/GridConnector.java @@ -224,8 +224,8 @@ public class GridConnector extends AbstractHasComponentsConnector { if (!Arrays.equals(columnIds, getState().sortColumns) || !Arrays.equals(directions, getState().sortDirs)) { // Report back to server if changed - getRpcProxy(GridServerRpc.class) - .sort(columnIds, directions); + getRpcProxy(GridServerRpc.class).sort(columnIds, + directions, event.getOriginator()); } } }); diff --git a/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java b/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java index baa12ae224..edbd84c4a5 100644 --- a/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java +++ b/client/src/com/vaadin/client/ui/grid/sort/SortEvent.java @@ -20,6 +20,7 @@ import java.util.List; import com.google.gwt.event.shared.GwtEvent; import com.vaadin.client.data.DataSource; import com.vaadin.client.ui.grid.Grid; +import com.vaadin.shared.ui.grid.SortEventOriginator; /** * A sort event, fired by the Grid when it needs its data source to provide data @@ -34,6 +35,7 @@ public class SortEvent extends GwtEvent> { private final Grid grid; private final List order; + private final SortEventOriginator originator; /** * Creates a new Sort Event. All provided parameters are final, and passed @@ -41,14 +43,16 @@ public class SortEvent extends GwtEvent> { * * @param grid * a grid reference - * @param datasource - * a reference to the grid's data source * @param order * an array dictating the desired sort order of the data source + * @param originator + * a value indicating where this event originated from */ - public SortEvent(Grid grid, List order) { + public SortEvent(Grid grid, List order, + SortEventOriginator originator) { this.grid = grid; this.order = order; + this.originator = originator; } @Override @@ -103,6 +107,18 @@ public class SortEvent extends GwtEvent> { return order; } + /** + * Gets a value describing the originator of this event, i.e. what actions + * resulted in this event being fired. + * + * @return a sort event originator value + * + * @see SortEventOriginator + */ + public SortEventOriginator getOriginator() { + return originator; + } + @SuppressWarnings("unchecked") @Override protected void dispatch(SortEventHandler handler) { diff --git a/server/src/com/vaadin/ui/components/grid/Grid.java b/server/src/com/vaadin/ui/components/grid/Grid.java index fba6eed462..3c115f9241 100644 --- a/server/src/com/vaadin/ui/components/grid/Grid.java +++ b/server/src/com/vaadin/ui/components/grid/Grid.java @@ -49,6 +49,7 @@ import com.vaadin.shared.ui.grid.GridStaticCellType; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.ui.grid.SortDirection; +import com.vaadin.shared.ui.grid.SortEventOriginator; import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.Component; import com.vaadin.ui.HasComponents; @@ -373,7 +374,8 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, } @Override - public void sort(String[] columnIds, SortDirection[] directions) { + public void sort(String[] columnIds, SortDirection[] directions, + SortEventOriginator originator) { assert columnIds.length == directions.length; List order = new ArrayList( @@ -383,7 +385,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, order.add(new SortOrder(propertyId, directions[i])); } - setSortOrder(order); + setSortOrder(order, originator); } }); } @@ -439,7 +441,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, } } - sort(); + sort(SortEventOriginator.INTERNAL); } else { // If the new container is not sortable, we'll just re-set the sort @@ -1126,7 +1128,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, */ public void clearSortOrder() { sortOrder.clear(); - sort(); + sort(false); } /** @@ -1140,6 +1142,11 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, * a sort order list. */ public void setSortOrder(List order) { + setSortOrder(order, SortEventOriginator.API); + } + + private void setSortOrder(List order, + SortEventOriginator originator) { if (!(getContainerDatasource() instanceof Container.Sortable)) { throw new IllegalStateException( "Attached container is not sortable (does not implement Container.Sortable)"); @@ -1164,7 +1171,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, } sortOrder.addAll(order); - sort(); + sort(originator); } /** @@ -1179,7 +1186,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, /** * Apply sorting to data source. */ - private void sort() { + private void sort(SortEventOriginator originator) { Container c = getContainerDatasource(); if (c instanceof Container.Sortable) { @@ -1215,7 +1222,7 @@ public class Grid extends AbstractComponent implements SelectionChangeNotifier, cs.sort(propertyIds, directions); fireEvent(new SortOrderChangeEvent(this, new ArrayList( - sortOrder))); + sortOrder), originator)); getState().sortColumns = columnKeys; getState(false).sortDirs = stateDirs; diff --git a/server/src/com/vaadin/ui/components/grid/SortOrderChangeEvent.java b/server/src/com/vaadin/ui/components/grid/SortOrderChangeEvent.java index 71afa10a9b..690fcdf1c4 100644 --- a/server/src/com/vaadin/ui/components/grid/SortOrderChangeEvent.java +++ b/server/src/com/vaadin/ui/components/grid/SortOrderChangeEvent.java @@ -17,6 +17,7 @@ package com.vaadin.ui.components.grid; import java.util.List; +import com.vaadin.shared.ui.grid.SortEventOriginator; import com.vaadin.ui.Component; import com.vaadin.ui.components.grid.sort.SortOrder; @@ -31,6 +32,7 @@ import com.vaadin.ui.components.grid.sort.SortOrder; public class SortOrderChangeEvent extends Component.Event { private final List sortOrder; + private final SortEventOriginator originator; /** * Creates a new sort order change event for a grid and a sort order list. @@ -39,10 +41,15 @@ public class SortOrderChangeEvent extends Component.Event { * the grid from which the event originates * @param sortOrder * the new sort order list + * @param wasInitiatedByUser + * should be set to true if this event results from end-user + * interaction instead of an API call or side effect */ - public SortOrderChangeEvent(Grid grid, List sortOrder) { + public SortOrderChangeEvent(Grid grid, List sortOrder, + SortEventOriginator originator) { super(grid); this.sortOrder = sortOrder; + this.originator = originator; } /** @@ -54,4 +61,16 @@ public class SortOrderChangeEvent extends Component.Event { return sortOrder; } + /** + * Gets a value describing the originator of this event, i.e. what actions + * resulted in this event being fired. + * + * @return a sort event originator value + * + * @see SortEventOriginator + */ + public SortEventOriginator getOriginator() { + return originator; + } + } diff --git a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java index 9ce094b092..fd671e30a7 100644 --- a/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/grid/GridServerRpc.java @@ -28,5 +28,6 @@ import com.vaadin.shared.communication.ServerRpc; public interface GridServerRpc extends ServerRpc { void selectionChange(List newSelection); - void sort(String[] columnIds, SortDirection[] directions); + void sort(String[] columnIds, SortDirection[] directions, + SortEventOriginator originator); } diff --git a/shared/src/com/vaadin/shared/ui/grid/SortEventOriginator.java b/shared/src/com/vaadin/shared/ui/grid/SortEventOriginator.java new file mode 100644 index 0000000000..acdd46ea5b --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/grid/SortEventOriginator.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.shared.ui.grid; + +/** + * Identifier for the originator of a sort event or sort order change event. + * + * @since + * @author Vaadin Ltd + */ +public enum SortEventOriginator { + + /** + * This event was the result of an API call. + */ + API, + + /** + * This event was the result of a user interacting with the UI. + */ + USER, + + /** + * This event resulted as a side-effect of an internal event. + */ + INTERNAL + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java index 031ebf7fa5..d54b1838ea 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -168,7 +168,24 @@ public class GridBasicFeatures extends AbstractComponentTest { grid.addSortOrderChangeListener(new SortOrderChangeListener() { @Override public void sortOrderChange(SortOrderChangeEvent event) { - log("Sort order: " + event.getSortOrder()); + + String origin; + switch (event.getOriginator()) { + case API: + origin = "API"; + break; + case INTERNAL: + origin = "INTERNAL"; + break; + case USER: + origin = "USER"; + break; + default: + origin = "!!! ERROR !!!"; + break; + } + + log("Sort order: " + event.getSortOrder() + " by " + origin); } }); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java index a5a83c156e..024be65e83 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSortingTest.java @@ -42,6 +42,11 @@ public class GridSortingTest extends GridBasicFeaturesTest { // First cells for first 3 rows are (9, 0), (99, 0) and (999, 0) sortBy("Column 9, DESC"); + // Verify that programmatic sorting calls are identified as originating + // from API + assertEquals("3. Sort order: [Column 9 DESCENDING] by API", + getLogRow(0)); + assertTrue("Column 9 should have the sort-desc stylename", grid .getHeaderCell(0, 9).getAttribute("class") .contains("sort-desc")); @@ -119,14 +124,17 @@ public class GridSortingTest extends GridBasicFeaturesTest { "(" + row + ", 0)", grid.getCell(i, 0).getText()); } - assertEquals("2. Sort order: [Column 9 ASCENDING]", getLogRow(2)); - assertEquals("4. Sort order: [Column 9 DESCENDING]", getLogRow(0)); + assertEquals("2. Sort order: [Column 9 ASCENDING] by USER", + getLogRow(2)); + assertEquals("4. Sort order: [Column 9 DESCENDING] by USER", + getLogRow(0)); // Column 10 is random numbers from Random with seed 13334 // Click header to sort ascending grid.getHeaderCell(0, 10).click(); - assertEquals("6. Sort order: [Column 10 ASCENDING]", getLogRow(0)); + assertEquals("6. Sort order: [Column 10 ASCENDING] by USER", + getLogRow(0)); // Not cleaning up correctly causes exceptions when scrolling. grid.scrollToRow(50); @@ -153,8 +161,10 @@ public class GridSortingTest extends GridBasicFeaturesTest { grid.getCell(GridBasicFeatures.ROWS - (i + 1), 0).getText()); } - assertEquals("9. Sort order: [Column 7 ASCENDING]", getLogRow(3)); - assertEquals("11. Sort order: [Column 7 DESCENDING]", getLogRow(1)); + assertEquals("9. Sort order: [Column 7 ASCENDING] by USER", + getLogRow(3)); + assertEquals("11. Sort order: [Column 7 DESCENDING] by USER", + getLogRow(1)); } @Test -- cgit v1.2.3