Change-Id: I069df183806937c2d97eb3e9c8a073ef53ab5c24tags/8.0.0.alpha1
@@ -57,6 +57,8 @@ import elemental.json.JsonValue; | |||
public class ConnectorBundle { | |||
private static final String FAIL_IF_NOT_SERIALIZABLE = "vFailIfNotSerializable"; | |||
static final String OLD_RENDERER_CONNECTOR_NAME = "com.vaadin.v7.client.connectors.AbstractRendererConnector"; | |||
public static final Comparator<JClassType> jClassComparator = new Comparator<JClassType>() { | |||
@Override | |||
public int compare(JClassType o1, JClassType o2) { | |||
@@ -475,8 +477,7 @@ public class ConnectorBundle { | |||
} | |||
public static boolean isConnectedRendererConnector(JClassType type) { | |||
return isConnected(type) | |||
&& isType(type, AbstractRendererConnector.class); | |||
return isConnected(type) && isRendererType(type); | |||
} | |||
private static boolean isInterfaceType(JClassType type, Class<?> class1) { | |||
@@ -492,6 +493,22 @@ public class ConnectorBundle { | |||
} | |||
} | |||
private static boolean isRendererType(JClassType type) { | |||
TypeOracle oracle = type.getOracle(); | |||
boolean isNew = false, isOld = false; | |||
try { | |||
isNew = oracle.getType(AbstractRendererConnector.class.getName()) | |||
.isAssignableFrom(type); | |||
} catch (NotFoundException e) { | |||
} | |||
try { | |||
isOld = oracle.getType(OLD_RENDERER_CONNECTOR_NAME) | |||
.isAssignableFrom(type); | |||
} catch (NotFoundException e) { | |||
} | |||
return isNew || isOld; | |||
} | |||
public void setNeedsInvoker(JClassType type, JMethod method) { | |||
if (!isNeedsInvoker(type, method)) { | |||
addMapping(needsInvoker, type, method); |
@@ -61,13 +61,15 @@ public class RendererVisitor extends TypeVisitor { | |||
.findInheritedMethod(type, "createRenderer").getEnclosingType(); | |||
// Needs GWT constructor if createRenderer is not overridden | |||
if (createRendererClass.getQualifiedSourceName() | |||
.equals(AbstractRendererConnector.class.getCanonicalName())) { | |||
String connectorSrcName = createRendererClass.getQualifiedSourceName(); | |||
if (isAbstractRendererConnector(connectorSrcName)) { | |||
// createRenderer not overridden | |||
JMethod getRenderer = ConnectorBundle.findInheritedMethod(type, | |||
"getRenderer"); | |||
if (getRenderer.getEnclosingType().getQualifiedSourceName().equals( | |||
AbstractRendererConnector.class.getCanonicalName())) { | |||
String rendererSrcName = getRenderer.getEnclosingType() | |||
.getQualifiedSourceName(); | |||
if (isAbstractRendererConnector(rendererSrcName)) { | |||
// getRenderer not overridden | |||
logger.log(Type.ERROR, type.getQualifiedSourceName() | |||
+ " must override either createRenderer or getRenderer"); | |||
@@ -108,8 +110,9 @@ public class RendererVisitor extends TypeVisitor { | |||
throw new NotFoundException(); | |||
} | |||
return !decodeMethod.getEnclosingType().getQualifiedSourceName() | |||
.equals(AbstractRendererConnector.class.getName()); | |||
String decodeSrcName = decodeMethod.getEnclosingType() | |||
.getQualifiedSourceName(); | |||
return !isAbstractRendererConnector(decodeSrcName); | |||
} catch (NotFoundException e) { | |||
logger.log(Type.ERROR, | |||
"Can't find decode method for renderer " + type, e); | |||
@@ -121,8 +124,8 @@ public class RendererVisitor extends TypeVisitor { | |||
throws UnableToCompleteException { | |||
JClassType originalType = type; | |||
while (type != null) { | |||
if (type.getQualifiedBinaryName() | |||
.equals(AbstractRendererConnector.class.getName())) { | |||
String typeBinName = type.getQualifiedBinaryName(); | |||
if (isAbstractRendererConnector(typeBinName)) { | |||
JParameterizedType parameterized = type.isParameterized(); | |||
if (parameterized == null) { | |||
logger.log(Type.ERROR, type.getQualifiedSourceName() | |||
@@ -138,4 +141,11 @@ public class RendererVisitor extends TypeVisitor { | |||
+ originalType.getQualifiedSourceName() + " does not extend " | |||
+ AbstractRendererConnector.class.getName()); | |||
} | |||
private static boolean isAbstractRendererConnector(String connectorSrcName) { | |||
return connectorSrcName | |||
.equals(AbstractRendererConnector.class.getName()) | |||
|| connectorSrcName | |||
.equals(ConnectorBundle.OLD_RENDERER_CONNECTOR_NAME); | |||
} | |||
} |
@@ -16,9 +16,8 @@ | |||
package com.vaadin.v7.client.connectors; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.connectors.AbstractRendererConnector; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widgets.Grid.Column; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widgets.Grid.Column; | |||
import elemental.json.JsonObject; | |||
@@ -0,0 +1,128 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.connectors; | |||
import com.vaadin.client.ServerConnector; | |||
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.metadata.TypeDataStore; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import elemental.json.JsonValue; | |||
/** | |||
* An abstract base class for renderer connectors. | |||
* | |||
* @param <T> | |||
* the presentation type of the renderer | |||
*/ | |||
public abstract class AbstractRendererConnector<T> | |||
extends AbstractExtensionConnector { | |||
private Renderer<T> renderer = null; | |||
private final Type presentationType = TypeDataStore | |||
.getPresentationType(this.getClass()); | |||
protected AbstractRendererConnector() { | |||
if (presentationType == null) { | |||
throw new IllegalStateException("No presentation type found for " | |||
+ getClass().getSimpleName() | |||
+ ". This may be caused by some unspecified problem in widgetset compilation."); | |||
} | |||
} | |||
/** | |||
* Returns the renderer associated with this renderer connector. | |||
* <p> | |||
* 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: | |||
* | |||
* <pre> | |||
* public MyRenderer getRenderer() { | |||
* return (MyRenderer) super.getRenderer(); | |||
* } | |||
* </pre> | |||
* | |||
* @return the renderer bound to this connector | |||
*/ | |||
public Renderer<T> getRenderer() { | |||
if (renderer == null) { | |||
renderer = createRenderer(); | |||
} | |||
return renderer; | |||
} | |||
/** | |||
* Creates a new Renderer instance associated with this renderer connector. | |||
* <p> | |||
* You should typically not override this method since the framework by | |||
* default generates an implementation that uses | |||
* {@link com.google.gwt.core.client.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 | |||
* <code>super.createRenderer()</code> 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<T> createRenderer() { | |||
// TODO generate type data | |||
Type type = TypeData.getType(getClass()); | |||
try { | |||
Type rendererType = type.getMethod("getRenderer").getReturnType(); | |||
@SuppressWarnings("unchecked") | |||
Renderer<T> instance = (Renderer<T>) rendererType.createInstance(); | |||
return instance; | |||
} catch (NoDataException e) { | |||
throw new IllegalStateException( | |||
"Default implementation of createRenderer() does not work for " | |||
+ getClass().getSimpleName() | |||
+ ". 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}. | |||
* | |||
* @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(presentationType, value, | |||
null, getConnection()); | |||
return decodedValue; | |||
} | |||
@Override | |||
@Deprecated | |||
protected void extend(ServerConnector target) { | |||
// NOOP | |||
} | |||
} |
@@ -19,9 +19,9 @@ import java.util.Collection; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.extensions.AbstractExtensionConnector; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import elemental.json.JsonObject; | |||
@@ -16,9 +16,9 @@ | |||
package com.vaadin.v7.client.connectors; | |||
import com.google.web.bindery.event.shared.HandlerRegistration; | |||
import com.vaadin.client.renderers.ButtonRenderer; | |||
import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.renderers.ButtonRenderer; | |||
import com.vaadin.v7.client.renderers.ClickableRenderer.RendererClickHandler; | |||
import elemental.json.JsonObject; | |||
@@ -17,10 +17,10 @@ package com.vaadin.v7.client.connectors; | |||
import com.google.web.bindery.event.shared.HandlerRegistration; | |||
import com.vaadin.client.MouseEventDetailsBuilder; | |||
import com.vaadin.client.renderers.ClickableRenderer; | |||
import com.vaadin.client.renderers.ClickableRenderer.RendererClickEvent; | |||
import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; | |||
import com.vaadin.shared.ui.grid.renderers.RendererClickRpc; | |||
import com.vaadin.v7.client.renderers.ClickableRenderer; | |||
import com.vaadin.v7.client.renderers.ClickableRenderer.RendererClickEvent; | |||
import com.vaadin.v7.client.renderers.ClickableRenderer.RendererClickHandler; | |||
import elemental.json.JsonObject; | |||
@@ -50,34 +50,6 @@ import com.vaadin.client.ui.AbstractComponentConnector; | |||
import com.vaadin.client.ui.AbstractHasComponentsConnector; | |||
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; | |||
import com.vaadin.client.ui.SimpleManagedLayout; | |||
import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent; | |||
import com.vaadin.client.widget.escalator.events.RowHeightChangedHandler; | |||
import com.vaadin.client.widget.grid.CellReference; | |||
import com.vaadin.client.widget.grid.CellStyleGenerator; | |||
import com.vaadin.client.widget.grid.EditorHandler; | |||
import com.vaadin.client.widget.grid.EventCellReference; | |||
import com.vaadin.client.widget.grid.HeightAwareDetailsGenerator; | |||
import com.vaadin.client.widget.grid.RowReference; | |||
import com.vaadin.client.widget.grid.RowStyleGenerator; | |||
import com.vaadin.client.widget.grid.events.BodyClickHandler; | |||
import com.vaadin.client.widget.grid.events.BodyDoubleClickHandler; | |||
import com.vaadin.client.widget.grid.events.ColumnReorderEvent; | |||
import com.vaadin.client.widget.grid.events.ColumnReorderHandler; | |||
import com.vaadin.client.widget.grid.events.ColumnResizeEvent; | |||
import com.vaadin.client.widget.grid.events.ColumnResizeHandler; | |||
import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeEvent; | |||
import com.vaadin.client.widget.grid.events.ColumnVisibilityChangeHandler; | |||
import com.vaadin.client.widget.grid.events.GridClickEvent; | |||
import com.vaadin.client.widget.grid.events.GridDoubleClickEvent; | |||
import com.vaadin.client.widget.grid.sort.SortEvent; | |||
import com.vaadin.client.widget.grid.sort.SortHandler; | |||
import com.vaadin.client.widget.grid.sort.SortOrder; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.client.widgets.Grid.Column; | |||
import com.vaadin.client.widgets.Grid.FooterCell; | |||
import com.vaadin.client.widgets.Grid.FooterRow; | |||
import com.vaadin.client.widgets.Grid.HeaderCell; | |||
import com.vaadin.client.widgets.Grid.HeaderRow; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.data.sort.SortDirection; | |||
import com.vaadin.shared.ui.Connect; | |||
@@ -95,6 +67,34 @@ import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; | |||
import com.vaadin.shared.ui.grid.ScrollDestination; | |||
import com.vaadin.v7.client.connectors.RpcDataSourceConnector.DetailsListener; | |||
import com.vaadin.v7.client.connectors.RpcDataSourceConnector.RpcDataSource; | |||
import com.vaadin.v7.client.widget.escalator.events.RowHeightChangedEvent; | |||
import com.vaadin.v7.client.widget.escalator.events.RowHeightChangedHandler; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.CellStyleGenerator; | |||
import com.vaadin.v7.client.widget.grid.EditorHandler; | |||
import com.vaadin.v7.client.widget.grid.EventCellReference; | |||
import com.vaadin.v7.client.widget.grid.HeightAwareDetailsGenerator; | |||
import com.vaadin.v7.client.widget.grid.RowReference; | |||
import com.vaadin.v7.client.widget.grid.RowStyleGenerator; | |||
import com.vaadin.v7.client.widget.grid.events.BodyClickHandler; | |||
import com.vaadin.v7.client.widget.grid.events.BodyDoubleClickHandler; | |||
import com.vaadin.v7.client.widget.grid.events.ColumnReorderEvent; | |||
import com.vaadin.v7.client.widget.grid.events.ColumnReorderHandler; | |||
import com.vaadin.v7.client.widget.grid.events.ColumnResizeEvent; | |||
import com.vaadin.v7.client.widget.grid.events.ColumnResizeHandler; | |||
import com.vaadin.v7.client.widget.grid.events.ColumnVisibilityChangeEvent; | |||
import com.vaadin.v7.client.widget.grid.events.ColumnVisibilityChangeHandler; | |||
import com.vaadin.v7.client.widget.grid.events.GridClickEvent; | |||
import com.vaadin.v7.client.widget.grid.events.GridDoubleClickEvent; | |||
import com.vaadin.v7.client.widget.grid.sort.SortEvent; | |||
import com.vaadin.v7.client.widget.grid.sort.SortHandler; | |||
import com.vaadin.v7.client.widget.grid.sort.SortOrder; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.Column; | |||
import com.vaadin.v7.client.widgets.Grid.FooterCell; | |||
import com.vaadin.v7.client.widgets.Grid.FooterRow; | |||
import com.vaadin.v7.client.widgets.Grid.HeaderCell; | |||
import com.vaadin.v7.client.widgets.Grid.HeaderRow; | |||
import elemental.json.JsonObject; | |||
import elemental.json.JsonValue; |
@@ -18,10 +18,10 @@ package com.vaadin.v7.client.connectors; | |||
import com.google.web.bindery.event.shared.HandlerRegistration; | |||
import com.vaadin.client.communication.JsonDecoder; | |||
import com.vaadin.client.metadata.TypeDataStore; | |||
import com.vaadin.client.renderers.ClickableRenderer.RendererClickHandler; | |||
import com.vaadin.client.renderers.ImageRenderer; | |||
import com.vaadin.shared.communication.URLReference; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.renderers.ImageRenderer; | |||
import com.vaadin.v7.client.renderers.ClickableRenderer.RendererClickHandler; | |||
import elemental.json.JsonObject; | |||
import elemental.json.JsonValue; |
@@ -24,12 +24,12 @@ import com.google.gwt.dom.client.NativeEvent; | |||
import com.vaadin.client.JavaScriptConnectorHelper; | |||
import com.vaadin.client.Util; | |||
import com.vaadin.client.communication.HasJavaScriptConnectorHelper; | |||
import com.vaadin.client.renderers.ComplexRenderer; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widget.grid.CellReference; | |||
import com.vaadin.client.widget.grid.RendererCellReference; | |||
import com.vaadin.shared.JavaScriptExtensionState; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.renderers.ComplexRenderer; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
import com.vaadin.v7.ui.renderers.AbstractJavaScriptRenderer; | |||
import elemental.json.JsonObject; |
@@ -29,23 +29,23 @@ import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.annotations.OnStateChange; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.renderers.ComplexRenderer; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widget.grid.DataAvailableEvent; | |||
import com.vaadin.client.widget.grid.DataAvailableHandler; | |||
import com.vaadin.client.widget.grid.events.SelectAllEvent; | |||
import com.vaadin.client.widget.grid.events.SelectAllHandler; | |||
import com.vaadin.client.widget.grid.selection.MultiSelectionRenderer; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel.Multi; | |||
import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.client.widgets.Grid.HeaderCell; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.shared.ui.grid.Range; | |||
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc; | |||
import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState; | |||
import com.vaadin.v7.client.renderers.ComplexRenderer; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widget.grid.DataAvailableEvent; | |||
import com.vaadin.v7.client.widget.grid.DataAvailableHandler; | |||
import com.vaadin.v7.client.widget.grid.events.SelectAllEvent; | |||
import com.vaadin.v7.client.widget.grid.events.SelectAllHandler; | |||
import com.vaadin.v7.client.widget.grid.selection.MultiSelectionRenderer; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.v7.client.widget.grid.selection.SpaceSelectHandler; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel.Multi; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.HeaderCell; | |||
import com.vaadin.v7.ui.Grid.MultiSelectionModel; | |||
import elemental.json.JsonObject; |
@@ -16,9 +16,9 @@ | |||
package com.vaadin.v7.client.connectors; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelNone; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModelNone; | |||
import com.vaadin.v7.ui.Grid.NoSelectionModel; | |||
import elemental.json.JsonObject; |
@@ -15,8 +15,8 @@ | |||
*/ | |||
package com.vaadin.v7.client.connectors; | |||
import com.vaadin.client.renderers.ProgressBarRenderer; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.renderers.ProgressBarRenderer; | |||
/** | |||
* A connector for {@link ProgressBarRenderer}. |
@@ -18,15 +18,15 @@ package com.vaadin.v7.client.connectors; | |||
import com.vaadin.client.ServerConnector; | |||
import com.vaadin.client.annotations.OnStateChange; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widget.grid.selection.ClickSelectHandler; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel.Single; | |||
import com.vaadin.client.widget.grid.selection.SpaceSelectHandler; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.grid.GridState; | |||
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc; | |||
import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widget.grid.selection.ClickSelectHandler; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.v7.client.widget.grid.selection.SpaceSelectHandler; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel.Single; | |||
import com.vaadin.v7.ui.Grid.SingleSelectionModel; | |||
import elemental.json.JsonObject; |
@@ -15,8 +15,8 @@ | |||
*/ | |||
package com.vaadin.v7.client.connectors; | |||
import com.vaadin.client.renderers.TextRenderer; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.renderers.TextRenderer; | |||
/** | |||
* A connector for {@link TextRenderer}. |
@@ -15,9 +15,9 @@ | |||
*/ | |||
package com.vaadin.v7.client.connectors; | |||
import com.vaadin.client.renderers.Renderer; | |||
import com.vaadin.client.widget.grid.RendererCellReference; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* A connector for {@link UnsafeHtmlRenderer} |
@@ -0,0 +1,44 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.google.gwt.core.shared.GWT; | |||
import com.google.gwt.user.client.ui.Button; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* A Renderer that displays buttons with textual captions. The values of the | |||
* corresponding column are used as the captions. Click handlers can be added to | |||
* the renderer, invoked when any of the rendered buttons is clicked. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ButtonRenderer extends ClickableRenderer<String, Button> { | |||
@Override | |||
public Button createWidget() { | |||
Button b = GWT.create(Button.class); | |||
b.addClickHandler(this); | |||
b.setStylePrimaryName("v-nativebutton"); | |||
return b; | |||
} | |||
@Override | |||
public void render(RendererCellReference cell, String text, Button button) { | |||
button.setText(text); | |||
} | |||
} |
@@ -0,0 +1,234 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
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.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.google.gwt.event.dom.client.DomEvent; | |||
import com.google.gwt.event.dom.client.MouseEvent; | |||
import com.google.gwt.event.shared.EventHandler; | |||
import com.google.gwt.event.shared.HandlerManager; | |||
import com.google.gwt.user.client.ui.Composite; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.google.web.bindery.event.shared.HandlerRegistration; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.escalator.Cell; | |||
import com.vaadin.v7.client.widget.escalator.RowContainer; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.EventCellReference; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* An abstract superclass for renderers that render clickable widgets. Click | |||
* handlers can be added to a renderer to listen to click events emitted by all | |||
* widgets rendered by the renderer. | |||
* | |||
* @param <T> | |||
* the presentation (column) type | |||
* @param <W> | |||
* the widget type | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public abstract class ClickableRenderer<T, W extends Widget> | |||
extends WidgetRenderer<T, W> implements ClickHandler { | |||
/** | |||
* A handler for {@link RendererClickEvent renderer click events}. | |||
* | |||
* @param <R> | |||
* the row type of the containing Grid | |||
* | |||
* @see {@link ButtonRenderer#addClickHandler(RendererClickHandler)} | |||
*/ | |||
public interface RendererClickHandler<R> extends EventHandler { | |||
/** | |||
* Called when a rendered button is clicked. | |||
* | |||
* @param event | |||
* the event representing the click | |||
*/ | |||
void onClick(RendererClickEvent<R> event); | |||
} | |||
/** | |||
* An event fired when a widget rendered by a ClickableWidgetRenderer | |||
* subclass is clicked. | |||
* | |||
* @param <R> | |||
* the row type of the containing Grid | |||
*/ | |||
@SuppressWarnings("rawtypes") | |||
public static class RendererClickEvent<R> | |||
extends MouseEvent<RendererClickHandler> { | |||
@SuppressWarnings("unchecked") | |||
static final Type<RendererClickHandler> TYPE = new Type<RendererClickHandler>( | |||
BrowserEvents.CLICK, new RendererClickEvent()); | |||
private CellReference<R> cell; | |||
private R row; | |||
private RendererClickEvent() { | |||
} | |||
/** | |||
* Returns the cell of the clicked button. | |||
* | |||
* @return the cell | |||
*/ | |||
public CellReference<R> getCell() { | |||
return cell; | |||
} | |||
/** | |||
* Returns the data object corresponding to the row of the clicked | |||
* button. | |||
* | |||
* @return the row data object | |||
*/ | |||
public R getRow() { | |||
return row; | |||
} | |||
@Override | |||
public Type<RendererClickHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
protected void dispatch(RendererClickHandler handler) { | |||
EventTarget target = getNativeEvent().getEventTarget(); | |||
if (!Element.is(target)) { | |||
return; | |||
} | |||
Element e = Element.as(target); | |||
Grid<R> grid = (Grid<R>) findClosestParentGrid(e); | |||
cell = findCell(grid, e); | |||
row = cell.getRow(); | |||
handler.onClick(this); | |||
} | |||
/** | |||
* Returns the cell the given element belongs to. | |||
* | |||
* @param grid | |||
* the grid instance that is queried | |||
* @param e | |||
* a cell element or the descendant of one | |||
* @return the cell or null if the element is not a grid cell or a | |||
* descendant of one | |||
*/ | |||
private static <T> CellReference<T> findCell(Grid<T> grid, Element e) { | |||
RowContainer container = getEscalator(grid).findRowContainer(e); | |||
if (container == null) { | |||
return null; | |||
} | |||
Cell cell = container.getCell(e); | |||
EventCellReference<T> cellReference = new EventCellReference<T>( | |||
grid); | |||
// FIXME: Section is currently always body. Might be useful for the | |||
// future to have an actual check. | |||
cellReference.set(cell, Section.BODY); | |||
return cellReference; | |||
} | |||
private native static Escalator getEscalator(Grid<?> grid) | |||
/*-{ | |||
return grid.@com.vaadin.v7.client.widgets.Grid::escalator; | |||
}-*/; | |||
/** | |||
* Returns the Grid instance containing the given element, if any. | |||
* <p> | |||
* <strong>Note:</strong> This method may not work reliably if the grid | |||
* in question is wrapped in a {@link Composite} <em>unless</em> the | |||
* element is inside another widget that is a child of the wrapped grid; | |||
* please refer to the note in | |||
* {@link WidgetUtil#findWidget(Element, Class) Util.findWidget} for | |||
* details. | |||
* | |||
* @param e | |||
* the element whose parent grid to find | |||
* @return the parent grid or null if none found. | |||
*/ | |||
private static Grid<?> findClosestParentGrid(Element e) { | |||
Widget w = WidgetUtil.findWidget(e, null); | |||
while (w != null && !(w instanceof Grid)) { | |||
w = w.getParent(); | |||
} | |||
return (Grid<?>) w; | |||
} | |||
} | |||
private HandlerManager handlerManager; | |||
/** | |||
* {@inheritDoc} | |||
* <p> | |||
* <em>Implementation note:</em> It is the implementing method's | |||
* responsibility to add {@code this} as a click handler of the returned | |||
* widget, or a widget nested therein, in order to make click events | |||
* propagate properly to handlers registered via | |||
* {@link #addClickHandler(RendererClickHandler) addClickHandler}. | |||
*/ | |||
@Override | |||
public abstract W createWidget(); | |||
/** | |||
* Adds a click handler to this button renderer. The handler is invoked | |||
* every time one of the widgets rendered by this renderer is clicked. | |||
* <p> | |||
* Note that the row type of the click handler must match the row type of | |||
* the containing Grid. | |||
* | |||
* @param handler | |||
* the click handler to be added | |||
*/ | |||
public HandlerRegistration addClickHandler( | |||
RendererClickHandler<?> handler) { | |||
if (handlerManager == null) { | |||
handlerManager = new HandlerManager(this); | |||
} | |||
return handlerManager.addHandler(RendererClickEvent.TYPE, handler); | |||
} | |||
@Override | |||
public void onClick(ClickEvent event) { | |||
/* | |||
* The handler manager is lazily instantiated so it's null iff | |||
* addClickHandler is never called. | |||
*/ | |||
if (handlerManager != null) { | |||
DomEvent.fireNativeEvent(event.getNativeEvent(), handlerManager); | |||
} | |||
} | |||
} |
@@ -0,0 +1,157 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
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.v7.client.widget.escalator.Cell; | |||
import com.vaadin.v7.client.widget.escalator.FlyweightCell; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* Base class for renderers that needs initialization and destruction logic | |||
* (override {@link #init(FlyweightCell)} and {@link #destroy(FlyweightCell) } | |||
* and event handling (see {@link #onBrowserEvent(Cell, NativeEvent)}, | |||
* {@link #getConsumedEvents()} and {@link #onActivate()}. | |||
* | |||
* <p> | |||
* Also provides a helper method for hiding the cell contents by overriding | |||
* {@link #setContentVisible(FlyweightCell, boolean)} | |||
* | |||
* @since 7.4.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public abstract class ComplexRenderer<T> implements Renderer<T> { | |||
/** | |||
* Called at initialization stage. Perform any initialization here e.g. | |||
* attach handlers, attach widgets etc. | |||
* | |||
* @param cell | |||
* The cell. Note that the cell is not to be stored outside of | |||
* the method as the cell instance will change. See | |||
* {@link FlyweightCell} | |||
*/ | |||
public abstract void init(RendererCellReference cell); | |||
/** | |||
* Called after the cell is deemed to be destroyed and no longer used by the | |||
* Grid. Called after the cell element is detached from the DOM. | |||
* <p> | |||
* The row object in the cell reference will be <code>null</code> since the | |||
* row might no longer be present in the data source. | |||
* | |||
* @param cell | |||
* The cell. Note that the cell is not to be stored outside of | |||
* the method as the cell instance will change. See | |||
* {@link FlyweightCell} | |||
*/ | |||
public void destroy(RendererCellReference cell) { | |||
// Implement if needed | |||
} | |||
/** | |||
* Returns the events that the renderer should consume. These are also the | |||
* events that the Grid will pass to | |||
* {@link #onBrowserEvent(Cell, NativeEvent)} when they occur. | |||
* | |||
* @return a list of consumed events | |||
* | |||
* @see com.google.gwt.dom.client.BrowserEvents | |||
*/ | |||
public Collection<String> getConsumedEvents() { | |||
return Collections.emptyList(); | |||
} | |||
/** | |||
* Called whenever a registered event is triggered in the column the | |||
* renderer renders. | |||
* <p> | |||
* The events that triggers this needs to be returned by the | |||
* {@link #getConsumedEvents()} method. | |||
* <p> | |||
* Returns boolean telling if the event has been completely handled and | |||
* should not cause any other actions. | |||
* | |||
* @param cell | |||
* Object containing information about the cell the event was | |||
* triggered on. | |||
* | |||
* @param event | |||
* The original DOM event | |||
* @return true if event should not be handled by grid | |||
*/ | |||
public boolean onBrowserEvent(CellReference<?> cell, NativeEvent event) { | |||
return false; | |||
} | |||
/** | |||
* 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. | |||
* <p> | |||
* 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 hasData | |||
* Has the cell content been loaded from the data source | |||
* | |||
*/ | |||
public void setContentVisible(RendererCellReference 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); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Called when the cell is activated by pressing <code>enter</code>, double | |||
* clicking or performing a double tap on the cell. | |||
* | |||
* @param cell | |||
* the activated cell | |||
* @return <code>true</code> if event was handled and should not be | |||
* interpreted as a generic gesture by Grid. | |||
*/ | |||
public boolean onActivate(CellReference<?> cell) { | |||
return false; | |||
} | |||
/** | |||
* Called when the renderer is deemed to be destroyed and no longer used by | |||
* the Grid. | |||
*/ | |||
public void destroy() { | |||
// Implement if needed | |||
} | |||
} |
@@ -0,0 +1,108 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import java.util.Date; | |||
import com.google.gwt.i18n.client.TimeZone; | |||
import com.google.gwt.i18n.shared.DateTimeFormat; | |||
import com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* A renderer for rendering dates into cells | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class DateRenderer implements Renderer<Date> { | |||
private DateTimeFormat format; | |||
// Calendar is unavailable for GWT | |||
@SuppressWarnings("deprecation") | |||
private TimeZone timeZone = TimeZone | |||
.createTimeZone(new Date().getTimezoneOffset()); | |||
public DateRenderer() { | |||
this(PredefinedFormat.DATE_TIME_SHORT); | |||
} | |||
public DateRenderer(PredefinedFormat format) { | |||
this(DateTimeFormat.getFormat(format)); | |||
} | |||
public DateRenderer(DateTimeFormat format) { | |||
setFormat(format); | |||
} | |||
@Override | |||
public void render(RendererCellReference cell, Date date) { | |||
String dateStr = format.format(date, timeZone); | |||
cell.getElement().setInnerText(dateStr); | |||
} | |||
/** | |||
* Gets the format of how the date is formatted. | |||
* | |||
* @return the format | |||
* @see <a href= | |||
* "http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/shared/DateTimeFormat.html">GWT | |||
* documentation on DateTimeFormat</a> | |||
*/ | |||
public DateTimeFormat getFormat() { | |||
return format; | |||
} | |||
/** | |||
* Sets the format used for formatting the dates. | |||
* | |||
* @param format | |||
* the format to set | |||
* @see <a href= | |||
* "http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/shared/DateTimeFormat.html">GWT | |||
* documentation on DateTimeFormat</a> | |||
*/ | |||
public void setFormat(DateTimeFormat format) { | |||
if (format == null) { | |||
throw new IllegalArgumentException("Format should not be null"); | |||
} | |||
this.format = format; | |||
} | |||
/** | |||
* Returns the time zone of the date. | |||
* | |||
* @return the time zone | |||
*/ | |||
public TimeZone getTimeZone() { | |||
return timeZone; | |||
} | |||
/** | |||
* Sets the time zone of the the date. By default uses the time zone of the | |||
* browser. | |||
* | |||
* @param timeZone | |||
* the timeZone to set | |||
*/ | |||
public void setTimeZone(TimeZone timeZone) { | |||
if (timeZone == null) { | |||
throw new IllegalArgumentException("Timezone should not be null"); | |||
} | |||
this.timeZone = timeZone; | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.google.gwt.safehtml.shared.SafeHtml; | |||
import com.google.gwt.safehtml.shared.SafeHtmlUtils; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* Renders a string as HTML into a cell. | |||
* <p> | |||
* The html string is rendered as is without any escaping. It is up to the | |||
* developer to ensure that the html string honors the {@link SafeHtml} | |||
* contract. For more information see | |||
* {@link SafeHtmlUtils#fromSafeConstant(String)}. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @see SafeHtmlUtils#fromSafeConstant(String) | |||
*/ | |||
public class HtmlRenderer implements Renderer<String> { | |||
@Override | |||
public void render(RendererCellReference cell, String htmlString) { | |||
cell.getElement() | |||
.setInnerSafeHtml(SafeHtmlUtils.fromSafeConstant(htmlString)); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.user.client.ui.Image; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* A renderer that renders an image into a cell. Click handlers can be added to | |||
* the renderer, invoked every time any of the images rendered by that rendered | |||
* is clicked. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ImageRenderer extends ClickableRenderer<String, Image> { | |||
public static final String TRANSPARENT_GIF_1PX = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="; | |||
@Override | |||
public Image createWidget() { | |||
Image image = GWT.create(Image.class); | |||
image.addClickHandler(this); | |||
return image; | |||
} | |||
@Override | |||
public void render(RendererCellReference cell, String url, Image image) { | |||
if (url == null) { | |||
image.setUrl(TRANSPARENT_GIF_1PX); | |||
} else { | |||
image.setUrl(url); | |||
} | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.google.gwt.i18n.client.NumberFormat; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* Renders a number into a cell using a specific {@link NumberFormat}. By | |||
* default uses the default number format returned by | |||
* {@link NumberFormat#getDecimalFormat()}. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* The number type to render. | |||
*/ | |||
public class NumberRenderer implements Renderer<Number> { | |||
private NumberFormat format; | |||
public NumberRenderer() { | |||
this(NumberFormat.getDecimalFormat()); | |||
} | |||
public NumberRenderer(NumberFormat format) { | |||
setFormat(format); | |||
} | |||
/** | |||
* Gets the number format that the number should be formatted in. | |||
* | |||
* @return the number format used to render the number | |||
*/ | |||
public NumberFormat getFormat() { | |||
return format; | |||
} | |||
/** | |||
* Sets the number format to use for formatting the number. | |||
* | |||
* @param format | |||
* the format to use | |||
* @throws IllegalArgumentException | |||
* when the format is null | |||
*/ | |||
public void setFormat(NumberFormat format) throws IllegalArgumentException { | |||
if (format == null) { | |||
throw new IllegalArgumentException("Format cannot be null"); | |||
} | |||
this.format = format; | |||
} | |||
@Override | |||
public void render(RendererCellReference cell, Number number) { | |||
cell.getElement().setInnerText(format.format(number)); | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.google.gwt.core.shared.GWT; | |||
import com.vaadin.client.ui.VProgressBar; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* A Renderer that represents a double value as a graphical progress bar. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ProgressBarRenderer extends WidgetRenderer<Double, VProgressBar> { | |||
@Override | |||
public VProgressBar createWidget() { | |||
VProgressBar progressBar = GWT.create(VProgressBar.class); | |||
progressBar.addStyleDependentName("static"); | |||
return progressBar; | |||
} | |||
@Override | |||
public void render(RendererCellReference cell, Double data, | |||
VProgressBar progressBar) { | |||
if (data == null) { | |||
progressBar.setEnabled(false); | |||
} else { | |||
progressBar.setEnabled(true); | |||
progressBar.setState(data.floatValue()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.vaadin.v7.client.widget.escalator.Cell; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Renderer for rending a value <T> into cell. | |||
* <p> | |||
* You can add a renderer to any column by overring the | |||
* {@link GridColumn#getRenderer()} method and returning your own renderer. You | |||
* can retrieve the cell element using {@link Cell#getElement()}. | |||
* | |||
* @param <T> | |||
* The column type | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface Renderer<T> { | |||
/** | |||
* Called whenever the {@link Grid} updates a cell. | |||
* <p> | |||
* For optimal performance, work done in this method should be kept to a | |||
* minimum since it will be called continuously while the user is scrolling. | |||
* It is recommended to set up the cell's DOM structure in | |||
* {@link ComplexRenderer#init(RendererCellReference)} and only make | |||
* incremental updates based on cell data in this method. | |||
* | |||
* @param cell | |||
* The cell. Note that the cell is a flyweight and should not be | |||
* stored outside of the method as it will change. | |||
* | |||
* @param data | |||
* The column data object | |||
*/ | |||
void render(RendererCellReference cell, T data); | |||
} |
@@ -0,0 +1,32 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* Renderer that renders text into a cell. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class TextRenderer implements Renderer<String> { | |||
@Override | |||
public void render(RendererCellReference cell, String text) { | |||
cell.getElement().setInnerText(text); | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.renderers; | |||
import com.google.gwt.dom.client.TableCellElement; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
/** | |||
* A renderer for rendering widgets into cells. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* the row data type | |||
* @param <W> | |||
* the Widget type | |||
*/ | |||
public abstract class WidgetRenderer<T, W extends Widget> | |||
extends ComplexRenderer<T> { | |||
@Override | |||
public void init(RendererCellReference cell) { | |||
// Implement if needed | |||
} | |||
/** | |||
* Creates a widget to attach to a cell. The widgets will be attached to the | |||
* cell after the cell element has been attached to DOM. | |||
* | |||
* @return widget to attach to a cell. All returned instances should be new | |||
* widget instances without a parent. | |||
*/ | |||
public abstract W createWidget(); | |||
@Override | |||
public void render(RendererCellReference cell, T data) { | |||
W w = getWidget(cell.getElement()); | |||
assert w != null : "Widget not found in cell (" + cell.getColumn() + "," | |||
+ cell.getRow() + ")"; | |||
render(cell, data, w); | |||
} | |||
/** | |||
* Renders a cell with a widget. This provides a way to update any | |||
* information in the widget that is cell specific. Do not detach the Widget | |||
* here, it will be done automatically by the Grid when the widget is no | |||
* longer needed. | |||
* <p> | |||
* For optimal performance, work done in this method should be kept to a | |||
* minimum since it will be called continuously while the user is scrolling. | |||
* The renderer can use {@link Widget#setLayoutData(Object)} to store cell | |||
* data that might be needed in e.g. event listeners. | |||
* | |||
* @param cell | |||
* The cell to render. Note that the cell is a flyweight and | |||
* should not be stored and used outside of this method as its | |||
* contents will change. | |||
* @param data | |||
* the data of the cell | |||
* @param widget | |||
* the widget embedded in the cell | |||
*/ | |||
public abstract void render(RendererCellReference cell, T data, W widget); | |||
/** | |||
* Returns the widget contained inside the given cell element. Cannot be | |||
* called for cells that do not contain a widget. | |||
* | |||
* @param e | |||
* the element inside which to find a widget | |||
* @return the widget inside the element | |||
*/ | |||
protected W getWidget(TableCellElement e) { | |||
W w = getWidget(e, null); | |||
assert w != null : "Widget not found inside cell"; | |||
return w; | |||
} | |||
/** | |||
* Returns the widget contained inside the given cell element, or null if it | |||
* is not an instance of the given class. Cannot be called for cells that do | |||
* not contain a widget. | |||
* | |||
* @param e | |||
* the element inside to find a widget | |||
* @param klass | |||
* the type of the widget to find | |||
* @return the widget inside the element, or null if its type does not match | |||
*/ | |||
protected static <W extends Widget> W getWidget(TableCellElement e, | |||
Class<W> klass) { | |||
W w = WidgetUtil.findWidget(e.getFirstChildElement(), klass); | |||
assert w == null || w.getElement() == e | |||
.getFirstChildElement() : "Widget not found inside cell"; | |||
return w; | |||
} | |||
} |
@@ -19,7 +19,7 @@ import com.google.gwt.core.client.JavaScriptObject; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.widgets.Escalator; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* A mousewheel handling class to get around the limits of |
@@ -0,0 +1,85 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.dom.client.TableCellElement; | |||
/** | |||
* Describes a cell | |||
* <p> | |||
* It's a representation of the element in a grid cell, and its row and column | |||
* indices. | |||
* <p> | |||
* Unlike the {@link FlyweightRow}, an instance of {@link Cell} can be stored in | |||
* a field. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class Cell { | |||
private final int row; | |||
private final int column; | |||
private final TableCellElement element; | |||
/** | |||
* Constructs a new {@link Cell}. | |||
* | |||
* @param row | |||
* The index of the row | |||
* @param column | |||
* The index of the column | |||
* @param element | |||
* The cell element | |||
*/ | |||
public Cell(int row, int column, TableCellElement element) { | |||
super(); | |||
this.row = row; | |||
this.column = column; | |||
this.element = element; | |||
} | |||
/** | |||
* Returns the index of the row the cell resides in. | |||
* | |||
* @return the row index | |||
* | |||
*/ | |||
public int getRow() { | |||
return row; | |||
} | |||
/** | |||
* Returns the index of the column the cell resides in. | |||
* | |||
* @return the column index | |||
*/ | |||
public int getColumn() { | |||
return column; | |||
} | |||
/** | |||
* Returns the element of the cell. | |||
* | |||
* @return the cell element | |||
*/ | |||
public TableCellElement getElement() { | |||
return element; | |||
} | |||
} |
@@ -0,0 +1,198 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import java.util.Map; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* A representation of the columns in an instance of {@link Escalator}. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @see Escalator#getColumnConfiguration() | |||
*/ | |||
public interface ColumnConfiguration { | |||
/** | |||
* Removes columns at certain indices. | |||
* <p> | |||
* If any of the removed columns were frozen, the number of frozen columns | |||
* will be reduced by the number of the removed columns that were frozen. | |||
* <p> | |||
* <em>Note:</em> This method simply removes the given columns, and does not | |||
* do much of anything else. Especially if you have column spans, you | |||
* probably need to run {@link #refreshColumns(int, int)} or | |||
* {@link RowContainer#refreshRows(int, int)} | |||
* | |||
* @param index | |||
* the index of the first column to be removed | |||
* @param numberOfColumns | |||
* the number of rows to remove, starting from {@code index} | |||
* @throws IndexOutOfBoundsException | |||
* if the entire range of removed columns is not currently | |||
* present in the escalator | |||
* @throws IllegalArgumentException | |||
* if <code>numberOfColumns</code> is less than 1. | |||
*/ | |||
public void removeColumns(int index, int numberOfColumns) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* Adds columns at a certain index. | |||
* <p> | |||
* The new columns will be inserted between the column at the index, and the | |||
* column before (an index of 0 means that the columns are inserted at the | |||
* beginning). Therefore, the columns at the index and afterwards will be | |||
* moved to the right. | |||
* <p> | |||
* The contents of the inserted columns will be queried from the respective | |||
* cell renderers in the header, body and footer. | |||
* <p> | |||
* If there are frozen columns and the first added column is to the left of | |||
* the last frozen column, the number of frozen columns will be increased by | |||
* the number of inserted columns. | |||
* <p> | |||
* <em>Note:</em> Only the contents of the inserted columns will be | |||
* rendered. If inserting new columns affects the contents of existing | |||
* columns (e.g. you have column spans), | |||
* {@link RowContainer#refreshRows(int, int)} or | |||
* {@link #refreshColumns(int, int)} needs to be called as appropriate. | |||
* | |||
* @param index | |||
* the index of the column before which new columns are inserted, | |||
* or {@link #getColumnCount()} to add new columns at the end | |||
* @param numberOfColumns | |||
* the number of columns to insert after the <code>index</code> | |||
* @throws IndexOutOfBoundsException | |||
* if <code>index</code> is not an integer in the range | |||
* <code>[0..{@link #getColumnCount()}]</code> | |||
* @throws IllegalArgumentException | |||
* if {@code numberOfColumns} is less than 1. | |||
*/ | |||
public void insertColumns(int index, int numberOfColumns) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* Returns the number of columns in the escalator. | |||
* | |||
* @return the number of columns in the escalator | |||
*/ | |||
public int getColumnCount(); | |||
/** | |||
* Sets the number of leftmost columns that are not affected by horizontal | |||
* scrolling. | |||
* | |||
* @param count | |||
* the number of columns to freeze | |||
* | |||
* @throws IllegalArgumentException | |||
* if the column count is < 0 or > the number of columns | |||
* | |||
*/ | |||
public void setFrozenColumnCount(int count) throws IllegalArgumentException; | |||
/** | |||
* Get the number of leftmost columns that are not affected by horizontal | |||
* scrolling. | |||
* | |||
* @return the number of frozen columns | |||
*/ | |||
public int getFrozenColumnCount(); | |||
/** | |||
* Sets (or unsets) an explicit width for a column. | |||
* | |||
* @param index | |||
* the index of the column for which to set a width | |||
* @param px | |||
* the number of pixels the indicated column should be, or a | |||
* negative number to let the escalator decide | |||
* @throws IllegalArgumentException | |||
* if <code>index</code> is not a valid column index | |||
*/ | |||
public void setColumnWidth(int index, double px) | |||
throws IllegalArgumentException; | |||
/** | |||
* Returns the user-defined width of a column. | |||
* | |||
* @param index | |||
* the index of the column for which to retrieve the width | |||
* @return the column's width in pixels, or a negative number if the width | |||
* is implicitly decided by the escalator | |||
* @throws IllegalArgumentException | |||
* if <code>index</code> is not a valid column index | |||
*/ | |||
public double getColumnWidth(int index) throws IllegalArgumentException; | |||
/** | |||
* Sets widths for a set of columns. | |||
* | |||
* @param indexWidthMap | |||
* a map from column index to its respective width to be set. If | |||
* the given width for a column index is negative, the column is | |||
* resized-to-fit. | |||
* @throws IllegalArgumentException | |||
* if {@code indexWidthMap} is {@code null} | |||
* @throws IllegalArgumentException | |||
* if any column index in {@code indexWidthMap} is invalid | |||
* @throws NullPointerException | |||
* If any value in the map is <code>null</code> | |||
*/ | |||
public void setColumnWidths(Map<Integer, Double> indexWidthMap) | |||
throws IllegalArgumentException; | |||
/** | |||
* Returns the actual width of a column. | |||
* | |||
* @param index | |||
* the index of the column for which to retrieve the width | |||
* @return the column's actual width in pixels | |||
* @throws IllegalArgumentException | |||
* if <code>index</code> is not a valid column index | |||
*/ | |||
public double getColumnWidthActual(int index) | |||
throws IllegalArgumentException; | |||
/** | |||
* Refreshes a range of rows in the current row containers in each Escalator | |||
* section. | |||
* <p> | |||
* The data for the refreshed columns is queried from the current cell | |||
* renderer. | |||
* | |||
* @param index | |||
* the index of the first row that will be updated | |||
* @param numberOfRows | |||
* the number of rows to update, starting from the index | |||
* @throws IndexOutOfBoundsException | |||
* if any integer number in the range | |||
* <code>[index..(index+numberOfColumns)]</code> is not an | |||
* existing column index. | |||
* @throws IllegalArgumentException | |||
* if {@code numberOfColumns} is less than 1. | |||
* @see RowContainer#setEscalatorUpdater(EscalatorUpdater) | |||
* @see Escalator#getHeader() | |||
* @see Escalator#getBody() | |||
* @see Escalator#getFooter() | |||
*/ | |||
public void refreshColumns(int index, int numberOfColumns) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
} |
@@ -0,0 +1,157 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* An interface that allows client code to define how a certain row in Escalator | |||
* will be displayed. The contents of an escalator's header, body and footer are | |||
* rendered by their respective updaters. | |||
* <p> | |||
* The updater is responsible for internally handling all remote communication, | |||
* should the displayed data need to be fetched remotely. | |||
* <p> | |||
* This has a similar function to {@link Grid Grid's} {@link Renderer Renderers} | |||
* , although they operate on different abstraction levels. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @see RowContainer#setEscalatorUpdater(EscalatorUpdater) | |||
* @see Escalator#getHeader() | |||
* @see Escalator#getBody() | |||
* @see Escalator#getFooter() | |||
* @see Renderer | |||
*/ | |||
public interface EscalatorUpdater { | |||
/** | |||
* An {@link EscalatorUpdater} that doesn't render anything. | |||
*/ | |||
public static final EscalatorUpdater NULL = new EscalatorUpdater() { | |||
@Override | |||
public void update(final Row row, | |||
final Iterable<FlyweightCell> cellsToUpdate) { | |||
// NOOP | |||
} | |||
@Override | |||
public void preAttach(final Row row, | |||
final Iterable<FlyweightCell> cellsToAttach) { | |||
// NOOP | |||
} | |||
@Override | |||
public void postAttach(final Row row, | |||
final Iterable<FlyweightCell> attachedCells) { | |||
// NOOP | |||
} | |||
@Override | |||
public void preDetach(final Row row, | |||
final Iterable<FlyweightCell> cellsToDetach) { | |||
// NOOP | |||
} | |||
@Override | |||
public void postDetach(final Row row, | |||
final Iterable<FlyweightCell> detachedCells) { | |||
// NOOP | |||
} | |||
}; | |||
/** | |||
* Renders a row contained in a row container. | |||
* <p> | |||
* <em>Note:</em> If rendering of cells is deferred (e.g. because | |||
* asynchronous data retrieval), this method is responsible for explicitly | |||
* displaying some placeholder data (empty content is valid). Because the | |||
* cells (and rows) in an escalator are recycled, failing to reset a cell's | |||
* presentation will lead to wrong data being displayed in the escalator. | |||
* <p> | |||
* For performance reasons, the escalator will never autonomously clear any | |||
* data in a cell. | |||
* | |||
* @param row | |||
* Information about the row that is being updated. | |||
* <em>Note:</em> You should not store nor reuse this reference. | |||
* @param cellsToUpdate | |||
* A collection of cells that need to be updated. <em>Note:</em> | |||
* You should neither store nor reuse the reference to the | |||
* iterable, nor to the individual cells. | |||
*/ | |||
public void update(Row row, Iterable<FlyweightCell> cellsToUpdate); | |||
/** | |||
* Called before attaching new cells to the escalator. | |||
* | |||
* @param row | |||
* Information about the row to which the cells will be added. | |||
* <em>Note:</em> You should not store nor reuse this reference. | |||
* @param cellsToAttach | |||
* A collection of cells that are about to be attached. | |||
* <em>Note:</em> You should neither store nor reuse the | |||
* reference to the iterable, nor to the individual cells. | |||
* | |||
*/ | |||
public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach); | |||
/** | |||
* Called after attaching new cells to the escalator. | |||
* | |||
* @param row | |||
* Information about the row to which the cells were added. | |||
* <em>Note:</em> You should not store nor reuse this reference. | |||
* @param attachedCells | |||
* A collection of cells that were attached. <em>Note:</em> You | |||
* should neither store nor reuse the reference to the iterable, | |||
* nor to the individual cells. | |||
* | |||
*/ | |||
public void postAttach(Row row, Iterable<FlyweightCell> attachedCells); | |||
/** | |||
* Called before detaching cells from the escalator. | |||
* | |||
* @param row | |||
* Information about the row from which the cells will be | |||
* removed. <em>Note:</em> You should not store nor reuse this | |||
* reference. | |||
* @param cellsToAttach | |||
* A collection of cells that are about to be detached. | |||
* <em>Note:</em> You should neither store nor reuse the | |||
* reference to the iterable, nor to the individual cells. | |||
* | |||
*/ | |||
public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach); | |||
/** | |||
* Called after detaching cells from the escalator. | |||
* | |||
* @param row | |||
* Information about the row from which the cells were removed. | |||
* <em>Note:</em> You should not store nor reuse this reference. | |||
* @param attachedCells | |||
* A collection of cells that were detached. <em>Note:</em> You | |||
* should neither store nor reuse the reference to the iterable, | |||
* nor to the individual cells. | |||
* | |||
*/ | |||
public void postDetach(Row row, Iterable<FlyweightCell> detachedCells); | |||
} |
@@ -0,0 +1,201 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import java.util.List; | |||
import com.google.gwt.dom.client.Style.Display; | |||
import com.google.gwt.dom.client.Style.Unit; | |||
import com.google.gwt.dom.client.TableCellElement; | |||
import com.vaadin.v7.client.widget.escalator.FlyweightRow.CellIterator; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* A {@link FlyweightCell} represents a cell in the {@link Grid} or | |||
* {@link Escalator} at a certain point in time. | |||
* | |||
* <p> | |||
* Since the {@link FlyweightCell} follows the <code>Flyweight</code>-pattern | |||
* any instance of this object is subject to change without the user knowing it | |||
* and so should not be stored anywhere outside of the method providing these | |||
* instances. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class FlyweightCell { | |||
public static final String COLSPAN_ATTR = "colSpan"; | |||
private final int column; | |||
private final FlyweightRow row; | |||
private TableCellElement element = null; | |||
private CellIterator currentIterator = null; | |||
public FlyweightCell(final FlyweightRow row, final int column) { | |||
this.row = row; | |||
this.column = column; | |||
} | |||
/** | |||
* Returns the row index of the cell | |||
*/ | |||
public int getRow() { | |||
assertSetup(); | |||
return row.getRow(); | |||
} | |||
/** | |||
* Returns the column index of the cell | |||
*/ | |||
public int getColumn() { | |||
assertSetup(); | |||
return column; | |||
} | |||
/** | |||
* Returns the element of the cell. Can be either a <code>TD</code> element | |||
* or a <code>TH</code> element. | |||
*/ | |||
public TableCellElement getElement() { | |||
assertSetup(); | |||
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 <code>TD</code> or | |||
* a <code>TH</code>. It is the caller's responsibility to actually insert | |||
* the given element to the document when needed. | |||
* | |||
* @param element | |||
* the element corresponding to this cell, cannot be null | |||
*/ | |||
public void setElement(TableCellElement element) { | |||
assert element != null; | |||
assertSetup(); | |||
this.element = element; | |||
} | |||
void setup(final CellIterator iterator) { | |||
currentIterator = iterator; | |||
if (iterator.areCellsAttached()) { | |||
final TableCellElement e = row.getElement().getCells() | |||
.getItem(column); | |||
assert e != null : "Cell " + column + " for logical row " | |||
+ row.getRow() + " doesn't exist in the DOM!"; | |||
e.setPropertyInt(COLSPAN_ATTR, 1); | |||
if (row.getColumnWidth(column) >= 0) { | |||
e.getStyle().setWidth(row.getColumnWidth(column), Unit.PX); | |||
} | |||
e.getStyle().clearDisplay(); | |||
setElement(e); | |||
} | |||
} | |||
/** | |||
* Tear down the state of the Cell. | |||
* <p> | |||
* This is an internal check method, to prevent retrieving uninitialized | |||
* data by calling {@link #getRow()}, {@link #getColumn()} or | |||
* {@link #getElement()} at an improper time. | |||
* <p> | |||
* This should only be used with asserts (" | |||
* <code>assert flyweightCell.teardown()</code> ") so that the code is never | |||
* run when asserts aren't enabled. | |||
* | |||
* @return always <code>true</code> | |||
* @see FlyweightRow#teardown() | |||
*/ | |||
boolean teardown() { | |||
currentIterator = null; | |||
element = null; | |||
return true; | |||
} | |||
/** | |||
* Asserts that the flyweight cell has properly been set up before trying to | |||
* access any of its data. | |||
*/ | |||
private void assertSetup() { | |||
assert currentIterator != null : "FlyweightCell was not properly " | |||
+ "initialized. This is either a bug in Grid/Escalator " | |||
+ "or a Cell reference has been stored and reused " | |||
+ "inappropriately."; | |||
} | |||
public void setColSpan(final int numberOfCells) { | |||
if (numberOfCells < 1) { | |||
throw new IllegalArgumentException( | |||
"Number of cells should be more than 0"); | |||
} | |||
/*- | |||
* This will default to 1 if unset, as per DOM specifications: | |||
* http://www.w3.org/TR/html5/tabular-data.html#attributes-common-to-td-and-th-elements | |||
*/ | |||
final int prevColSpan = getElement().getPropertyInt(COLSPAN_ATTR); | |||
if (numberOfCells == 1 && prevColSpan == 1) { | |||
return; | |||
} | |||
getElement().setPropertyInt(COLSPAN_ATTR, numberOfCells); | |||
adjustCellWidthForSpan(numberOfCells); | |||
hideOrRevealAdjacentCellElements(numberOfCells, prevColSpan); | |||
currentIterator.setSkipNext(numberOfCells - 1); | |||
} | |||
private void adjustCellWidthForSpan(final int numberOfCells) { | |||
final int cellsToTheRight = currentIterator | |||
.rawPeekNext(numberOfCells - 1).size(); | |||
final double selfWidth = row.getColumnWidth(column); | |||
double widthsOfColumnsToTheRight = 0; | |||
for (int i = 0; i < cellsToTheRight; i++) { | |||
widthsOfColumnsToTheRight += row.getColumnWidth(column + i + 1); | |||
} | |||
getElement().getStyle().setWidth(selfWidth + widthsOfColumnsToTheRight, | |||
Unit.PX); | |||
} | |||
private void hideOrRevealAdjacentCellElements(final int numberOfCells, | |||
final int prevColSpan) { | |||
final int affectedCellsNumber = Math.max(prevColSpan, numberOfCells); | |||
final List<FlyweightCell> affectedCells = currentIterator | |||
.rawPeekNext(affectedCellsNumber - 1); | |||
if (prevColSpan < numberOfCells) { | |||
for (int i = 0; i < affectedCells.size(); i++) { | |||
affectedCells.get(prevColSpan + i - 1).getElement().getStyle() | |||
.setDisplay(Display.NONE); | |||
} | |||
} else if (prevColSpan > numberOfCells) { | |||
for (int i = 0; i < affectedCells.size(); i++) { | |||
affectedCells.get(numberOfCells + i - 1).getElement().getStyle() | |||
.clearDisplay(); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,298 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import com.google.gwt.dom.client.TableRowElement; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* An internal implementation of the {@link Row} interface. | |||
* <p> | |||
* There is only one instance per Escalator. This is designed to be re-used when | |||
* rendering rows. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @see Escalator.AbstractRowContainer#refreshRow(Node, int) | |||
*/ | |||
public class FlyweightRow implements Row { | |||
static class CellIterator implements Iterator<FlyweightCell> { | |||
/** A defensive copy of the cells in the current row. */ | |||
private final ArrayList<FlyweightCell> cells; | |||
private final boolean cellsAttached; | |||
private int cursor = 0; | |||
private int skipNext = 0; | |||
/** | |||
* Creates a new iterator of attached flyweight cells. A cell is | |||
* attached if it has a corresponding {@link FlyweightCell#getElement() | |||
* DOM element} attached to the row element. | |||
* | |||
* @param cells | |||
* the collection of cells to iterate | |||
*/ | |||
public static CellIterator attached( | |||
final Collection<FlyweightCell> cells) { | |||
return new CellIterator(cells, true); | |||
} | |||
/** | |||
* Creates a new iterator of unattached flyweight cells. A cell is | |||
* unattached if it does not have a corresponding | |||
* {@link FlyweightCell#getElement() DOM element} attached to the row | |||
* element. | |||
* | |||
* @param cells | |||
* the collection of cells to iterate | |||
*/ | |||
public static CellIterator unattached( | |||
final Collection<FlyweightCell> cells) { | |||
return new CellIterator(cells, false); | |||
} | |||
private CellIterator(final Collection<FlyweightCell> cells, | |||
final boolean attached) { | |||
this.cells = new ArrayList<FlyweightCell>(cells); | |||
cellsAttached = attached; | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return cursor + skipNext < cells.size(); | |||
} | |||
@Override | |||
public FlyweightCell next() { | |||
// if we needed to skip some cells since the last invocation. | |||
for (int i = 0; i < skipNext; i++) { | |||
cells.remove(cursor); | |||
} | |||
skipNext = 0; | |||
final FlyweightCell cell = cells.get(cursor++); | |||
cell.setup(this); | |||
return cell; | |||
} | |||
@Override | |||
public void remove() { | |||
throw new UnsupportedOperationException( | |||
"Cannot remove cells via iterator"); | |||
} | |||
/** | |||
* Sets the number of cells to skip when {@link #next()} is called the | |||
* next time. Cell hiding is also handled eagerly in this method. | |||
* | |||
* @param colspan | |||
* the number of cells to skip on next invocation of | |||
* {@link #next()} | |||
*/ | |||
public void setSkipNext(final int colspan) { | |||
assert colspan > 0 : "Number of cells didn't make sense: " | |||
+ colspan; | |||
skipNext = colspan; | |||
} | |||
/** | |||
* Gets the next <code>n</code> cells in the iterator, ignoring any | |||
* possibly spanned cells. | |||
* | |||
* @param n | |||
* the number of next cells to retrieve | |||
* @return A list of next <code>n</code> cells, or less if there aren't | |||
* enough cells to retrieve | |||
*/ | |||
public List<FlyweightCell> rawPeekNext(final int n) { | |||
final int from = Math.min(cursor, cells.size()); | |||
final int to = Math.min(cursor + n, cells.size()); | |||
List<FlyweightCell> nextCells = cells.subList(from, to); | |||
for (FlyweightCell cell : nextCells) { | |||
cell.setup(this); | |||
} | |||
return nextCells; | |||
} | |||
public boolean areCellsAttached() { | |||
return cellsAttached; | |||
} | |||
} | |||
private static final int BLANK = Integer.MIN_VALUE; | |||
private int row; | |||
private TableRowElement element; | |||
private double[] columnWidths = null; | |||
private final List<FlyweightCell> cells = new ArrayList<FlyweightCell>(); | |||
public void setup(final TableRowElement e, final int row, | |||
double[] columnWidths) { | |||
element = e; | |||
this.row = row; | |||
this.columnWidths = columnWidths; | |||
} | |||
/** | |||
* Tear down the state of the Row. | |||
* <p> | |||
* This is an internal check method, to prevent retrieving uninitialized | |||
* data by calling {@link #getRow()}, {@link #getElement()} or | |||
* {@link #getCells()} at an improper time. | |||
* <p> | |||
* This should only be used with asserts (" | |||
* <code>assert flyweightRow.teardown()</code> ") so that the code is never | |||
* run when asserts aren't enabled. | |||
* | |||
* @return always <code>true</code> | |||
*/ | |||
public boolean teardown() { | |||
element = null; | |||
row = BLANK; | |||
columnWidths = null; | |||
for (final FlyweightCell cell : cells) { | |||
assert cell.teardown(); | |||
} | |||
return true; | |||
} | |||
@Override | |||
public int getRow() { | |||
assertSetup(); | |||
return row; | |||
} | |||
@Override | |||
public TableRowElement getElement() { | |||
assertSetup(); | |||
return element; | |||
} | |||
public void addCells(final int index, final int numberOfColumns) { | |||
for (int i = 0; i < numberOfColumns; i++) { | |||
final int col = index + i; | |||
cells.add(col, new FlyweightCell(this, col)); | |||
} | |||
updateRestOfCells(index + numberOfColumns); | |||
} | |||
public void removeCells(final int index, final int numberOfColumns) { | |||
cells.subList(index, index + numberOfColumns).clear(); | |||
updateRestOfCells(index); | |||
} | |||
private void updateRestOfCells(final int startPos) { | |||
// update the column number for the cells to the right | |||
for (int col = startPos; col < cells.size(); col++) { | |||
cells.set(col, new FlyweightCell(this, col)); | |||
} | |||
} | |||
/** | |||
* Returns flyweight cells for the client code to render. The cells get | |||
* their associated {@link FlyweightCell#getElement() elements} from the row | |||
* element. | |||
* <p> | |||
* Precondition: each cell has a corresponding element in the row | |||
* | |||
* @return an iterable of flyweight cells | |||
* | |||
* @see #setup(Element, int, int[]) | |||
* @see #teardown() | |||
*/ | |||
public Iterable<FlyweightCell> getCells() { | |||
return getCells(0, cells.size()); | |||
} | |||
/** | |||
* Returns a subrange of flyweight cells for the client code to render. The | |||
* cells get their associated {@link FlyweightCell#getElement() elements} | |||
* from the row element. | |||
* <p> | |||
* Precondition: each cell has a corresponding element in the row | |||
* | |||
* @param offset | |||
* the index of the first cell to return | |||
* @param numberOfCells | |||
* the number of cells to return | |||
* @return an iterable of flyweight cells | |||
*/ | |||
public Iterable<FlyweightCell> getCells(final int offset, | |||
final int numberOfCells) { | |||
assertSetup(); | |||
assert offset >= 0 && offset + numberOfCells <= cells | |||
.size() : "Invalid range of cells"; | |||
return new Iterable<FlyweightCell>() { | |||
@Override | |||
public Iterator<FlyweightCell> iterator() { | |||
return CellIterator.attached( | |||
cells.subList(offset, offset + numberOfCells)); | |||
} | |||
}; | |||
} | |||
/** | |||
* Returns a subrange of unattached flyweight cells. Unattached cells do not | |||
* have {@link FlyweightCell#getElement() elements} associated. Note that | |||
* FlyweightRow does not keep track of whether cells in actuality have | |||
* corresponding DOM elements or not; it is the caller's responsibility to | |||
* invoke this method with correct parameters. | |||
* <p> | |||
* Precondition: the range [offset, offset + numberOfCells) must be valid | |||
* | |||
* @param offset | |||
* the index of the first cell to return | |||
* @param numberOfCells | |||
* the number of cells to return | |||
* @return an iterable of flyweight cells | |||
*/ | |||
public Iterable<FlyweightCell> getUnattachedCells(final int offset, | |||
final int numberOfCells) { | |||
assertSetup(); | |||
assert offset >= 0 && offset + numberOfCells <= cells | |||
.size() : "Invalid range of cells"; | |||
return new Iterable<FlyweightCell>() { | |||
@Override | |||
public Iterator<FlyweightCell> iterator() { | |||
return CellIterator.unattached( | |||
cells.subList(offset, offset + numberOfCells)); | |||
} | |||
}; | |||
} | |||
/** | |||
* Asserts that the flyweight row has properly been set up before trying to | |||
* access any of its data. | |||
*/ | |||
private void assertSetup() { | |||
assert element != null && row != BLANK | |||
&& columnWidths != null : "Flyweight row was not " | |||
+ "properly initialized. Make sure the setup-method is " | |||
+ "called before retrieving data. This is either a bug " | |||
+ "in Escalator, or the instance of the flyweight row " | |||
+ "has been stored and accessed."; | |||
} | |||
double getColumnWidth(int column) { | |||
assertSetup(); | |||
return columnWidths[column]; | |||
} | |||
} |
@@ -0,0 +1,118 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.Style.Unit; | |||
/** | |||
* A functional interface that can be used for positioning elements in the DOM. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface PositionFunction { | |||
/** | |||
* A position function using "transform: translate3d(x,y,z)" to position | |||
* elements in the DOM. | |||
*/ | |||
public static class Translate3DPosition implements PositionFunction { | |||
@Override | |||
public void set(Element e, double x, double y) { | |||
e.getStyle().setProperty("transform", | |||
"translate3d(" + x + "px, " + y + "px, 0)"); | |||
} | |||
@Override | |||
public void reset(Element e) { | |||
e.getStyle().clearProperty("transform"); | |||
} | |||
} | |||
/** | |||
* A position function using "transform: translate(x,y)" to position | |||
* elements in the DOM. | |||
*/ | |||
public static class TranslatePosition implements PositionFunction { | |||
@Override | |||
public void set(Element e, double x, double y) { | |||
e.getStyle().setProperty("transform", | |||
"translate(" + x + "px," + y + "px)"); | |||
} | |||
@Override | |||
public void reset(Element e) { | |||
e.getStyle().clearProperty("transform"); | |||
} | |||
} | |||
/** | |||
* A position function using "-webkit-transform: translate3d(x,y,z)" to | |||
* position elements in the DOM. | |||
*/ | |||
public static class WebkitTranslate3DPosition implements PositionFunction { | |||
@Override | |||
public void set(Element e, double x, double y) { | |||
e.getStyle().setProperty("webkitTransform", | |||
"translate3d(" + x + "px," + y + "px,0)"); | |||
} | |||
@Override | |||
public void reset(Element e) { | |||
e.getStyle().clearProperty("webkitTransform"); | |||
} | |||
} | |||
/** | |||
* A position function using "left: x" and "top: y" to position elements in | |||
* the DOM. | |||
*/ | |||
public static class AbsolutePosition implements PositionFunction { | |||
@Override | |||
public void set(Element e, double x, double y) { | |||
e.getStyle().setLeft(x, Unit.PX); | |||
e.getStyle().setTop(y, Unit.PX); | |||
} | |||
@Override | |||
public void reset(Element e) { | |||
e.getStyle().clearLeft(); | |||
e.getStyle().clearTop(); | |||
} | |||
} | |||
/** | |||
* Position an element in an (x,y) coordinate system in the DOM. | |||
* | |||
* @param e | |||
* the element to position. Never <code>null</code>. | |||
* @param x | |||
* the x coordinate, in pixels | |||
* @param y | |||
* the y coordinate, in pixels | |||
*/ | |||
void set(Element e, double x, double y); | |||
/** | |||
* Resets any previously applied positioning, clearing the used style | |||
* attributes. | |||
* | |||
* @param e | |||
* the element for which to reset the positioning | |||
*/ | |||
void reset(Element e); | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.dom.client.TableRowElement; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* A representation of a row in an {@link Escalator}. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface Row { | |||
/** | |||
* Gets the row index. | |||
* | |||
* @return the row index | |||
*/ | |||
public int getRow(); | |||
/** | |||
* Gets the root element for this row. | |||
* <p> | |||
* The {@link EscalatorUpdater} may update the class names of the element | |||
* and add inline styles, but may not modify the contained DOM structure. | |||
* <p> | |||
* If you wish to modify the cells within this row element, access them via | |||
* the <code>List<{@link Cell}></code> objects passed in to | |||
* {@code EscalatorUpdater.updateCells(Row, List)} | |||
* | |||
* @return the root element of the row | |||
*/ | |||
public TableRowElement getElement(); | |||
} |
@@ -0,0 +1,282 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.TableRowElement; | |||
import com.google.gwt.dom.client.TableSectionElement; | |||
import com.vaadin.v7.client.widgets.Escalator; | |||
/** | |||
* A representation of the rows in each of the sections (header, body and | |||
* footer) in an {@link com.vaadin.v7.client.widgets.Escalator}. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @see com.vaadin.v7.client.widgets.Escalator#getHeader() | |||
* @see com.vaadin.v7.client.widgets.Escalator#getBody() | |||
* @see com.vaadin.v7.client.widgets.Escalator#getFooter() | |||
* @see SpacerContainer | |||
*/ | |||
public interface RowContainer { | |||
/** | |||
* The row container for the body section in an | |||
* {@link com.vaadin.v7.client.widgets.Escalator}. | |||
* <p> | |||
* The body section can contain both rows and spacers. | |||
* | |||
* @since 7.5.0 | |||
* @author Vaadin Ltd | |||
* @see com.vaadin.v7.client.widgets.Escalator#getBody() | |||
*/ | |||
public interface BodyRowContainer extends RowContainer { | |||
/** | |||
* Marks a spacer and its height. | |||
* <p> | |||
* If a spacer is already registered with the given row index, that | |||
* spacer will be updated with the given height. | |||
* <p> | |||
* <em>Note:</em> The row index for a spacer will change if rows are | |||
* inserted or removed above the current position. Spacers will also be | |||
* removed alongside their associated rows | |||
* | |||
* @param rowIndex | |||
* the row index for the spacer to modify. The affected | |||
* spacer is underneath the given index. Use -1 to insert a | |||
* spacer before the first row | |||
* @param height | |||
* the pixel height of the spacer. If {@code height} is | |||
* negative, the affected spacer (if exists) will be removed | |||
* @throws IllegalArgumentException | |||
* if {@code rowIndex} is not a valid row index | |||
* @see #insertRows(int, int) | |||
* @see #removeRows(int, int) | |||
*/ | |||
void setSpacer(int rowIndex, double height) | |||
throws IllegalArgumentException; | |||
/** | |||
* Sets a new spacer updater. | |||
* <p> | |||
* Spacers that are currently visible will be updated, i.e. | |||
* {@link SpacerUpdater#destroy(Spacer) destroyed} with the previous | |||
* one, and {@link SpacerUpdater#init(Spacer) initialized} with the new | |||
* one. | |||
* | |||
* @param spacerUpdater | |||
* the new spacer updater | |||
* @throws IllegalArgumentException | |||
* if {@code spacerUpdater} is {@code null} | |||
*/ | |||
void setSpacerUpdater(SpacerUpdater spacerUpdater) | |||
throws IllegalArgumentException; | |||
/** | |||
* Gets the spacer updater currently in use. | |||
* <p> | |||
* {@link SpacerUpdater#NULL} is the default. | |||
* | |||
* @return the spacer updater currently in use. Never <code>null</code> | |||
*/ | |||
SpacerUpdater getSpacerUpdater(); | |||
/** | |||
* {@inheritDoc} | |||
* <p> | |||
* Any spacers underneath {@code index} will be offset and "pushed" | |||
* down. This also modifies the row index they are associated with. | |||
*/ | |||
@Override | |||
public void insertRows(int index, int numberOfRows) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* {@inheritDoc} | |||
* <p> | |||
* Any spacers underneath {@code index} will be offset and "pulled" up. | |||
* This also modifies the row index they are associated with. Any | |||
* spacers in the removed range will also be closed and removed. | |||
*/ | |||
@Override | |||
public void removeRows(int index, int numberOfRows) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
} | |||
/** | |||
* An arbitrary pixel height of a row, before any autodetection for the row | |||
* height has been made. | |||
*/ | |||
public static final double INITIAL_DEFAULT_ROW_HEIGHT = 20; | |||
/** | |||
* Returns the current {@link EscalatorUpdater} used to render cells. | |||
* | |||
* @return the current escalator updater | |||
*/ | |||
public EscalatorUpdater getEscalatorUpdater(); | |||
/** | |||
* Sets the {@link EscalatorUpdater} to use when displaying data in the | |||
* escalator. | |||
* | |||
* @param escalatorUpdater | |||
* the escalator updater to use to render cells. May not be | |||
* <code>null</code> | |||
* @throws IllegalArgumentException | |||
* if {@code cellRenderer} is <code>null</code> | |||
* @see EscalatorUpdater#NULL | |||
*/ | |||
public void setEscalatorUpdater(EscalatorUpdater escalatorUpdater) | |||
throws IllegalArgumentException; | |||
/** | |||
* Removes rows at a certain index in the current row container. | |||
* | |||
* @param index | |||
* the index of the first row to be removed | |||
* @param numberOfRows | |||
* the number of rows to remove, starting from the index | |||
* @throws IndexOutOfBoundsException | |||
* if any integer number in the range | |||
* <code>[index..(index+numberOfRows)]</code> is not an existing | |||
* row index | |||
* @throws IllegalArgumentException | |||
* if {@code numberOfRows} is less than 1. | |||
*/ | |||
public void removeRows(int index, int numberOfRows) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* Adds rows at a certain index in this row container. | |||
* <p> | |||
* The new rows will be inserted between the row at the index, and the row | |||
* before (an index of 0 means that the rows are inserted at the beginning). | |||
* Therefore, the rows currently at the index and afterwards will be moved | |||
* downwards. | |||
* <p> | |||
* The contents of the inserted rows will subsequently be queried from the | |||
* escalator updater. | |||
* <p> | |||
* <em>Note:</em> Only the contents of the inserted rows will be rendered. | |||
* If inserting new rows affects the contents of existing rows, | |||
* {@link #refreshRows(int, int)} needs to be called for those rows | |||
* separately. | |||
* | |||
* @param index | |||
* the index of the row before which new rows are inserted, or | |||
* {@link #getRowCount()} to add rows at the end | |||
* @param numberOfRows | |||
* the number of rows to insert after the <code>index</code> | |||
* @see #setEscalatorUpdater(EscalatorUpdater) | |||
* @throws IndexOutOfBoundsException | |||
* if <code>index</code> is not an integer in the range | |||
* <code>[0..{@link #getRowCount()}]</code> | |||
* @throws IllegalArgumentException | |||
* if {@code numberOfRows} is less than 1. | |||
*/ | |||
public void insertRows(int index, int numberOfRows) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* Refreshes a range of rows in the current row container. | |||
* <p> | |||
* The data for the refreshed rows is queried from the current cell | |||
* renderer. | |||
* | |||
* @param index | |||
* the index of the first row that will be updated | |||
* @param numberOfRows | |||
* the number of rows to update, starting from the index | |||
* @see #setEscalatorUpdater(EscalatorUpdater) | |||
* @throws IndexOutOfBoundsException | |||
* if any integer number in the range | |||
* <code>[index..(index+numberOfColumns)]</code> is not an | |||
* existing column index. | |||
* @throws IllegalArgumentException | |||
* if {@code numberOfRows} is less than 1. | |||
*/ | |||
public void refreshRows(int index, int numberOfRows) | |||
throws IndexOutOfBoundsException, IllegalArgumentException; | |||
/** | |||
* Gets the number of rows in the current row container. | |||
* | |||
* @return the number of rows in the current row container | |||
*/ | |||
public int getRowCount(); | |||
/** | |||
* The default height of the rows in this RowContainer. | |||
* | |||
* @param px | |||
* the default height in pixels of the rows in this RowContainer | |||
* @throws IllegalArgumentException | |||
* if <code>px < 1</code> | |||
* @see #getDefaultRowHeight() | |||
*/ | |||
public void setDefaultRowHeight(double px) throws IllegalArgumentException; | |||
/** | |||
* Returns the default height of the rows in this RowContainer. | |||
* <p> | |||
* This value will be equal to {@link #INITIAL_DEFAULT_ROW_HEIGHT} if the | |||
* {@link Escalator} has not yet had a chance to autodetect the row height, | |||
* or no explicit value has yet given via {@link #setDefaultRowHeight(int)} | |||
* | |||
* @return the default height of the rows in this RowContainer, in pixels | |||
* @see #setDefaultRowHeight(int) | |||
*/ | |||
public double getDefaultRowHeight(); | |||
/** | |||
* Returns the cell object which contains information about the cell the | |||
* element is in. | |||
* | |||
* @param element | |||
* The element to get the cell for. If element is not present in | |||
* row container then <code>null</code> is returned. | |||
* | |||
* @return the cell of the element, or <code>null</code> if element is not | |||
* present in the {@link RowContainer}. | |||
*/ | |||
public Cell getCell(Element element); | |||
/** | |||
* Gets the row element with given logical index. For lazy loaded containers | |||
* such as Escalators BodyRowContainer visibility should be checked before | |||
* calling this function. See {@link Escalator#getVisibleRowRange()}. | |||
* | |||
* @param index | |||
* the logical index of the element to retrieve | |||
* @return the element at position {@code index} | |||
* @throws IndexOutOfBoundsException | |||
* if {@code index} is not valid within container | |||
* @throws IllegalStateException | |||
* if {@code index} is currently not available in the DOM | |||
*/ | |||
public TableRowElement getRowElement(int index) | |||
throws IndexOutOfBoundsException, IllegalStateException; | |||
/** | |||
* Returns the root element of RowContainer | |||
* | |||
* @return RowContainer root element | |||
*/ | |||
public TableSectionElement getElement(); | |||
} |
@@ -0,0 +1,99 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.shared.ui.grid.Range; | |||
/** | |||
* Event fired when the range of visible rows changes e.g. because of scrolling. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class RowVisibilityChangeEvent | |||
extends GwtEvent<RowVisibilityChangeHandler> { | |||
/** | |||
* The type of this event. | |||
*/ | |||
public static final Type<RowVisibilityChangeHandler> TYPE = new Type<RowVisibilityChangeHandler>(); | |||
private final Range visibleRows; | |||
/** | |||
* Creates a new row visibility change event | |||
* | |||
* @param firstVisibleRow | |||
* the index of the first visible row | |||
* @param visibleRowCount | |||
* the number of visible rows | |||
*/ | |||
public RowVisibilityChangeEvent(int firstVisibleRow, int visibleRowCount) { | |||
visibleRows = Range.withLength(firstVisibleRow, visibleRowCount); | |||
} | |||
/** | |||
* Gets the index of the first row that is at least partially visible. | |||
* | |||
* @return the index of the first visible row | |||
*/ | |||
public int getFirstVisibleRow() { | |||
return visibleRows.getStart(); | |||
} | |||
/** | |||
* Gets the number of at least partially visible rows. | |||
* | |||
* @return the number of visible rows | |||
*/ | |||
public int getVisibleRowCount() { | |||
return visibleRows.length(); | |||
} | |||
/** | |||
* Gets the range of visible rows. | |||
* | |||
* @since 7.6 | |||
* @return the visible rows | |||
*/ | |||
public Range getVisibleRowRange() { | |||
return visibleRows; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.google.gwt.event.shared.GwtEvent#getAssociatedType() | |||
*/ | |||
@Override | |||
public Type<RowVisibilityChangeHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.google.gwt.event.shared.GwtEvent#dispatch(com.google.gwt.event.shared | |||
* .EventHandler) | |||
*/ | |||
@Override | |||
protected void dispatch(RowVisibilityChangeHandler handler) { | |||
handler.onRowVisibilityChange(this); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
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 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface RowVisibilityChangeHandler extends EventHandler { | |||
/** | |||
* Called when the range of visible rows changes e.g. because of scrolling. | |||
* | |||
* @param event | |||
* the row visibility change event describing the change | |||
*/ | |||
void onRowVisibilityChange(RowVisibilityChangeEvent event); | |||
} |
@@ -0,0 +1,869 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.Style.Display; | |||
import com.google.gwt.dom.client.Style.Overflow; | |||
import com.google.gwt.dom.client.Style.Unit; | |||
import com.google.gwt.dom.client.Style.Visibility; | |||
import com.google.gwt.event.shared.EventHandler; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.google.gwt.event.shared.HandlerManager; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.EventListener; | |||
import com.google.gwt.user.client.Timer; | |||
import com.vaadin.client.DeferredWorker; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.v7.client.widget.grid.events.ScrollEvent; | |||
import com.vaadin.v7.client.widget.grid.events.ScrollHandler; | |||
/** | |||
* An element-like bundle representing a configurable and visual scrollbar in | |||
* one axis. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
* @see VerticalScrollbarBundle | |||
* @see HorizontalScrollbarBundle | |||
*/ | |||
public abstract class ScrollbarBundle implements DeferredWorker { | |||
private class ScrollEventFirer { | |||
private final ScheduledCommand fireEventCommand = new ScheduledCommand() { | |||
@Override | |||
public void execute() { | |||
/* | |||
* Some kind of native-scroll-event related asynchronous problem | |||
* occurs here (at least on desktops) where the internal | |||
* bookkeeping isn't up to date with the real scroll position. | |||
* The weird thing is, that happens only once, and if you drag | |||
* scrollbar fast enough. After it has failed once, it never | |||
* fails again. | |||
* | |||
* Theory: the user drags the scrollbar, and this command is | |||
* executed before the browser has a chance to fire a scroll | |||
* event (which normally would correct this situation). This | |||
* would explain why slow scrolling doesn't trigger the problem, | |||
* while fast scrolling does. | |||
* | |||
* To make absolutely sure that we have the latest scroll | |||
* position, let's update the internal value. | |||
* | |||
* This might lead to a slight performance hit (on my computer | |||
* it was never more than 3ms on either of Chrome 38 or Firefox | |||
* 31). It also _slightly_ counteracts the purpose of the | |||
* internal bookkeeping. But since getScrollPos is called 3 | |||
* times (on one direction) per scroll loop, it's still better | |||
* to have take this small penalty than removing it altogether. | |||
*/ | |||
updateScrollPosFromDom(); | |||
getHandlerManager().fireEvent(new ScrollEvent()); | |||
isBeingFired = false; | |||
} | |||
}; | |||
private boolean isBeingFired; | |||
public void scheduleEvent() { | |||
if (!isBeingFired) { | |||
/* | |||
* We'll gather all the scroll events, and only fire once, once | |||
* everything has calmed down. | |||
*/ | |||
Scheduler.get().scheduleDeferred(fireEventCommand); | |||
isBeingFired = true; | |||
} | |||
} | |||
} | |||
/** | |||
* The orientation of the scrollbar. | |||
*/ | |||
public enum Direction { | |||
VERTICAL, HORIZONTAL; | |||
} | |||
private class TemporaryResizer { | |||
private static final int TEMPORARY_RESIZE_DELAY = 1000; | |||
private final Timer timer = new Timer() { | |||
@Override | |||
public void run() { | |||
internalSetScrollbarThickness(1); | |||
root.getStyle().setVisibility(Visibility.HIDDEN); | |||
} | |||
}; | |||
public void show() { | |||
internalSetScrollbarThickness(OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX); | |||
root.getStyle().setVisibility(Visibility.VISIBLE); | |||
timer.schedule(TEMPORARY_RESIZE_DELAY); | |||
} | |||
} | |||
/** | |||
* A means to listen to when the scrollbar handle in a | |||
* {@link ScrollbarBundle} either appears or is removed. | |||
*/ | |||
public interface VisibilityHandler extends EventHandler { | |||
/** | |||
* This method is called whenever the scrollbar handle's visibility is | |||
* changed in a {@link ScrollbarBundle}. | |||
* | |||
* @param event | |||
* the {@link VisibilityChangeEvent} | |||
*/ | |||
void visibilityChanged(VisibilityChangeEvent event); | |||
} | |||
public static class VisibilityChangeEvent | |||
extends GwtEvent<VisibilityHandler> { | |||
public static final Type<VisibilityHandler> TYPE = new Type<ScrollbarBundle.VisibilityHandler>() { | |||
@Override | |||
public String toString() { | |||
return "VisibilityChangeEvent"; | |||
} | |||
}; | |||
private final boolean isScrollerVisible; | |||
private VisibilityChangeEvent(boolean isScrollerVisible) { | |||
this.isScrollerVisible = isScrollerVisible; | |||
} | |||
/** | |||
* Checks whether the scroll handle is currently visible or not | |||
* | |||
* @return <code>true</code> if the scroll handle is currently visible. | |||
* <code>false</code> if not. | |||
*/ | |||
public boolean isScrollerVisible() { | |||
return isScrollerVisible; | |||
} | |||
@Override | |||
public Type<VisibilityHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
protected void dispatch(VisibilityHandler handler) { | |||
handler.visibilityChanged(this); | |||
} | |||
} | |||
/** | |||
* The pixel size for OSX's invisible scrollbars. | |||
* <p> | |||
* Touch devices don't show a scrollbar at all, so the scrollbar size is | |||
* irrelevant in their case. There doesn't seem to be any other popular | |||
* platforms that has scrollbars similar to OSX. Thus, this behavior is | |||
* tailored for OSX only, until additional platforms start behaving this | |||
* way. | |||
*/ | |||
private static final int OSX_INVISIBLE_SCROLLBAR_FAKE_SIZE_PX = 13; | |||
/** | |||
* A representation of a single vertical scrollbar. | |||
* | |||
* @see VerticalScrollbarBundle#getElement() | |||
*/ | |||
public final static class VerticalScrollbarBundle extends ScrollbarBundle { | |||
@Override | |||
public void setStylePrimaryName(String primaryStyleName) { | |||
super.setStylePrimaryName(primaryStyleName); | |||
root.addClassName(primaryStyleName + "-scroller-vertical"); | |||
} | |||
@Override | |||
protected void internalSetScrollPos(int px) { | |||
root.setScrollTop(px); | |||
} | |||
@Override | |||
protected int internalGetScrollPos() { | |||
return root.getScrollTop(); | |||
} | |||
@Override | |||
protected void internalSetScrollSize(double px) { | |||
scrollSizeElement.getStyle().setHeight(px, Unit.PX); | |||
} | |||
@Override | |||
protected String internalGetScrollSize() { | |||
return scrollSizeElement.getStyle().getHeight(); | |||
} | |||
@Override | |||
protected void internalSetOffsetSize(double px) { | |||
root.getStyle().setHeight(px, Unit.PX); | |||
} | |||
@Override | |||
public String internalGetOffsetSize() { | |||
return root.getStyle().getHeight(); | |||
} | |||
@Override | |||
protected void internalSetScrollbarThickness(double px) { | |||
root.getStyle().setPaddingRight(px, Unit.PX); | |||
root.getStyle().setWidth(0, Unit.PX); | |||
scrollSizeElement.getStyle().setWidth(px, Unit.PX); | |||
} | |||
@Override | |||
protected String internalGetScrollbarThickness() { | |||
return scrollSizeElement.getStyle().getWidth(); | |||
} | |||
@Override | |||
protected void internalForceScrollbar(boolean enable) { | |||
if (enable) { | |||
root.getStyle().setOverflowY(Overflow.SCROLL); | |||
} else { | |||
root.getStyle().clearOverflowY(); | |||
} | |||
} | |||
@Override | |||
public Direction getDirection() { | |||
return Direction.VERTICAL; | |||
} | |||
} | |||
/** | |||
* A representation of a single horizontal scrollbar. | |||
* | |||
* @see HorizontalScrollbarBundle#getElement() | |||
*/ | |||
public final static class HorizontalScrollbarBundle | |||
extends ScrollbarBundle { | |||
@Override | |||
public void setStylePrimaryName(String primaryStyleName) { | |||
super.setStylePrimaryName(primaryStyleName); | |||
root.addClassName(primaryStyleName + "-scroller-horizontal"); | |||
} | |||
@Override | |||
protected void internalSetScrollPos(int px) { | |||
root.setScrollLeft(px); | |||
} | |||
@Override | |||
protected int internalGetScrollPos() { | |||
return root.getScrollLeft(); | |||
} | |||
@Override | |||
protected void internalSetScrollSize(double px) { | |||
scrollSizeElement.getStyle().setWidth(px, Unit.PX); | |||
} | |||
@Override | |||
protected String internalGetScrollSize() { | |||
return scrollSizeElement.getStyle().getWidth(); | |||
} | |||
@Override | |||
protected void internalSetOffsetSize(double px) { | |||
root.getStyle().setWidth(px, Unit.PX); | |||
} | |||
@Override | |||
public String internalGetOffsetSize() { | |||
return root.getStyle().getWidth(); | |||
} | |||
@Override | |||
protected void internalSetScrollbarThickness(double px) { | |||
root.getStyle().setPaddingBottom(px, Unit.PX); | |||
root.getStyle().setHeight(0, Unit.PX); | |||
scrollSizeElement.getStyle().setHeight(px, Unit.PX); | |||
} | |||
@Override | |||
protected String internalGetScrollbarThickness() { | |||
return scrollSizeElement.getStyle().getHeight(); | |||
} | |||
@Override | |||
protected void internalForceScrollbar(boolean enable) { | |||
if (enable) { | |||
root.getStyle().setOverflowX(Overflow.SCROLL); | |||
} else { | |||
root.getStyle().clearOverflowX(); | |||
} | |||
} | |||
@Override | |||
public Direction getDirection() { | |||
return Direction.HORIZONTAL; | |||
} | |||
} | |||
protected final Element root = DOM.createDiv(); | |||
protected final Element scrollSizeElement = DOM.createDiv(); | |||
protected boolean isInvisibleScrollbar = false; | |||
private double scrollPos = 0; | |||
private double maxScrollPos = 0; | |||
private boolean scrollHandleIsVisible = false; | |||
private boolean isLocked = false; | |||
/** @deprecated access via {@link #getHandlerManager()} instead. */ | |||
@Deprecated | |||
private HandlerManager handlerManager; | |||
private TemporaryResizer invisibleScrollbarTemporaryResizer = new TemporaryResizer(); | |||
private final ScrollEventFirer scrollEventFirer = new ScrollEventFirer(); | |||
private HandlerRegistration scrollSizeTemporaryScrollHandler; | |||
private HandlerRegistration offsetSizeTemporaryScrollHandler; | |||
private ScrollbarBundle() { | |||
root.appendChild(scrollSizeElement); | |||
root.getStyle().setDisplay(Display.NONE); | |||
root.setTabIndex(-1); | |||
} | |||
protected abstract String internalGetScrollSize(); | |||
/** | |||
* Sets the primary style name | |||
* | |||
* @param primaryStyleName | |||
* The primary style name to use | |||
*/ | |||
public void setStylePrimaryName(String primaryStyleName) { | |||
root.setClassName(primaryStyleName + "-scroller"); | |||
} | |||
/** | |||
* Gets the root element of this scrollbar-composition. | |||
* | |||
* @return the root element | |||
*/ | |||
public final Element getElement() { | |||
return root; | |||
} | |||
/** | |||
* Modifies the scroll position of this scrollbar by a number of pixels. | |||
* <p> | |||
* <em>Note:</em> Even though {@code double} values are used, they are | |||
* currently only used as integers as large {@code int} (or small but fast | |||
* {@code long}). This means, all values are truncated to zero decimal | |||
* places. | |||
* | |||
* @param delta | |||
* the delta in pixels to change the scroll position by | |||
*/ | |||
public final void setScrollPosByDelta(double delta) { | |||
if (delta != 0) { | |||
setScrollPos(getScrollPos() + delta); | |||
} | |||
} | |||
/** | |||
* Modifies {@link #root root's} dimensions in the axis the scrollbar is | |||
* representing. | |||
* | |||
* @param px | |||
* the new size of {@link #root} in the dimension this scrollbar | |||
* is representing | |||
*/ | |||
protected abstract void internalSetOffsetSize(double px); | |||
/** | |||
* Sets the length of the scrollbar. | |||
* | |||
* @param px | |||
* the length of the scrollbar in pixels | |||
*/ | |||
public final void setOffsetSize(final double px) { | |||
/* | |||
* This needs to be made step-by-step because IE8 flat-out refuses to | |||
* fire a scroll event when the scroll size becomes smaller than the | |||
* offset size. All other browser need to suffer alongside. | |||
*/ | |||
boolean newOffsetSizeIsGreaterThanScrollSize = px > getScrollSize(); | |||
boolean offsetSizeBecomesGreaterThanScrollSize = showsScrollHandle() | |||
&& newOffsetSizeIsGreaterThanScrollSize; | |||
if (offsetSizeBecomesGreaterThanScrollSize && getScrollPos() != 0) { | |||
// must be a field because Java insists. | |||
offsetSizeTemporaryScrollHandler = addScrollHandler( | |||
new ScrollHandler() { | |||
@Override | |||
public void onScroll(ScrollEvent event) { | |||
setOffsetSizeNow(px); | |||
} | |||
}); | |||
setScrollPos(0); | |||
} else { | |||
setOffsetSizeNow(px); | |||
} | |||
} | |||
private void setOffsetSizeNow(double px) { | |||
internalSetOffsetSize(Math.max(0, px)); | |||
recalculateMaxScrollPos(); | |||
forceScrollbar(showsScrollHandle()); | |||
fireVisibilityChangeIfNeeded(); | |||
if (offsetSizeTemporaryScrollHandler != null) { | |||
offsetSizeTemporaryScrollHandler.removeHandler(); | |||
offsetSizeTemporaryScrollHandler = null; | |||
} | |||
} | |||
/** | |||
* Force the scrollbar to be visible with CSS. In practice, this means to | |||
* set either <code>overflow-x</code> or <code>overflow-y</code> to " | |||
* <code>scroll</code>" in the scrollbar's direction. | |||
* <p> | |||
* This is an IE8 workaround, since it doesn't always show scrollbars with | |||
* <code>overflow: auto</code> enabled. | |||
*/ | |||
protected void forceScrollbar(boolean enable) { | |||
if (enable) { | |||
root.getStyle().clearDisplay(); | |||
} else { | |||
root.getStyle().setDisplay(Display.NONE); | |||
} | |||
internalForceScrollbar(enable); | |||
} | |||
protected abstract void internalForceScrollbar(boolean enable); | |||
/** | |||
* Gets the length of the scrollbar | |||
* | |||
* @return the length of the scrollbar in pixels | |||
*/ | |||
public double getOffsetSize() { | |||
return parseCssDimensionToPixels(internalGetOffsetSize()); | |||
} | |||
public abstract String internalGetOffsetSize(); | |||
/** | |||
* Sets the scroll position of the scrollbar in the axis the scrollbar is | |||
* representing. | |||
* <p> | |||
* <em>Note:</em> Even though {@code double} values are used, they are | |||
* currently only used as integers as large {@code int} (or small but fast | |||
* {@code long}). This means, all values are truncated to zero decimal | |||
* places. | |||
* | |||
* @param px | |||
* the new scroll position in pixels | |||
*/ | |||
public final void setScrollPos(double px) { | |||
if (isLocked()) { | |||
return; | |||
} | |||
double oldScrollPos = scrollPos; | |||
scrollPos = Math.max(0, Math.min(maxScrollPos, truncate(px))); | |||
if (!WidgetUtil.pixelValuesEqual(oldScrollPos, scrollPos)) { | |||
if (isInvisibleScrollbar) { | |||
invisibleScrollbarTemporaryResizer.show(); | |||
} | |||
/* | |||
* This is where the value needs to be converted into an integer no | |||
* matter how we flip it, since GWT expects an integer value. | |||
* There's no point making a JSNI method that accepts doubles as the | |||
* scroll position, since the browsers themselves don't support such | |||
* large numbers (as of today, 25.3.2014). This double-ranged is | |||
* only facilitating future virtual scrollbars. | |||
*/ | |||
internalSetScrollPos(toInt32(scrollPos)); | |||
} | |||
} | |||
/** | |||
* Should be called whenever this bundle is attached to the DOM (typically, | |||
* from the onLoad of the containing widget). Used to ensure the DOM scroll | |||
* position is maintained when detaching and reattaching the bundle. | |||
* | |||
* @since 7.4.1 | |||
*/ | |||
public void onLoad() { | |||
internalSetScrollPos(toInt32(scrollPos)); | |||
} | |||
/** | |||
* Truncates a double such that no decimal places are retained. | |||
* <p> | |||
* E.g. {@code trunc(2.3d) == 2.0d} and {@code trunc(-2.3d) == -2.0d}. | |||
* | |||
* @param num | |||
* the double value to be truncated | |||
* @return the {@code num} value without any decimal digits | |||
*/ | |||
private static double truncate(double num) { | |||
if (num > 0) { | |||
return Math.floor(num); | |||
} else { | |||
return Math.ceil(num); | |||
} | |||
} | |||
/** | |||
* Modifies the element's scroll position (scrollTop or scrollLeft). | |||
* <p> | |||
* <em>Note:</em> The parameter here is a type of integer (instead of a | |||
* double) by design. The browsers internally convert all double values into | |||
* an integer value. To make this fact explicit, this API has chosen to | |||
* force integers already at this level. | |||
* | |||
* @param px | |||
* integer pixel value to scroll to | |||
*/ | |||
protected abstract void internalSetScrollPos(int px); | |||
/** | |||
* Gets the scroll position of the scrollbar in the axis the scrollbar is | |||
* representing. | |||
* | |||
* @return the new scroll position in pixels | |||
*/ | |||
public final double getScrollPos() { | |||
assert internalGetScrollPos() == toInt32( | |||
scrollPos) : "calculated scroll position (" + scrollPos | |||
+ ") did not match the DOM element scroll position (" | |||
+ internalGetScrollPos() + ")"; | |||
return scrollPos; | |||
} | |||
/** | |||
* Retrieves the element's scroll position (scrollTop or scrollLeft). | |||
* <p> | |||
* <em>Note:</em> The parameter here is a type of integer (instead of a | |||
* double) by design. The browsers internally convert all double values into | |||
* an integer value. To make this fact explicit, this API has chosen to | |||
* force integers already at this level. | |||
* | |||
* @return integer pixel value of the scroll position | |||
*/ | |||
protected abstract int internalGetScrollPos(); | |||
/** | |||
* Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in | |||
* such a way that the scrollbar is able to scroll a certain number of | |||
* pixels in the axis it is representing. | |||
* | |||
* @param px | |||
* the new size of {@link #scrollSizeElement} in the dimension | |||
* this scrollbar is representing | |||
*/ | |||
protected abstract void internalSetScrollSize(double px); | |||
/** | |||
* Sets the amount of pixels the scrollbar needs to be able to scroll | |||
* through. | |||
* | |||
* @param px | |||
* the number of pixels the scrollbar should be able to scroll | |||
* through | |||
*/ | |||
public final void setScrollSize(final double px) { | |||
/* | |||
* This needs to be made step-by-step because IE8 flat-out refuses to | |||
* fire a scroll event when the scroll size becomes smaller than the | |||
* offset size. All other browser need to suffer alongside. | |||
*/ | |||
boolean newScrollSizeIsSmallerThanOffsetSize = px <= getOffsetSize(); | |||
boolean scrollSizeBecomesSmallerThanOffsetSize = showsScrollHandle() | |||
&& newScrollSizeIsSmallerThanOffsetSize; | |||
if (scrollSizeBecomesSmallerThanOffsetSize && getScrollPos() != 0) { | |||
// must be a field because Java insists. | |||
scrollSizeTemporaryScrollHandler = addScrollHandler( | |||
new ScrollHandler() { | |||
@Override | |||
public void onScroll(ScrollEvent event) { | |||
setScrollSizeNow(px); | |||
} | |||
}); | |||
setScrollPos(0); | |||
} else { | |||
setScrollSizeNow(px); | |||
} | |||
} | |||
private void setScrollSizeNow(double px) { | |||
internalSetScrollSize(Math.max(0, px)); | |||
recalculateMaxScrollPos(); | |||
forceScrollbar(showsScrollHandle()); | |||
fireVisibilityChangeIfNeeded(); | |||
if (scrollSizeTemporaryScrollHandler != null) { | |||
scrollSizeTemporaryScrollHandler.removeHandler(); | |||
scrollSizeTemporaryScrollHandler = null; | |||
} | |||
} | |||
/** | |||
* Gets the amount of pixels the scrollbar needs to be able to scroll | |||
* through. | |||
* | |||
* @return the number of pixels the scrollbar should be able to scroll | |||
* through | |||
*/ | |||
public double getScrollSize() { | |||
return parseCssDimensionToPixels(internalGetScrollSize()); | |||
} | |||
/** | |||
* Modifies {@link #scrollSizeElement scrollSizeElement's} dimensions in the | |||
* opposite axis to what the scrollbar is representing. | |||
* | |||
* @param px | |||
* the dimension that {@link #scrollSizeElement} should take in | |||
* the opposite axis to what the scrollbar is representing | |||
*/ | |||
protected abstract void internalSetScrollbarThickness(double px); | |||
/** | |||
* Sets the scrollbar's thickness. | |||
* <p> | |||
* If the thickness is set to 0, the scrollbar will be treated as an | |||
* "invisible" scrollbar. This means, the DOM structure will be given a | |||
* non-zero size, but {@link #getScrollbarThickness()} will still return the | |||
* value 0. | |||
* | |||
* @param px | |||
* the scrollbar's thickness in pixels | |||
*/ | |||
public final void setScrollbarThickness(double px) { | |||
isInvisibleScrollbar = (px == 0); | |||
if (isInvisibleScrollbar) { | |||
Event.sinkEvents(root, Event.ONSCROLL); | |||
Event.setEventListener(root, new EventListener() { | |||
@Override | |||
public void onBrowserEvent(Event event) { | |||
invisibleScrollbarTemporaryResizer.show(); | |||
} | |||
}); | |||
root.getStyle().setVisibility(Visibility.HIDDEN); | |||
} else { | |||
Event.sinkEvents(root, 0); | |||
Event.setEventListener(root, null); | |||
root.getStyle().clearVisibility(); | |||
} | |||
internalSetScrollbarThickness(Math.max(1d, px)); | |||
} | |||
/** | |||
* Gets the scrollbar's thickness as defined in the DOM. | |||
* | |||
* @return the scrollbar's thickness as defined in the DOM, in pixels | |||
*/ | |||
protected abstract String internalGetScrollbarThickness(); | |||
/** | |||
* Gets the scrollbar's thickness. | |||
* <p> | |||
* This value will differ from the value in the DOM, if the thickness was | |||
* set to 0 with {@link #setScrollbarThickness(double)}, as the scrollbar is | |||
* then treated as "invisible." | |||
* | |||
* @return the scrollbar's thickness in pixels | |||
*/ | |||
public final double getScrollbarThickness() { | |||
if (!isInvisibleScrollbar) { | |||
return parseCssDimensionToPixels(internalGetScrollbarThickness()); | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/** | |||
* Checks whether the scrollbar's handle is visible. | |||
* <p> | |||
* In other words, this method checks whether the contents is larger than | |||
* can visually fit in the element. | |||
* | |||
* @return <code>true</code> iff the scrollbar's handle is visible | |||
*/ | |||
public boolean showsScrollHandle() { | |||
return getScrollSize() - getOffsetSize() > WidgetUtil.PIXEL_EPSILON; | |||
} | |||
public void recalculateMaxScrollPos() { | |||
double scrollSize = getScrollSize(); | |||
double offsetSize = getOffsetSize(); | |||
maxScrollPos = Math.max(0, scrollSize - offsetSize); | |||
// make sure that the correct max scroll position is maintained. | |||
setScrollPos(scrollPos); | |||
} | |||
/** | |||
* This is a method that JSNI can call to synchronize the object state from | |||
* the DOM. | |||
*/ | |||
private final void updateScrollPosFromDom() { | |||
/* | |||
* TODO: this method probably shouldn't be called from Escalator's JSNI, | |||
* but probably could be handled internally by this listening to its own | |||
* element. Would clean up the code quite a bit. Needs further | |||
* investigation. | |||
*/ | |||
int newScrollPos = internalGetScrollPos(); | |||
if (!isLocked()) { | |||
scrollPos = newScrollPos; | |||
scrollEventFirer.scheduleEvent(); | |||
} else if (scrollPos != newScrollPos) { | |||
// we need to actually undo the setting of the scroll. | |||
internalSetScrollPos(toInt32(scrollPos)); | |||
} | |||
} | |||
protected HandlerManager getHandlerManager() { | |||
if (handlerManager == null) { | |||
handlerManager = new HandlerManager(this); | |||
} | |||
return handlerManager; | |||
} | |||
/** | |||
* Adds handler for the scrollbar handle visibility. | |||
* | |||
* @param handler | |||
* the {@link VisibilityHandler} to add | |||
* @return {@link HandlerRegistration} used to remove the handler | |||
*/ | |||
public HandlerRegistration addVisibilityHandler( | |||
final VisibilityHandler handler) { | |||
return getHandlerManager().addHandler(VisibilityChangeEvent.TYPE, | |||
handler); | |||
} | |||
private void fireVisibilityChangeIfNeeded() { | |||
final boolean oldHandleIsVisible = scrollHandleIsVisible; | |||
scrollHandleIsVisible = showsScrollHandle(); | |||
if (oldHandleIsVisible != scrollHandleIsVisible) { | |||
final VisibilityChangeEvent event = new VisibilityChangeEvent( | |||
scrollHandleIsVisible); | |||
getHandlerManager().fireEvent(event); | |||
} | |||
} | |||
/** | |||
* Converts a double into an integer by JavaScript's terms. | |||
* <p> | |||
* Implementation copied from {@link Element#toInt32(double)}. | |||
* | |||
* @param val | |||
* the double value to convert into an integer | |||
* @return the double value converted to an integer | |||
*/ | |||
private static native int toInt32(double val) | |||
/*-{ | |||
return Math.round(val) | 0; | |||
}-*/; | |||
/** | |||
* Locks or unlocks the scrollbar bundle. | |||
* <p> | |||
* A locked scrollbar bundle will refuse to scroll, both programmatically | |||
* and via user-triggered events. | |||
* | |||
* @param isLocked | |||
* <code>true</code> to lock, <code>false</code> to unlock | |||
*/ | |||
public void setLocked(boolean isLocked) { | |||
this.isLocked = isLocked; | |||
} | |||
/** | |||
* Checks whether the scrollbar bundle is locked or not. | |||
* | |||
* @return <code>true</code> iff the scrollbar bundle is locked | |||
*/ | |||
public boolean isLocked() { | |||
return isLocked; | |||
} | |||
/** | |||
* Returns the scroll direction of this scrollbar bundle. | |||
* | |||
* @return the scroll direction of this scrollbar bundle | |||
*/ | |||
public abstract Direction getDirection(); | |||
/** | |||
* Adds a scroll handler to the scrollbar bundle. | |||
* | |||
* @param handler | |||
* the handler to add | |||
* @return the registration object for the handler registration | |||
*/ | |||
public HandlerRegistration addScrollHandler(final ScrollHandler handler) { | |||
return getHandlerManager().addHandler(ScrollEvent.TYPE, handler); | |||
} | |||
private static double parseCssDimensionToPixels(String size) { | |||
/* | |||
* Sizes of elements are calculated from CSS rather than | |||
* element.getOffset*() because those values are 0 whenever display: | |||
* none. Because we know that all elements have populated | |||
* CSS-dimensions, it's better to do it that way. | |||
* | |||
* Another solution would be to make the elements visible while | |||
* measuring and then re-hide them, but that would cause unnecessary | |||
* reflows that would probably kill the performance dead. | |||
*/ | |||
if (size.isEmpty()) { | |||
return 0; | |||
} else { | |||
assert size.endsWith("px") : "Can't parse CSS dimension \"" + size | |||
+ "\""; | |||
return Double.parseDouble(size.substring(0, size.length() - 2)); | |||
} | |||
} | |||
@Override | |||
public boolean isWorkPending() { | |||
return scrollSizeTemporaryScrollHandler != null | |||
|| offsetSizeTemporaryScrollHandler != null; | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.google.gwt.dom.client.Element; | |||
/** | |||
* A representation of a spacer element in a | |||
* {@link com.vaadin.v7.client.widget.escalator.RowContainer.BodyRowContainer}. | |||
* | |||
* @since 7.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface Spacer { | |||
/** | |||
* Gets the root element for the spacer content. | |||
* | |||
* @return the root element for the spacer content | |||
*/ | |||
Element getElement(); | |||
/** | |||
* Gets the decorative element for this spacer. | |||
*/ | |||
Element getDecoElement(); | |||
/** | |||
* Gets the row index. | |||
* | |||
* @return the row index. | |||
*/ | |||
int getRow(); | |||
} |
@@ -0,0 +1,64 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator; | |||
import com.vaadin.v7.client.widget.escalator.RowContainer.BodyRowContainer; | |||
/** | |||
* An interface that handles the display of content for spacers. | |||
* <p> | |||
* The updater is responsible for making sure all elements are properly | |||
* constructed and cleaned up. | |||
* | |||
* @since 7.5.0 | |||
* @author Vaadin Ltd | |||
* @see Spacer | |||
* @see BodyRowContainer | |||
*/ | |||
public interface SpacerUpdater { | |||
/** A spacer updater that does nothing. */ | |||
public static final SpacerUpdater NULL = new SpacerUpdater() { | |||
@Override | |||
public void init(Spacer spacer) { | |||
// NOOP | |||
} | |||
@Override | |||
public void destroy(Spacer spacer) { | |||
// NOOP | |||
} | |||
}; | |||
/** | |||
* Called whenever a spacer should be initialized with content. | |||
* | |||
* @param spacer | |||
* the spacer reference that should be initialized | |||
*/ | |||
void init(Spacer spacer); | |||
/** | |||
* Called whenever a spacer should be cleaned. | |||
* <p> | |||
* The structure to clean up is the same that has been constructed by | |||
* {@link #init(Spacer)}. | |||
* | |||
* @param spacer | |||
* the spacer reference that should be destroyed | |||
*/ | |||
void destroy(Spacer spacer); | |||
} |
@@ -0,0 +1,48 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
/** | |||
* Event fired when the row height changed in the Escalator's header, body or | |||
* footer. | |||
* | |||
* @since 7.7 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class RowHeightChangedEvent extends GwtEvent<RowHeightChangedHandler> { | |||
/** | |||
* Handler type. | |||
*/ | |||
public final static Type<RowHeightChangedHandler> TYPE = new Type<RowHeightChangedHandler>(); | |||
public static final Type<RowHeightChangedHandler> getType() { | |||
return TYPE; | |||
} | |||
@Override | |||
public Type<RowHeightChangedHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
protected void dispatch(RowHeightChangedHandler handler) { | |||
handler.onRowHeightChanged(this); | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.escalator.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Event handler for a row height changed event. | |||
* | |||
* @since 7.7 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface RowHeightChangedHandler extends EventHandler { | |||
/** | |||
* A row height changed event, fired by Escalator when the header, body or | |||
* footer row height has changed. | |||
* | |||
* @param event | |||
* Row height changed event | |||
*/ | |||
public void onRowHeightChanged(RowHeightChangedEvent event); | |||
} |
@@ -0,0 +1,647 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.animation.client.AnimationScheduler; | |||
import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; | |||
import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.TableElement; | |||
import com.google.gwt.dom.client.TableSectionElement; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
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.WidgetUtil; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* A class for handling automatic scrolling vertically / horizontally in the | |||
* Grid when the cursor is close enough the edge of the body of the grid, | |||
* depending on the scroll direction chosen. | |||
* | |||
* @since 7.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class AutoScroller { | |||
/** | |||
* Callback that notifies when the cursor is on top of a new row or column | |||
* because of the automatic scrolling. | |||
*/ | |||
public interface AutoScrollerCallback { | |||
/** | |||
* Triggered when doing automatic scrolling. | |||
* <p> | |||
* Because the auto scroller currently only supports scrolling in one | |||
* axis, this method is used for both vertical and horizontal scrolling. | |||
* | |||
* @param scrollDiff | |||
* the amount of pixels that have been auto scrolled since | |||
* last call | |||
*/ | |||
void onAutoScroll(int scrollDiff); | |||
/** | |||
* Triggered when the grid scroll has reached the minimum scroll | |||
* position. Depending on the scroll axis, either scrollLeft or | |||
* scrollTop is 0. | |||
*/ | |||
void onAutoScrollReachedMin(); | |||
/** | |||
* Triggered when the grid scroll has reached the max scroll position. | |||
* Depending on the scroll axis, either scrollLeft or scrollTop is at | |||
* its maximum value. | |||
*/ | |||
void onAutoScrollReachedMax(); | |||
} | |||
public enum ScrollAxis { | |||
VERTICAL, HORIZONTAL | |||
} | |||
/** The maximum number of pixels per second to autoscroll. */ | |||
private static final int SCROLL_TOP_SPEED_PX_SEC = 500; | |||
/** | |||
* The minimum area where the grid doesn't scroll while the pointer is | |||
* pressed. | |||
*/ | |||
private static final int MIN_NO_AUTOSCROLL_AREA_PX = 50; | |||
/** The size of the autoscroll area, both top/left and bottom/right. */ | |||
private int scrollAreaPX = 100; | |||
/** | |||
* This class's main objective is to listen when to stop autoscrolling, and | |||
* make sure everything stops accordingly. | |||
*/ | |||
private class TouchEventHandler implements NativePreviewHandler { | |||
@Override | |||
public void onPreviewNativeEvent(final NativePreviewEvent event) { | |||
/* | |||
* Remember: targetElement is always where touchstart started, not | |||
* where the finger is pointing currently. | |||
*/ | |||
switch (event.getTypeInt()) { | |||
case Event.ONTOUCHSTART: { | |||
if (event.getNativeEvent().getTouches().length() == 1) { | |||
/* | |||
* Something has dropped a touchend/touchcancel and the | |||
* scroller is most probably running amok. Let's cancel it | |||
* and pretend that everything's going as expected | |||
* | |||
* Because this is a preview, this code is run before start | |||
* event can be passed to the start(...) method. | |||
*/ | |||
stop(); | |||
/* | |||
* Related TODO: investigate why iOS seems to ignore a | |||
* touchend/touchcancel when frames are dropped, and/or if | |||
* something can be done about that. | |||
*/ | |||
} | |||
break; | |||
} | |||
case Event.ONTOUCHMOVE: | |||
event.cancel(); | |||
break; | |||
case Event.ONTOUCHEND: | |||
case Event.ONTOUCHCANCEL: | |||
// TODO investigate if this works as desired | |||
stop(); | |||
break; | |||
} | |||
} | |||
} | |||
/** | |||
* This class's responsibility is to scroll the table while a pointer is | |||
* kept in a scrolling zone. | |||
* <p> | |||
* <em>Techical note:</em> This class is an AnimationCallback because we | |||
* need a timer: when the finger is kept in place while the grid scrolls, we | |||
* still need to be able to make new selections. So, instead of relying on | |||
* events (which won't be fired, since the pointer isn't necessarily | |||
* moving), we do this check on each frame while the pointer is "active" | |||
* (mouse is pressed, finger is on screen). | |||
*/ | |||
private class AutoScrollingFrame implements AnimationCallback { | |||
/** | |||
* If the acceleration gradient area is smaller than this, autoscrolling | |||
* will be disabled (it becomes too quick to accelerate to be usable). | |||
*/ | |||
private static final int GRADIENT_MIN_THRESHOLD_PX = 10; | |||
/** | |||
* The speed at which the gradient area recovers, once scrolling in that | |||
* direction has started. | |||
*/ | |||
private static final int SCROLL_AREA_REBOUND_PX_PER_SEC = 1; | |||
private static final double SCROLL_AREA_REBOUND_PX_PER_MS = SCROLL_AREA_REBOUND_PX_PER_SEC | |||
/ 1000.0d; | |||
/** | |||
* The lowest y/x-coordinate on the {@link Event#getClientY() client-y} | |||
* or {@link Event#getClientX() client-x} from where we need to start | |||
* scrolling towards the top/left. | |||
*/ | |||
private int startBound = -1; | |||
/** | |||
* The highest y/x-coordinate on the {@link Event#getClientY() client-y} | |||
* or {@link Event#getClientX() client-x} from where we need to | |||
* scrolling towards the bottom. | |||
*/ | |||
private int endBound = -1; | |||
/** | |||
* The area where the selection acceleration takes place. If < | |||
* {@link #GRADIENT_MIN_THRESHOLD_PX}, autoscrolling is disabled | |||
*/ | |||
private final int gradientArea; | |||
/** | |||
* The number of pixels per seconds we currently are scrolling (negative | |||
* is towards the top/left, positive is towards the bottom/right). | |||
*/ | |||
private double scrollSpeed = 0; | |||
private double prevTimestamp = 0; | |||
/** | |||
* This field stores fractions of pixels to scroll, to make sure that | |||
* we're able to scroll less than one px per frame. | |||
*/ | |||
private double pixelsToScroll = 0.0d; | |||
/** Should this animator be running. */ | |||
private boolean running = false; | |||
/** The handle in which this instance is running. */ | |||
private AnimationHandle handle; | |||
/** | |||
* The pointer's pageY (VERTICAL) / pageX (HORIZONTAL) coordinate | |||
* depending on scrolling axis. | |||
*/ | |||
private int scrollingAxisPageCoordinate; | |||
/** @see #doScrollAreaChecks(int) */ | |||
private int finalStartBound; | |||
/** @see #doScrollAreaChecks(int) */ | |||
private int finalEndBound; | |||
private boolean scrollAreaShouldRebound = false; | |||
public AutoScrollingFrame(final int startBound, final int endBound, | |||
final int gradientArea) { | |||
finalStartBound = startBound; | |||
finalEndBound = endBound; | |||
this.gradientArea = gradientArea; | |||
} | |||
@Override | |||
public void execute(final double timestamp) { | |||
final double timeDiff = timestamp - prevTimestamp; | |||
prevTimestamp = timestamp; | |||
reboundScrollArea(timeDiff); | |||
pixelsToScroll += scrollSpeed * (timeDiff / 1000.0d); | |||
final int intPixelsToScroll = (int) pixelsToScroll; | |||
pixelsToScroll -= intPixelsToScroll; | |||
if (intPixelsToScroll != 0) { | |||
double scrollPos; | |||
double maxScrollPos; | |||
double newScrollPos; | |||
if (scrollDirection == ScrollAxis.VERTICAL) { | |||
scrollPos = grid.getScrollTop(); | |||
maxScrollPos = getMaxScrollTop(); | |||
} else { | |||
scrollPos = grid.getScrollLeft(); | |||
maxScrollPos = getMaxScrollLeft(); | |||
} | |||
if (intPixelsToScroll > 0 && scrollPos < maxScrollPos | |||
|| intPixelsToScroll < 0 && scrollPos > 0) { | |||
newScrollPos = scrollPos + intPixelsToScroll; | |||
if (scrollDirection == ScrollAxis.VERTICAL) { | |||
grid.setScrollTop(newScrollPos); | |||
} else { | |||
grid.setScrollLeft(newScrollPos); | |||
} | |||
callback.onAutoScroll(intPixelsToScroll); | |||
if (newScrollPos <= 0) { | |||
callback.onAutoScrollReachedMin(); | |||
} else if (newScrollPos >= maxScrollPos) { | |||
callback.onAutoScrollReachedMax(); | |||
} | |||
} | |||
} | |||
reschedule(); | |||
} | |||
/** | |||
* If the scroll are has been offset by the pointer starting out there, | |||
* move it back a bit | |||
*/ | |||
private void reboundScrollArea(double timeDiff) { | |||
if (!scrollAreaShouldRebound) { | |||
return; | |||
} | |||
int reboundPx = (int) Math | |||
.ceil(SCROLL_AREA_REBOUND_PX_PER_MS * timeDiff); | |||
if (startBound < finalStartBound) { | |||
startBound += reboundPx; | |||
startBound = Math.min(startBound, finalStartBound); | |||
updateScrollSpeed(scrollingAxisPageCoordinate); | |||
} else if (endBound > finalEndBound) { | |||
endBound -= reboundPx; | |||
endBound = Math.max(endBound, finalEndBound); | |||
updateScrollSpeed(scrollingAxisPageCoordinate); | |||
} | |||
} | |||
private void updateScrollSpeed(final int pointerPageCordinate) { | |||
final double ratio; | |||
if (pointerPageCordinate < startBound) { | |||
final double distance = pointerPageCordinate - startBound; | |||
ratio = Math.max(-1, distance / gradientArea); | |||
} | |||
else if (pointerPageCordinate > endBound) { | |||
final double distance = pointerPageCordinate - endBound; | |||
ratio = Math.min(1, distance / gradientArea); | |||
} | |||
else { | |||
ratio = 0; | |||
} | |||
scrollSpeed = ratio * SCROLL_TOP_SPEED_PX_SEC; | |||
} | |||
public void start() { | |||
running = true; | |||
reschedule(); | |||
} | |||
public void stop() { | |||
running = false; | |||
if (handle != null) { | |||
handle.cancel(); | |||
handle = null; | |||
} | |||
} | |||
private void reschedule() { | |||
if (running && gradientArea >= GRADIENT_MIN_THRESHOLD_PX) { | |||
handle = AnimationScheduler.get().requestAnimationFrame(this, | |||
grid.getElement()); | |||
} | |||
} | |||
public void updatePointerCoords(int pageX, int pageY) { | |||
final int pageCordinate; | |||
if (scrollDirection == ScrollAxis.VERTICAL) { | |||
pageCordinate = pageY; | |||
} else { | |||
pageCordinate = pageX; | |||
} | |||
doScrollAreaChecks(pageCordinate); | |||
updateScrollSpeed(pageCordinate); | |||
scrollingAxisPageCoordinate = pageCordinate; | |||
} | |||
/** | |||
* This method checks whether the first pointer event started in an area | |||
* that would start scrolling immediately, and does some actions | |||
* accordingly. | |||
* <p> | |||
* If it is, that scroll area will be offset "beyond" the pointer (above | |||
* if pointer is towards the top/left, otherwise below/right). | |||
*/ | |||
private void doScrollAreaChecks(int pageCordinate) { | |||
/* | |||
* The first run makes sure that neither scroll position is | |||
* underneath the finger, but offset to either direction from | |||
* underneath the pointer. | |||
*/ | |||
if (startBound == -1) { | |||
startBound = Math.min(finalStartBound, pageCordinate); | |||
endBound = Math.max(finalEndBound, pageCordinate); | |||
} | |||
/* | |||
* Subsequent runs make sure that the scroll area grows (but doesn't | |||
* shrink) with the finger, but no further than the final bound. | |||
*/ | |||
else { | |||
int oldTopBound = startBound; | |||
if (startBound < finalStartBound) { | |||
startBound = Math.max(startBound, | |||
Math.min(finalStartBound, pageCordinate)); | |||
} | |||
int oldBottomBound = endBound; | |||
if (endBound > finalEndBound) { | |||
endBound = Math.min(endBound, | |||
Math.max(finalEndBound, pageCordinate)); | |||
} | |||
final boolean startDidNotMove = oldTopBound == startBound; | |||
final boolean endDidNotMove = oldBottomBound == endBound; | |||
final boolean wasMovement = pageCordinate != scrollingAxisPageCoordinate; | |||
scrollAreaShouldRebound = (startDidNotMove && endDidNotMove | |||
&& wasMovement); | |||
} | |||
} | |||
} | |||
/** | |||
* This handler makes sure that pointer movements are handled. | |||
* <p> | |||
* Essentially, a native preview handler is registered (so that selection | |||
* gestures can happen outside of the selection column). The handler itself | |||
* makes sure that it's detached when the pointer is "lifted". | |||
*/ | |||
private final NativePreviewHandler scrollPreviewHandler = new NativePreviewHandler() { | |||
@Override | |||
public void onPreviewNativeEvent(final NativePreviewEvent event) { | |||
if (autoScroller == null) { | |||
stop(); | |||
return; | |||
} | |||
final NativeEvent nativeEvent = event.getNativeEvent(); | |||
int pageY = 0; | |||
int pageX = 0; | |||
switch (event.getTypeInt()) { | |||
case Event.ONMOUSEMOVE: | |||
case Event.ONTOUCHMOVE: | |||
pageY = WidgetUtil.getTouchOrMouseClientY(nativeEvent); | |||
pageX = WidgetUtil.getTouchOrMouseClientX(nativeEvent); | |||
autoScroller.updatePointerCoords(pageX, pageY); | |||
break; | |||
case Event.ONMOUSEUP: | |||
case Event.ONTOUCHEND: | |||
case Event.ONTOUCHCANCEL: | |||
stop(); | |||
break; | |||
} | |||
} | |||
}; | |||
/** The registration info for {@link #scrollPreviewHandler} */ | |||
private HandlerRegistration handlerRegistration; | |||
/** | |||
* The top/left bound, as calculated from the {@link Event#getClientY() | |||
* client-y} or {@link Event#getClientX() client-x} coordinates. | |||
*/ | |||
private double startingBound = -1; | |||
/** | |||
* The bottom/right bound, as calculated from the {@link Event#getClientY() | |||
* client-y} or or {@link Event#getClientX() client-x} coordinates. | |||
*/ | |||
private int endingBound = -1; | |||
/** The size of the autoscroll acceleration area. */ | |||
private int gradientArea; | |||
private Grid<?> grid; | |||
private HandlerRegistration nativePreviewHandlerRegistration; | |||
private ScrollAxis scrollDirection; | |||
private AutoScrollingFrame autoScroller; | |||
private AutoScrollerCallback callback; | |||
/** | |||
* Creates a new instance for scrolling the given grid. | |||
* | |||
* @param grid | |||
* the grid to auto scroll | |||
*/ | |||
public AutoScroller(Grid<?> grid) { | |||
this.grid = grid; | |||
} | |||
/** | |||
* Starts the automatic scrolling detection. | |||
* | |||
* @param startEvent | |||
* the event that starts the automatic scroll | |||
* @param scrollAxis | |||
* the axis along which the scrolling should happen | |||
* @param callback | |||
* the callback for getting info about the automatic scrolling | |||
*/ | |||
public void start(final NativeEvent startEvent, ScrollAxis scrollAxis, | |||
AutoScrollerCallback callback) { | |||
scrollDirection = scrollAxis; | |||
this.callback = callback; | |||
injectNativeHandler(); | |||
start(); | |||
startEvent.preventDefault(); | |||
startEvent.stopPropagation(); | |||
} | |||
/** | |||
* Stops the automatic scrolling. | |||
*/ | |||
public void stop() { | |||
if (handlerRegistration != null) { | |||
handlerRegistration.removeHandler(); | |||
handlerRegistration = null; | |||
} | |||
if (autoScroller != null) { | |||
autoScroller.stop(); | |||
autoScroller = null; | |||
} | |||
removeNativeHandler(); | |||
} | |||
/** | |||
* Set the auto scroll area height or width depending on the scrolling axis. | |||
* This is the amount of pixels from the edge of the grid that the scroll is | |||
* triggered. | |||
* <p> | |||
* Defaults to 100px. | |||
* | |||
* @param px | |||
* the pixel height/width for the auto scroll area depending on | |||
* direction | |||
*/ | |||
public void setScrollArea(int px) { | |||
scrollAreaPX = px; | |||
} | |||
/** | |||
* Returns the size of the auto scroll area in pixels. | |||
* <p> | |||
* Defaults to 100px. | |||
* | |||
* @return size in pixels | |||
*/ | |||
public int getScrollArea() { | |||
return scrollAreaPX; | |||
} | |||
private void start() { | |||
/* | |||
* bounds are updated whenever the autoscroll cycle starts, to make sure | |||
* that the widget hasn't changed in size, moved around, or whatnot. | |||
*/ | |||
updateScrollBounds(); | |||
assert handlerRegistration == null : "handlerRegistration was not null"; | |||
assert autoScroller == null : "autoScroller was not null"; | |||
handlerRegistration = Event | |||
.addNativePreviewHandler(scrollPreviewHandler); | |||
autoScroller = new AutoScrollingFrame((int) Math.ceil(startingBound), | |||
endingBound, gradientArea); | |||
autoScroller.start(); | |||
} | |||
private void updateScrollBounds() { | |||
double startBorder = getBodyClientStart(); | |||
final int endBorder = getBodyClientEnd(); | |||
startBorder += getFrozenColumnsWidth(); | |||
startingBound = startBorder + scrollAreaPX; | |||
endingBound = endBorder - scrollAreaPX; | |||
gradientArea = scrollAreaPX; | |||
// modify bounds if they're too tightly packed | |||
if (endingBound - startingBound < MIN_NO_AUTOSCROLL_AREA_PX) { | |||
double adjustment = MIN_NO_AUTOSCROLL_AREA_PX | |||
- (endingBound - startingBound); | |||
startingBound -= adjustment / 2; | |||
endingBound += adjustment / 2; | |||
gradientArea -= adjustment / 2; | |||
} | |||
} | |||
private void injectNativeHandler() { | |||
removeNativeHandler(); | |||
nativePreviewHandlerRegistration = Event | |||
.addNativePreviewHandler(new TouchEventHandler()); | |||
} | |||
private void removeNativeHandler() { | |||
if (nativePreviewHandlerRegistration != null) { | |||
nativePreviewHandlerRegistration.removeHandler(); | |||
nativePreviewHandlerRegistration = null; | |||
} | |||
} | |||
private TableElement getTableElement() { | |||
final Element root = grid.getElement(); | |||
final Element tablewrapper = Element.as(root.getChild(2)); | |||
if (tablewrapper != null) { | |||
return TableElement.as(tablewrapper.getFirstChildElement()); | |||
} else { | |||
return null; | |||
} | |||
} | |||
private TableSectionElement getTheadElement() { | |||
TableElement table = getTableElement(); | |||
if (table != null) { | |||
return table.getTHead(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
private TableSectionElement getTfootElement() { | |||
TableElement table = getTableElement(); | |||
if (table != null) { | |||
return table.getTFoot(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
private int getBodyClientEnd() { | |||
if (scrollDirection == ScrollAxis.VERTICAL) { | |||
return getTfootElement().getAbsoluteTop() - 1; | |||
} else { | |||
return getTableElement().getAbsoluteRight(); | |||
} | |||
} | |||
private int getBodyClientStart() { | |||
if (scrollDirection == ScrollAxis.VERTICAL) { | |||
return getTheadElement().getAbsoluteBottom() + 1; | |||
} else { | |||
return getTableElement().getAbsoluteLeft(); | |||
} | |||
} | |||
public double getFrozenColumnsWidth() { | |||
double value = 0; | |||
for (int i = 0; i < getRealFrozenColumnCount(); i++) { | |||
value += grid.getColumn(i).getWidthActual(); | |||
} | |||
return value; | |||
} | |||
private int getRealFrozenColumnCount() { | |||
if (grid.getFrozenColumnCount() < 0) { | |||
return 0; | |||
} else if (grid.getSelectionModel() | |||
.getSelectionColumnRenderer() != null) { | |||
// includes the selection column | |||
return grid.getFrozenColumnCount() + 1; | |||
} else { | |||
return grid.getFrozenColumnCount(); | |||
} | |||
} | |||
private double getMaxScrollLeft() { | |||
return grid.getScrollWidth() | |||
- (getTableElement().getParentElement().getOffsetWidth() | |||
- getFrozenColumnsWidth()); | |||
} | |||
private double getMaxScrollTop() { | |||
return grid.getScrollHeight() - getTfootElement().getOffsetHeight() | |||
- getTheadElement().getOffsetHeight(); | |||
} | |||
} |
@@ -0,0 +1,151 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.dom.client.TableCellElement; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* A data class which contains information which identifies a cell in a | |||
* {@link Grid}. | |||
* <p> | |||
* Since this class follows the <code>Flyweight</code>-pattern any instance of | |||
* this object is subject to change without the user knowing it and so should | |||
* not be stored anywhere outside of the method providing these instances. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* the type of the row object containing this cell | |||
* @since 7.4 | |||
*/ | |||
public class CellReference<T> { | |||
private int columnIndexDOM; | |||
private int columnIndex; | |||
private Grid.Column<?, T> column; | |||
private final RowReference<T> rowReference; | |||
public CellReference(RowReference<T> rowReference) { | |||
this.rowReference = rowReference; | |||
} | |||
/** | |||
* Sets the identifying information for this cell. | |||
* <p> | |||
* The difference between {@link #columnIndexDOM} and {@link #columnIndex} | |||
* comes from hidden columns. | |||
* | |||
* @param columnIndexDOM | |||
* the index of the column in the DOM | |||
* @param columnIndex | |||
* the index of the column | |||
* @param column | |||
* the column object | |||
*/ | |||
public void set(int columnIndexDOM, int columnIndex, | |||
Grid.Column<?, T> column) { | |||
this.columnIndexDOM = columnIndexDOM; | |||
this.columnIndex = columnIndex; | |||
this.column = column; | |||
} | |||
/** | |||
* Gets the grid that contains the referenced cell. | |||
* | |||
* @return the grid that contains referenced cell | |||
*/ | |||
public Grid<T> getGrid() { | |||
return rowReference.getGrid(); | |||
} | |||
/** | |||
* Gets the row index of the row. | |||
* | |||
* @return the index of the row | |||
*/ | |||
public int getRowIndex() { | |||
return rowReference.getRowIndex(); | |||
} | |||
/** | |||
* Gets the row data object. | |||
* | |||
* @return the row object | |||
*/ | |||
public T getRow() { | |||
return rowReference.getRow(); | |||
} | |||
/** | |||
* Gets the index of the column. | |||
* <p> | |||
* <em>NOTE:</em> The index includes hidden columns in the count, unlike | |||
* {@link #getColumnIndexDOM()}. | |||
* | |||
* @return the index of the column | |||
*/ | |||
public int getColumnIndex() { | |||
return columnIndex; | |||
} | |||
/** | |||
* Gets the index of the cell in the DOM. The difference to | |||
* {@link #getColumnIndex()} is caused by hidden columns. | |||
* | |||
* @since 7.5.0 | |||
* @return the index of the column in the DOM | |||
*/ | |||
public int getColumnIndexDOM() { | |||
return columnIndexDOM; | |||
} | |||
/** | |||
* Gets the column objects. | |||
* | |||
* @return the column object | |||
*/ | |||
public Grid.Column<?, T> getColumn() { | |||
return column; | |||
} | |||
/** | |||
* Gets the value of the cell. | |||
* | |||
* @return the value of the cell | |||
*/ | |||
public Object getValue() { | |||
return getColumn().getValue(getRow()); | |||
} | |||
/** | |||
* Get the element of the cell. | |||
* | |||
* @return the element of the cell | |||
*/ | |||
public TableCellElement getElement() { | |||
return rowReference.getElement().getCells().getItem(columnIndexDOM); | |||
} | |||
/** | |||
* Gets the RowReference for this CellReference. | |||
* | |||
* @return the row reference | |||
*/ | |||
protected RowReference<T> getRowReference() { | |||
return rowReference; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Callback interface for generating custom style names for cells | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* the row type of the target grid | |||
* @see Grid#setCellStyleGenerator(CellStyleGenerator) | |||
* @since 7.4 | |||
*/ | |||
public interface CellStyleGenerator<T> { | |||
/** | |||
* Called by Grid to generate a style name for a column element. | |||
* | |||
* @param cellReference | |||
* The cell to generate a style for | |||
* @return the style name to add to this cell, or {@code null} to not set | |||
* any style | |||
*/ | |||
public abstract String getStyle(CellReference<T> cellReference); | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.shared.ui.grid.Range; | |||
/** | |||
* Event object describing a change of row availability in DataSource of a Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class DataAvailableEvent extends GwtEvent<DataAvailableHandler> { | |||
private Range rowsAvailable; | |||
public static final Type<DataAvailableHandler> TYPE = new Type<DataAvailableHandler>(); | |||
public DataAvailableEvent(Range rowsAvailable) { | |||
this.rowsAvailable = rowsAvailable; | |||
} | |||
/** | |||
* Returns the range of available rows in {@link DataSource} for this event. | |||
* | |||
* @return range of available rows | |||
*/ | |||
public Range getAvailableRows() { | |||
return rowsAvailable; | |||
} | |||
@Override | |||
public Type<DataAvailableHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
protected void dispatch(DataAvailableHandler handler) { | |||
handler.onDataAvailable(this); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for {@link DataAvailableEvent}s. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface DataAvailableHandler extends EventHandler { | |||
/** | |||
* Called when DataSource has data available. Supplied with row range. | |||
* | |||
* @param availableRows | |||
* Range of rows available in the DataSource | |||
* @return true if the command was successfully completed, false to call | |||
* again the next time new data is available | |||
*/ | |||
public void onDataAvailable(DataAvailableEvent event); | |||
} |
@@ -0,0 +1,332 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.core.client.Duration; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.event.dom.client.KeyCodes; | |||
import com.google.gwt.user.client.Event; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.client.ui.FocusUtil; | |||
import com.vaadin.v7.client.widgets.Grid.Editor; | |||
import com.vaadin.v7.client.widgets.Grid.EditorDomEvent; | |||
/** | |||
* The default handler for Grid editor events. Offers several overridable | |||
* protected methods for easier customization. | |||
* | |||
* @since 7.6 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class DefaultEditorEventHandler<T> implements Editor.EventHandler<T> { | |||
public static final int KEYCODE_OPEN = KeyCodes.KEY_ENTER; | |||
public static final int KEYCODE_MOVE_VERTICAL = KeyCodes.KEY_ENTER; | |||
public static final int KEYCODE_CLOSE = KeyCodes.KEY_ESCAPE; | |||
public static final int KEYCODE_MOVE_HORIZONTAL = KeyCodes.KEY_TAB; | |||
public static final int KEYCODE_BUFFERED_SAVE = KeyCodes.KEY_ENTER; | |||
private double lastTouchEventTime = 0; | |||
private int lastTouchEventX = -1; | |||
private int lastTouchEventY = -1; | |||
private int lastTouchEventRow = -1; | |||
/** | |||
* Returns whether the given event is a touch event that should open the | |||
* editor. | |||
* | |||
* @param event | |||
* the received event | |||
* @return whether the event is a touch open event | |||
*/ | |||
protected boolean isTouchOpenEvent(EditorDomEvent<T> event) { | |||
final Event e = event.getDomEvent(); | |||
final int type = e.getTypeInt(); | |||
final double now = Duration.currentTimeMillis(); | |||
final int currentX = WidgetUtil.getTouchOrMouseClientX(e); | |||
final int currentY = WidgetUtil.getTouchOrMouseClientY(e); | |||
final boolean validTouchOpenEvent = type == Event.ONTOUCHEND | |||
&& now - lastTouchEventTime < 500 | |||
&& lastTouchEventRow == event.getCell().getRowIndex() | |||
&& Math.abs(lastTouchEventX - currentX) < 20 | |||
&& Math.abs(lastTouchEventY - currentY) < 20; | |||
if (type == Event.ONTOUCHSTART) { | |||
lastTouchEventX = currentX; | |||
lastTouchEventY = currentY; | |||
} | |||
if (type == Event.ONTOUCHEND) { | |||
lastTouchEventTime = now; | |||
lastTouchEventRow = event.getCell().getRowIndex(); | |||
} | |||
return validTouchOpenEvent; | |||
} | |||
/** | |||
* Returns whether the given event should open the editor. The default | |||
* implementation returns true if and only if the event is a doubleclick or | |||
* if it is a keydown event and the keycode is {@link #KEYCODE_OPEN}. | |||
* | |||
* @param event | |||
* the received event | |||
* @return true if the event is an open event, false otherwise | |||
*/ | |||
protected boolean isOpenEvent(EditorDomEvent<T> event) { | |||
final Event e = event.getDomEvent(); | |||
return e.getTypeInt() == Event.ONDBLCLICK | |||
|| (e.getTypeInt() == Event.ONKEYDOWN | |||
&& e.getKeyCode() == KEYCODE_OPEN) | |||
|| isTouchOpenEvent(event); | |||
} | |||
/** | |||
* Opens the editor on the appropriate row if the received event is an open | |||
* event. The default implementation uses | |||
* {@link #isOpenEvent(EditorDomEvent) isOpenEvent}. | |||
* | |||
* @param event | |||
* the received event | |||
* @return true if this method handled the event and nothing else should be | |||
* done, false otherwise | |||
*/ | |||
protected boolean handleOpenEvent(EditorDomEvent<T> event) { | |||
if (isOpenEvent(event)) { | |||
final EventCellReference<T> cell = event.getCell(); | |||
editRow(event, cell.getRowIndex(), cell.getColumnIndexDOM()); | |||
event.getDomEvent().preventDefault(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Moves the editor to another row or another column if the received event | |||
* is a move event. The default implementation moves the editor to the | |||
* clicked row if the event is a click; otherwise, if the event is a keydown | |||
* and the keycode is {@link #KEYCODE_MOVE_VERTICAL}, moves the editor one | |||
* row up or down if the shift key is pressed or not, respectively. Keydown | |||
* event with keycode {@link #KEYCODE_MOVE_HORIZONTAL} moves the editor left | |||
* or right if shift key is pressed or not, respectively. | |||
* | |||
* @param event | |||
* the received event | |||
* @return true if this method handled the event and nothing else should be | |||
* done, false otherwise | |||
*/ | |||
protected boolean handleMoveEvent(EditorDomEvent<T> event) { | |||
Event e = event.getDomEvent(); | |||
final EventCellReference<T> cell = event.getCell(); | |||
// TODO: Move on touch events | |||
if (e.getTypeInt() == Event.ONCLICK) { | |||
editRow(event, cell.getRowIndex(), cell.getColumnIndexDOM()); | |||
return true; | |||
} | |||
else if (e.getTypeInt() == Event.ONKEYDOWN) { | |||
int rowDelta = 0; | |||
int colDelta = 0; | |||
if (e.getKeyCode() == KEYCODE_MOVE_VERTICAL) { | |||
rowDelta = (e.getShiftKey() ? -1 : +1); | |||
} else if (e.getKeyCode() == KEYCODE_MOVE_HORIZONTAL) { | |||
colDelta = (e.getShiftKey() ? -1 : +1); | |||
// Prevent tab out of Grid Editor | |||
event.getDomEvent().preventDefault(); | |||
} | |||
final boolean changed = rowDelta != 0 || colDelta != 0; | |||
if (changed) { | |||
int columnCount = event.getGrid().getVisibleColumns().size(); | |||
int colIndex = event.getFocusedColumnIndex() + colDelta; | |||
int rowIndex = event.getRowIndex(); | |||
// Handle row change with horizontal move when column goes out | |||
// of range. | |||
if (rowDelta == 0) { | |||
if (colIndex >= columnCount | |||
&& rowIndex < event.getGrid().getDataSource().size() | |||
- 1) { | |||
rowDelta = 1; | |||
colIndex = 0; | |||
} else if (colIndex < 0 && rowIndex > 0) { | |||
rowDelta = -1; | |||
colIndex = columnCount - 1; | |||
} | |||
} | |||
editRow(event, rowIndex + rowDelta, colIndex); | |||
} | |||
return changed; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Moves the editor to another column if the received event is a move event. | |||
* By default the editor is moved on a keydown event with keycode | |||
* {@link #KEYCODE_MOVE_HORIZONTAL}. This moves the editor left or right if | |||
* shift key is pressed or not, respectively. | |||
* | |||
* @param event | |||
* the received event | |||
* @return true if this method handled the event and nothing else should be | |||
* done, false otherwise | |||
*/ | |||
protected boolean handleBufferedMoveEvent(EditorDomEvent<T> event) { | |||
Event e = event.getDomEvent(); | |||
if (e.getType().equals(BrowserEvents.CLICK) | |||
&& event.getRowIndex() == event.getCell().getRowIndex()) { | |||
editRow(event, event.getRowIndex(), | |||
event.getCell().getColumnIndexDOM()); | |||
return true; | |||
} else if (e.getType().equals(BrowserEvents.KEYDOWN) | |||
&& e.getKeyCode() == KEYCODE_MOVE_HORIZONTAL) { | |||
// Prevent tab out of Grid Editor | |||
event.getDomEvent().preventDefault(); | |||
editRow(event, event.getRowIndex(), event.getFocusedColumnIndex() | |||
+ (e.getShiftKey() ? -1 : +1)); | |||
return true; | |||
} else if (e.getType().equals(BrowserEvents.KEYDOWN) | |||
&& e.getKeyCode() == KEYCODE_BUFFERED_SAVE) { | |||
triggerValueChangeEvent(event); | |||
// Save and close. | |||
event.getGrid().getEditor().save(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Returns whether the given event should close the editor. The default | |||
* implementation returns true if and only if the event is a keydown event | |||
* and the keycode is {@link #KEYCODE_CLOSE}. | |||
* | |||
* @param event | |||
* the received event | |||
* @return true if the event is a close event, false otherwise | |||
*/ | |||
protected boolean isCloseEvent(EditorDomEvent<T> event) { | |||
final Event e = event.getDomEvent(); | |||
return e.getTypeInt() == Event.ONKEYDOWN | |||
&& e.getKeyCode() == KEYCODE_CLOSE; | |||
} | |||
/** | |||
* Closes the editor if the received event is a close event. The default | |||
* implementation uses {@link #isCloseEvent(EditorDomEvent) isCloseEvent}. | |||
* | |||
* @param event | |||
* the received event | |||
* @return true if this method handled the event and nothing else should be | |||
* done, false otherwise | |||
*/ | |||
protected boolean handleCloseEvent(EditorDomEvent<T> event) { | |||
if (isCloseEvent(event)) { | |||
event.getEditor().cancel(); | |||
FocusUtil.setFocus(event.getGrid(), true); | |||
return true; | |||
} | |||
return false; | |||
} | |||
protected void editRow(EditorDomEvent<T> event, int rowIndex, | |||
int colIndex) { | |||
int rowCount = event.getGrid().getDataSource().size(); | |||
// Limit rowIndex between 0 and rowCount - 1 | |||
rowIndex = Math.max(0, Math.min(rowCount - 1, rowIndex)); | |||
int colCount = event.getGrid().getVisibleColumns().size(); | |||
// Limit colIndex between 0 and colCount - 1 | |||
colIndex = Math.max(0, Math.min(colCount - 1, colIndex)); | |||
if (rowIndex != event.getRowIndex()) { | |||
triggerValueChangeEvent(event); | |||
} | |||
event.getEditor().editRow(rowIndex, colIndex); | |||
} | |||
/** | |||
* Triggers a value change event from the editor field if it has focus. This | |||
* is based on the assumption that editor field will fire the value change | |||
* when a blur event occurs. | |||
* | |||
* @param event | |||
* the editor DOM event | |||
*/ | |||
private void triggerValueChangeEvent(EditorDomEvent<T> event) { | |||
// Force a blur to cause a value change event | |||
Widget editorWidget = event.getEditorWidget(); | |||
if (editorWidget != null) { | |||
Element focusedElement = WidgetUtil.getFocusedElement(); | |||
if (editorWidget.getElement().isOrHasChild(focusedElement)) { | |||
focusedElement.blur(); | |||
focusedElement.focus(); | |||
} | |||
} | |||
} | |||
@Override | |||
public boolean handleEvent(EditorDomEvent<T> event) { | |||
final Editor<T> editor = event.getEditor(); | |||
final boolean isBody = event.getCell().isBody(); | |||
final boolean handled; | |||
if (event.getGrid().isEditorActive()) { | |||
handled = handleCloseEvent(event) | |||
|| (!editor.isBuffered() && isBody | |||
&& handleMoveEvent(event)) | |||
|| (editor.isBuffered() && isBody | |||
&& handleBufferedMoveEvent(event)); | |||
} else { | |||
handled = event.getGrid().isEnabled() && isBody | |||
&& handleOpenEvent(event); | |||
} | |||
// Buffered mode should swallow all events, if not already handled. | |||
boolean swallowEvent = event.getGrid().isEditorActive() | |||
&& editor.isBuffered(); | |||
return handled || swallowEvent; | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.user.client.ui.Widget; | |||
/** | |||
* A callback interface for generating details for a particular row in Grid. | |||
* | |||
* @since 7.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface DetailsGenerator { | |||
/** A details generator that provides no details */ | |||
public static final DetailsGenerator NULL = new DetailsGenerator() { | |||
@Override | |||
public Widget getDetails(int rowIndex) { | |||
return null; | |||
} | |||
}; | |||
/** | |||
* This method is called for whenever a new details row needs to be | |||
* generated. | |||
* | |||
* @param rowIndex | |||
* the index of the row for which to generate details | |||
* @return the details for the given row, or <code>null</code> to leave the | |||
* details empty. | |||
*/ | |||
Widget getDetails(int rowIndex); | |||
} |
@@ -0,0 +1,174 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import java.util.Collection; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* An interface for binding widgets and data to the grid row editor. Used by the | |||
* editor to support different row types, data sources and custom data binding | |||
* mechanisms. | |||
* | |||
* @param <T> | |||
* the row data type | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface EditorHandler<T> { | |||
/** | |||
* A request class passed as a parameter to the editor handler methods. The | |||
* request is callback-based to facilitate usage with remote or otherwise | |||
* asynchronous data sources. | |||
* <p> | |||
* An implementation must call either {@link #success()} or {@link #fail()}, | |||
* according to whether the operation was a success or failed during | |||
* execution, respectively. | |||
* | |||
* @param <T> | |||
* the row data type | |||
*/ | |||
public interface EditorRequest<T> { | |||
/** | |||
* Returns the index of the row being requested. | |||
* | |||
* @return the row index | |||
*/ | |||
public int getRowIndex(); | |||
/** | |||
* Returns the index of the column being focused. | |||
* | |||
* @return the column index | |||
*/ | |||
public int getColumnIndex(); | |||
/** | |||
* Returns the row data related to the row being requested. | |||
* | |||
* @return the row data | |||
*/ | |||
public T getRow(); | |||
/** | |||
* Returns the grid instance related to this editor request. | |||
* | |||
* @return the grid instance | |||
*/ | |||
public Grid<T> getGrid(); | |||
/** | |||
* Returns the editor widget used to edit the values of the given | |||
* column. | |||
* | |||
* @param column | |||
* the column whose widget to get | |||
* @return the widget related to the column | |||
*/ | |||
public Widget getWidget(Grid.Column<?, T> column); | |||
/** | |||
* Informs Grid that the editor request was a success. | |||
*/ | |||
public void success(); | |||
/** | |||
* Informs Grid that an error occurred while trying to process the | |||
* request. | |||
* | |||
* @param errorMessage | |||
* and error message to show to the user, or | |||
* <code>null</code> to not show any message. | |||
* @param errorColumns | |||
* a collection of columns for which an error indicator | |||
* should be shown, or <code>null</code> if no columns should | |||
* be marked as erroneous. | |||
*/ | |||
public void failure(String errorMessage, | |||
Collection<Grid.Column<?, T>> errorColumns); | |||
/** | |||
* Checks whether the request is completed or not. | |||
* | |||
* @return <code>true</code> iff the request is completed | |||
*/ | |||
public boolean isCompleted(); | |||
} | |||
/** | |||
* Binds row data to the editor widgets. Called by the editor when it is | |||
* opened for editing. | |||
* <p> | |||
* The implementation <em>must</em> call either | |||
* {@link EditorRequest#success()} or | |||
* {@link EditorRequest#failure(String, Collection)} to signal a successful | |||
* or a failed (respectively) bind action. | |||
* | |||
* @param request | |||
* the data binding request | |||
* | |||
* @see Grid#editRow(int) | |||
*/ | |||
public void bind(EditorRequest<T> request); | |||
/** | |||
* Called by the editor when editing is cancelled. This method may have an | |||
* empty implementation in case no special processing is required. | |||
* <p> | |||
* In contrast to {@link #bind(EditorRequest)} and | |||
* {@link #save(EditorRequest)}, any calls to | |||
* {@link EditorRequest#success()} or | |||
* {@link EditorRequest#failure(String, Collection)} have no effect on the | |||
* outcome of the cancel action. The editor is already closed when this | |||
* method is called. | |||
* | |||
* @param request | |||
* the cancel request | |||
* | |||
* @see Grid#cancelEditor() | |||
*/ | |||
public void cancel(EditorRequest<T> request); | |||
/** | |||
* Commits changes in the currently active edit to the data source. Called | |||
* by the editor when changes are saved. | |||
* <p> | |||
* The implementation <em>must</em> call either | |||
* {@link EditorRequest#success()} or {@link EditorRequest#fail()} to signal | |||
* a successful or a failed (respectively) save action. | |||
* | |||
* @param request | |||
* the save request | |||
* | |||
* @see Grid#saveEditor() | |||
*/ | |||
public void save(EditorRequest<T> request); | |||
/** | |||
* Returns a widget instance that is used to edit the values in the given | |||
* column. A null return value means the column is not editable. | |||
* | |||
* @param column | |||
* the column whose values should be edited | |||
* @return the editor widget for the column or null if the column is not | |||
* editable | |||
*/ | |||
public Widget getWidget(Grid.Column<?, T> column); | |||
} |
@@ -0,0 +1,128 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.dom.client.TableCellElement; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.escalator.Cell; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.Column; | |||
/** | |||
* A data class which contains information which identifies a cell being the | |||
* target of an event from {@link Grid}. | |||
* <p> | |||
* Since this class follows the <code>Flyweight</code>-pattern any instance of | |||
* this object is subject to change without the user knowing it and so should | |||
* not be stored anywhere outside of the method providing these instances. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class EventCellReference<T> extends CellReference<T> { | |||
private Section section; | |||
private TableCellElement element; | |||
public EventCellReference(Grid<T> grid) { | |||
super(new RowReference<T>(grid)); | |||
} | |||
/** | |||
* Sets the RowReference and CellReference to point to given Cell. | |||
* | |||
* @param targetCell | |||
* cell to point to | |||
*/ | |||
public void set(Cell targetCell, Section section) { | |||
Grid<T> grid = getGrid(); | |||
int columnIndexDOM = targetCell.getColumn(); | |||
Column<?, T> column = null; | |||
if (columnIndexDOM >= 0 | |||
&& columnIndexDOM < grid.getVisibleColumns().size()) { | |||
column = grid.getVisibleColumns().get(columnIndexDOM); | |||
} | |||
int row = targetCell.getRow(); | |||
// Row objects only make sense for body section of Grid. | |||
T rowObject; | |||
if (section == Section.BODY && row >= 0 | |||
&& row < grid.getDataSource().size()) { | |||
rowObject = grid.getDataSource().getRow(row); | |||
} else { | |||
rowObject = null; | |||
} | |||
// At least for now we don't need to have the actual TableRowElement | |||
// available. | |||
getRowReference().set(row, rowObject, null); | |||
int columnIndex = grid.getColumns().indexOf(column); | |||
set(columnIndexDOM, columnIndex, column); | |||
this.element = targetCell.getElement(); | |||
this.section = section; | |||
} | |||
@Override | |||
public TableCellElement getElement() { | |||
return element; | |||
} | |||
/** | |||
* Is the cell reference for a cell in the header of the Grid. | |||
* | |||
* @since 7.5 | |||
* @return <code>true</true> if referenced cell is in the header, | |||
* <code>false</code> if not | |||
*/ | |||
public boolean isHeader() { | |||
return section == Section.HEADER; | |||
} | |||
/** | |||
* Is the cell reference for a cell in the body of the Grid. | |||
* | |||
* @since 7.5 | |||
* @return <code>true</true> if referenced cell is in the body, | |||
* <code>false</code> if not | |||
*/ | |||
public boolean isBody() { | |||
return section == Section.BODY; | |||
} | |||
/** | |||
* Is the cell reference for a cell in the footer of the Grid. | |||
* | |||
* @since 7.5 | |||
* @return <code>true</true> if referenced cell is in the footer, | |||
* <code>false</code> if not | |||
*/ | |||
public boolean isFooter() { | |||
return section == Section.FOOTER; | |||
} | |||
/** | |||
* Gets the Grid section where the referenced cell is. | |||
* | |||
* @since 7.5 | |||
* @return grid section | |||
*/ | |||
public Section getSection() { | |||
return section; | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
/** | |||
* {@link DetailsGenerator} that is aware of content heights. | |||
* <p> | |||
* <b>FOR INTERNAL USE ONLY!</b> This class exists only for the sake of a | |||
* temporary workaround and might be removed or renamed at any time. | |||
* </p> | |||
* | |||
* @since 7.6.1 | |||
* @author Vaadin Ltd | |||
*/ | |||
@Deprecated | |||
public interface HeightAwareDetailsGenerator extends DetailsGenerator { | |||
/** | |||
* This method is called for whenever a details row's height needs to be | |||
* calculated. | |||
* <p> | |||
* <b>FOR INTERNAL USE ONLY!</b> This method exists only for the sake of a | |||
* temporary workaround and might be removed or renamed at any time. | |||
* </p> | |||
* | |||
* @since 7.6.1 | |||
* @param rowIndex | |||
* the index of the row for which to calculate details row height | |||
* @return height of the details row | |||
*/ | |||
public double getDetailsHeight(int rowIndex); | |||
} |
@@ -0,0 +1,93 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.dom.client.TableCellElement; | |||
import com.vaadin.v7.client.widget.escalator.FlyweightCell; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* A data class which contains information which identifies a cell being | |||
* rendered in a {@link Grid}. | |||
* <p> | |||
* Since this class follows the <code>Flyweight</code>-pattern any instance of | |||
* this object is subject to change without the user knowing it and so should | |||
* not be stored anywhere outside of the method providing these instances. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class RendererCellReference extends CellReference<Object> { | |||
/** | |||
* Creates a new renderer cell reference bound to a row reference. | |||
* | |||
* @param rowReference | |||
* the row reference to bind to | |||
*/ | |||
public RendererCellReference(RowReference<Object> rowReference) { | |||
super(rowReference); | |||
} | |||
private FlyweightCell cell; | |||
/** | |||
* Sets the identifying information for this cell. | |||
* | |||
* @param cell | |||
* the flyweight cell to reference | |||
* @param columnIndex | |||
* the index of the column in the grid, including hidden cells | |||
* @param column | |||
* the column to reference | |||
*/ | |||
public void set(FlyweightCell cell, int columnIndex, | |||
Grid.Column<?, ?> column) { | |||
this.cell = cell; | |||
super.set(cell.getColumn(), columnIndex, | |||
(Grid.Column<?, Object>) column); | |||
} | |||
/** | |||
* Returns the element of the cell. Can be either a <code>TD</code> element | |||
* or a <code>TH</code> element. | |||
* | |||
* @return the element of the cell | |||
*/ | |||
@Override | |||
public TableCellElement getElement() { | |||
return cell.getElement(); | |||
} | |||
/** | |||
* Sets the colspan attribute of the element of this cell. | |||
* | |||
* @param numberOfCells | |||
* the number of columns that the cell should span | |||
*/ | |||
public void setColSpan(int numberOfCells) { | |||
cell.setColSpan(numberOfCells); | |||
} | |||
/** | |||
* Gets the colspan attribute of the element of this cell. | |||
* | |||
* @return the number of columns that the cell should span | |||
*/ | |||
public int getColSpan() { | |||
return cell.getColSpan(); | |||
} | |||
} |
@@ -0,0 +1,104 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import com.google.gwt.dom.client.TableRowElement; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* A data class which contains information which identifies a row in a | |||
* {@link Grid}. | |||
* <p> | |||
* Since this class follows the <code>Flyweight</code>-pattern any instance of | |||
* this object is subject to change without the user knowing it and so should | |||
* not be stored anywhere outside of the method providing these instances. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* the row object type | |||
* @since 7.4 | |||
*/ | |||
public class RowReference<T> { | |||
private final Grid<T> grid; | |||
private int rowIndex; | |||
private T row; | |||
private TableRowElement element; | |||
/** | |||
* Creates a new row reference for the given grid. | |||
* | |||
* @param grid | |||
* the grid that the row belongs to | |||
*/ | |||
public RowReference(Grid<T> grid) { | |||
this.grid = grid; | |||
} | |||
/** | |||
* Sets the identifying information for this row. | |||
* | |||
* @param rowIndex | |||
* the index of the row | |||
* @param row | |||
* the row object | |||
* @param elemenet | |||
* the element of the row | |||
*/ | |||
public void set(int rowIndex, T row, TableRowElement element) { | |||
this.rowIndex = rowIndex; | |||
this.row = row; | |||
this.element = element; | |||
} | |||
/** | |||
* Gets the grid that contains the referenced row. | |||
* | |||
* @return the grid that contains referenced row | |||
*/ | |||
public Grid<T> getGrid() { | |||
return grid; | |||
} | |||
/** | |||
* Gets the row index of the row. | |||
* | |||
* @return the index of the row | |||
*/ | |||
public int getRowIndex() { | |||
return rowIndex; | |||
} | |||
/** | |||
* Gets the row data object. | |||
* | |||
* @return the row object | |||
*/ | |||
public T getRow() { | |||
return row; | |||
} | |||
/** | |||
* Gets the table row element of the row. | |||
* | |||
* @return the element of the row | |||
*/ | |||
public TableRowElement getElement() { | |||
return element; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid; | |||
import java.io.Serializable; | |||
/** | |||
* Callback interface for generating custom style names for data rows | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* the row type of the target grid | |||
* @see Grid#setRowStyleGenerator(RowStyleGenerator) | |||
* @since 7.4 | |||
*/ | |||
public interface RowStyleGenerator<T> extends Serializable { | |||
/** | |||
* Called by Grid to generate a style name for a row. | |||
* | |||
* @param rowReference | |||
* The row to generate a style for | |||
* @return the style name to add to this row, or {@code null} to not set any | |||
* style | |||
*/ | |||
public abstract String getStyle(RowReference<T> rowReference); | |||
} |
@@ -0,0 +1,465 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.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.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
import java.util.Objects; | |||
import java.util.Set; | |||
import java.util.stream.Stream; | |||
import com.vaadin.client.data.DataChangeHandler; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.v7.client.widget.grid.events.SelectAllEvent; | |||
import com.vaadin.v7.client.widget.grid.events.SelectAllHandler; | |||
/** | |||
* 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. | |||
* | |||
* <p> | |||
* Usage: | |||
* | |||
* <pre> | |||
* 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)); | |||
* </pre> | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ListDataSource<T> implements DataSource<T> { | |||
private class RowHandleImpl extends RowHandle<T> { | |||
private final T row; | |||
public RowHandleImpl(T row) { | |||
this.row = row; | |||
} | |||
@Override | |||
public T getRow() { | |||
/* | |||
* We'll cheat here and don't throw an IllegalStateException even if | |||
* this isn't pinned, because we know that the reference never gets | |||
* stale. | |||
*/ | |||
return row; | |||
} | |||
@Override | |||
public void pin() { | |||
// NOOP, really | |||
} | |||
@Override | |||
public void unpin() throws IllegalStateException { | |||
/* | |||
* Just to make things easier for everyone, we won't throw the | |||
* exception, even in illegal situations. | |||
*/ | |||
} | |||
@Override | |||
protected boolean equalsExplicit(Object obj) { | |||
if (obj instanceof ListDataSource.RowHandleImpl) { | |||
/* | |||
* Java prefers AbstractRemoteDataSource<?>.RowHandleImpl. I | |||
* like the @SuppressWarnings more (keeps the line length in | |||
* check.) | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
RowHandleImpl rhi = (RowHandleImpl) obj; | |||
return SharedUtil.equals(row, rhi.row); | |||
} else { | |||
return false; | |||
} | |||
} | |||
@Override | |||
protected int hashCodeExplicit() { | |||
return row.hashCode(); | |||
} | |||
@Override | |||
public void updateRow() { | |||
getHandlers() | |||
.forEach(dch -> dch.dataUpdated(ds.indexOf(getRow()), 1)); | |||
} | |||
} | |||
/** | |||
* Wraps the datasource list and notifies the change handler of changing to | |||
* the list | |||
*/ | |||
private class ListWrapper implements List<T> { | |||
@Override | |||
public int size() { | |||
return ds.size(); | |||
} | |||
@Override | |||
public boolean isEmpty() { | |||
return ds.isEmpty(); | |||
} | |||
@Override | |||
public boolean contains(Object o) { | |||
return ds.contains(o); | |||
} | |||
@Override | |||
public Iterator<T> iterator() { | |||
return new ListWrapperIterator(ds.iterator()); | |||
} | |||
@Override | |||
public Object[] toArray() { | |||
return ds.toArray(); | |||
} | |||
@Override | |||
@SuppressWarnings("hiding") | |||
public <T> T[] toArray(T[] a) { | |||
return ds.toArray(a); | |||
} | |||
@Override | |||
public boolean add(T e) { | |||
if (ds.add(e)) { | |||
getHandlers().forEach(dch -> dch.dataAdded(ds.size() - 1, 1)); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@Override | |||
public boolean remove(Object o) { | |||
int index = ds.indexOf(o); | |||
if (ds.remove(o)) { | |||
getHandlers().forEach(dch -> dch.dataRemoved(index, 1)); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@Override | |||
public boolean containsAll(Collection<?> c) { | |||
return ds.containsAll(c); | |||
} | |||
@Override | |||
public boolean addAll(Collection<? extends T> c) { | |||
int idx = ds.size(); | |||
if (ds.addAll(c)) { | |||
getHandlers().forEach(dch -> dch.dataAdded(idx, c.size())); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@Override | |||
public boolean addAll(int index, Collection<? extends T> c) { | |||
if (ds.addAll(index, c)) { | |||
getHandlers().forEach(dch -> dch.dataAdded(index, c.size())); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@Override | |||
public boolean removeAll(Collection<?> c) { | |||
if (ds.removeAll(c)) { | |||
getHandlers().forEach(dch -> dch.dataUpdated(0, ds.size())); | |||
getHandlers().forEach(dch -> dch.dataAvailable(0, ds.size())); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@Override | |||
public boolean retainAll(Collection<?> c) { | |||
if (ds.retainAll(c)) { | |||
getHandlers().forEach(dch -> dch.dataUpdated(0, ds.size())); | |||
getHandlers().forEach(dch -> dch.dataAvailable(0, ds.size())); | |||
return true; | |||
} | |||
return false; | |||
} | |||
@Override | |||
public void clear() { | |||
int size = ds.size(); | |||
ds.clear(); | |||
getHandlers().forEach(dch -> dch.dataRemoved(0, size)); | |||
} | |||
@Override | |||
public T get(int index) { | |||
return ds.get(index); | |||
} | |||
@Override | |||
public T set(int index, T element) { | |||
T prev = ds.set(index, element); | |||
getHandlers().forEach(dch -> dch.dataUpdated(index, 1)); | |||
return prev; | |||
} | |||
@Override | |||
public void add(int index, T element) { | |||
ds.add(index, element); | |||
getHandlers().forEach(dch -> dch.dataAdded(index, 1)); | |||
} | |||
@Override | |||
public T remove(int index) { | |||
T removed = ds.remove(index); | |||
getHandlers().forEach(dch -> dch.dataRemoved(index, 1)); | |||
return removed; | |||
} | |||
@Override | |||
public int indexOf(Object o) { | |||
return ds.indexOf(o); | |||
} | |||
@Override | |||
public int lastIndexOf(Object o) { | |||
return ds.lastIndexOf(o); | |||
} | |||
@Override | |||
public ListIterator<T> listIterator() { | |||
// TODO could be implemented by a custom iterator. | |||
throw new UnsupportedOperationException( | |||
"List iterators not supported at this time."); | |||
} | |||
@Override | |||
public ListIterator<T> listIterator(int index) { | |||
// TODO could be implemented by a custom iterator. | |||
throw new UnsupportedOperationException( | |||
"List iterators not supported at this time."); | |||
} | |||
@Override | |||
public List<T> subList(int fromIndex, int toIndex) { | |||
throw new UnsupportedOperationException("Sub lists not supported."); | |||
} | |||
} | |||
/** | |||
* Iterator returned by {@link ListWrapper} | |||
*/ | |||
private class ListWrapperIterator implements Iterator<T> { | |||
private final Iterator<T> iterator; | |||
/** | |||
* Constructs a new iterator | |||
*/ | |||
public ListWrapperIterator(Iterator<T> iterator) { | |||
this.iterator = iterator; | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return iterator.hasNext(); | |||
} | |||
@Override | |||
public T next() { | |||
return iterator.next(); | |||
} | |||
@Override | |||
public void remove() { | |||
throw new UnsupportedOperationException( | |||
"Iterator.remove() is not supported by this iterator."); | |||
} | |||
} | |||
/** | |||
* Datasource for providing row pojo's | |||
*/ | |||
private final List<T> ds; | |||
/** | |||
* Wrapper that wraps the data source | |||
*/ | |||
private final ListWrapper wrapper; | |||
/** | |||
* Handler for listening to changes in the underlying list. | |||
*/ | |||
private Set<DataChangeHandler> changeHandlers = new LinkedHashSet<>(); | |||
/** | |||
* Constructs a new list data source. | |||
* <p> | |||
* Note: Modifications to the original list will not be reflected in the | |||
* 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 | |||
*/ | |||
public ListDataSource(List<T> datasource) { | |||
if (datasource == null) { | |||
throw new IllegalArgumentException("datasource cannot be null"); | |||
} | |||
ds = new ArrayList<T>(datasource); | |||
wrapper = new ListWrapper(); | |||
} | |||
/** | |||
* 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 | |||
*/ | |||
public ListDataSource(T... rows) { | |||
if (rows == null) { | |||
ds = new ArrayList<T>(); | |||
} else { | |||
ds = new ArrayList<T>(Arrays.asList(rows)); | |||
} | |||
wrapper = new ListWrapper(); | |||
} | |||
@Override | |||
public void ensureAvailability(int firstRowIndex, int numberOfRows) { | |||
if (firstRowIndex >= ds.size()) { | |||
throw new IllegalStateException( | |||
"Trying to fetch rows outside of array"); | |||
} | |||
getHandlers() | |||
.forEach(dch -> dch.dataAvailable(firstRowIndex, numberOfRows)); | |||
} | |||
@Override | |||
public T getRow(int rowIndex) { | |||
return ds.get(rowIndex); | |||
} | |||
@Override | |||
public int size() { | |||
return ds.size(); | |||
} | |||
@Override | |||
public Registration addDataChangeHandler( | |||
DataChangeHandler dataChangeHandler) { | |||
Objects.requireNonNull(dataChangeHandler, | |||
"DataChangeHandler can't be null"); | |||
changeHandlers.add(dataChangeHandler); | |||
return () -> changeHandlers.remove(dataChangeHandler); | |||
} | |||
/** | |||
* Gets the list that backs this datasource. Any changes made to this list | |||
* will be reflected in the datasource. | |||
* <p> | |||
* 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. | |||
*/ | |||
public List<T> asList() { | |||
return wrapper; | |||
} | |||
@Override | |||
public RowHandle<T> getHandle(T row) throws IllegalStateException { | |||
assert ds.contains(row) : "This data source doesn't contain the row " | |||
+ 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<T> comparator) { | |||
Collections.sort(ds, comparator); | |||
getHandlers().forEach(dch -> dch.dataUpdated(0, ds.size())); | |||
} | |||
/** | |||
* Retrieves the index for given row object. | |||
* <p> | |||
* <em>Note:</em> This method does not verify that the given row object | |||
* exists at all in this DataSource. | |||
* | |||
* @param row | |||
* the row object | |||
* @return index of the row; or <code>-1</code> if row is not available | |||
*/ | |||
public int indexOf(T row) { | |||
return ds.indexOf(row); | |||
} | |||
/** | |||
* Returns a {@link SelectAllHandler} for this ListDataSource. | |||
* | |||
* @return select all handler | |||
*/ | |||
public SelectAllHandler<T> getSelectAllHandler() { | |||
return new SelectAllHandler<T>() { | |||
@Override | |||
public void onSelectAll(SelectAllEvent<T> event) { | |||
event.getSelectionModel().select(asList()); | |||
} | |||
}; | |||
} | |||
private Stream<DataChangeHandler> getHandlers() { | |||
Set<DataChangeHandler> copy = new LinkedHashSet<>(changeHandlers); | |||
return copy.stream(); | |||
} | |||
} |
@@ -0,0 +1,176 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.datasources; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.shared.data.sort.SortDirection; | |||
import com.vaadin.v7.client.widget.grid.sort.SortEvent; | |||
import com.vaadin.v7.client.widget.grid.sort.SortHandler; | |||
import com.vaadin.v7.client.widget.grid.sort.SortOrder; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Provides sorting facility from Grid for the {@link ListDataSource} in-memory | |||
* data source. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* Grid row data type | |||
* @since 7.4 | |||
*/ | |||
public class ListSorter<T> { | |||
private Grid<T> grid; | |||
private Map<Grid.Column<?, T>, Comparator<?>> comparators; | |||
private HandlerRegistration sortHandlerRegistration; | |||
public ListSorter(Grid<T> grid) { | |||
if (grid == null) { | |||
throw new IllegalArgumentException("Grid can not be null"); | |||
} | |||
this.grid = grid; | |||
comparators = new HashMap<Grid.Column<?, T>, Comparator<?>>(); | |||
sortHandlerRegistration = grid.addSortHandler(new SortHandler<T>() { | |||
@Override | |||
public void sort(SortEvent<T> event) { | |||
ListSorter.this.sort(event.getOrder()); | |||
} | |||
}); | |||
} | |||
/** | |||
* Detach this Sorter from the Grid. This unregisters the sort event handler | |||
* which was used to apply sorting to the ListDataSource. | |||
*/ | |||
public void removeFromGrid() { | |||
sortHandlerRegistration.removeHandler(); | |||
} | |||
/** | |||
* Assign or remove a comparator for a column. This comparator method, if | |||
* defined, is always used in favour of 'natural' comparison of objects | |||
* (i.e. the compareTo of objects implementing the Comparable interface, | |||
* which includes all standard data classes like String, Number derivatives | |||
* and Dates). Any existing comparator can be removed by passing in a | |||
* non-null GridColumn and a null Comparator. | |||
* | |||
* @param column | |||
* a grid column. May not be null. | |||
* @param comparator | |||
* comparator method for the values returned by the grid column. | |||
* If null, any existing comparator is removed. | |||
*/ | |||
public <C> void setComparator(Grid.Column<C, T> column, | |||
Comparator<C> comparator) { | |||
if (column == null) { | |||
throw new IllegalArgumentException( | |||
"Column reference can not be null"); | |||
} | |||
if (comparator == null) { | |||
comparators.remove(column); | |||
} else { | |||
comparators.put(column, comparator); | |||
} | |||
} | |||
/** | |||
* Retrieve the comparator assigned for a specific grid column. | |||
* | |||
* @param column | |||
* a grid column. May not be null. | |||
* @return a comparator, or null if no comparator for the specified grid | |||
* column has been set. | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public <C> Comparator<C> getComparator(Grid.Column<C, T> column) { | |||
if (column == null) { | |||
throw new IllegalArgumentException( | |||
"Column reference can not be null"); | |||
} | |||
return (Comparator<C>) comparators.get(column); | |||
} | |||
/** | |||
* Remove all comparator mappings. Useful if the data source has changed but | |||
* this Sorter is being re-used. | |||
*/ | |||
public void clearComparators() { | |||
comparators.clear(); | |||
} | |||
/** | |||
* Apply sorting to the current ListDataSource. | |||
* | |||
* @param order | |||
* the sort order list provided by the grid sort event | |||
*/ | |||
private void sort(final List<SortOrder> order) { | |||
DataSource<T> ds = grid.getDataSource(); | |||
if (!(ds instanceof ListDataSource)) { | |||
throw new IllegalStateException( | |||
"Grid " + grid + " data source is not a ListDataSource!"); | |||
} | |||
((ListDataSource<T>) ds).sort(new Comparator<T>() { | |||
@Override | |||
@SuppressWarnings({ "rawtypes", "unchecked" }) | |||
public int compare(T a, T b) { | |||
for (SortOrder o : order) { | |||
Grid.Column column = o.getColumn(); | |||
Comparator cmp = ListSorter.this.comparators.get(column); | |||
int result = 0; | |||
Object value_a = column.getValue(a); | |||
Object value_b = column.getValue(b); | |||
if (cmp != null) { | |||
result = cmp.compare(value_a, value_b); | |||
} else { | |||
if (!(value_a instanceof Comparable)) { | |||
throw new IllegalStateException("Column " + column | |||
+ " has no assigned comparator and value " | |||
+ value_a + " isn't naturally comparable"); | |||
} | |||
result = ((Comparable) value_a).compareTo(value_b); | |||
} | |||
if (result != 0) { | |||
return o.getDirection() == SortDirection.ASCENDING | |||
? result : -result; | |||
} | |||
} | |||
if (order.size() > 0) { | |||
return order.get(0) | |||
.getDirection() == SortDirection.ASCENDING | |||
? a.hashCode() - b.hashCode() | |||
: b.hashCode() - a.hashCode(); | |||
} | |||
return a.hashCode() - b.hashCode(); | |||
} | |||
}); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridKeyEvent; | |||
/** | |||
* Base interface of all handlers for {@link AbstractGridKeyEvent}s. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public abstract interface AbstractGridKeyEventHandler extends EventHandler { | |||
public abstract interface GridKeyDownHandler | |||
extends AbstractGridKeyEventHandler { | |||
public void onKeyDown(GridKeyDownEvent event); | |||
} | |||
public abstract interface GridKeyUpHandler | |||
extends AbstractGridKeyEventHandler { | |||
public void onKeyUp(GridKeyUpEvent event); | |||
} | |||
public abstract interface GridKeyPressHandler | |||
extends AbstractGridKeyEventHandler { | |||
public void onKeyPress(GridKeyPressEvent event); | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridMouseEvent; | |||
/** | |||
* Base interface of all handlers for {@link AbstractGridMouseEvent}s. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public abstract interface AbstractGridMouseEventHandler extends EventHandler { | |||
public abstract interface GridClickHandler | |||
extends AbstractGridMouseEventHandler { | |||
public void onClick(GridClickEvent event); | |||
} | |||
public abstract interface GridDoubleClickHandler | |||
extends AbstractGridMouseEventHandler { | |||
public void onDoubleClick(GridDoubleClickEvent event); | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridClickHandler; | |||
/** | |||
* Handler for {@link GridClickEvent}s that happen in the body of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface BodyClickHandler extends GridClickHandler { | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridDoubleClickHandler; | |||
/** | |||
* Handler for {@link GridDoubleClickEvent}s that happen in the body of the | |||
* Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface BodyDoubleClickHandler extends GridDoubleClickHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyDownHandler; | |||
/** | |||
* Handler for {@link GridKeyDownEvent}s that happen when the focused cell is in | |||
* the body of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface BodyKeyDownHandler extends GridKeyDownHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyPressHandler; | |||
/** | |||
* Handler for {@link GridKeyPressEvent}s that happen when the focused cell is | |||
* in the body of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface BodyKeyPressHandler extends GridKeyPressHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyUpHandler; | |||
/** | |||
* Handler for {@link GridKeyUpEvent}s that happen when the focused cell is in | |||
* the body of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface BodyKeyUpHandler extends GridKeyUpHandler { | |||
} |
@@ -0,0 +1,51 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
/** | |||
* An event for notifying that the columns in the Grid have been reordered. | |||
* | |||
* @param <T> | |||
* 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.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ColumnReorderEvent<T> extends GwtEvent<ColumnReorderHandler<T>> { | |||
/** | |||
* Handler type. | |||
*/ | |||
private final static Type<ColumnReorderHandler<?>> TYPE = new Type<ColumnReorderHandler<?>>(); | |||
public static final Type<ColumnReorderHandler<?>> getType() { | |||
return TYPE; | |||
} | |||
@SuppressWarnings({ "rawtypes", "unchecked" }) | |||
@Override | |||
public Type<ColumnReorderHandler<T>> getAssociatedType() { | |||
return (Type) TYPE; | |||
} | |||
@Override | |||
protected void dispatch(ColumnReorderHandler<T> handler) { | |||
handler.onColumnReorder(this); | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for a Grid column reorder event, called when the Grid's columns has | |||
* been reordered. | |||
* | |||
* @param <T> | |||
* 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.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface ColumnReorderHandler<T> extends EventHandler { | |||
/** | |||
* A column reorder event, fired by Grid when the columns of the Grid have | |||
* been reordered. | |||
* | |||
* @param event | |||
* column reorder event | |||
*/ | |||
public void onColumnReorder(ColumnReorderEvent<T> event); | |||
} |
@@ -0,0 +1,67 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.v7.client.widgets.Grid.Column; | |||
/** | |||
* An event for notifying that the columns in the Grid have been resized. | |||
* | |||
* @param <T> | |||
* 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.6 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ColumnResizeEvent<T> extends GwtEvent<ColumnResizeHandler<T>> { | |||
/** | |||
* Handler type. | |||
*/ | |||
private final static Type<ColumnResizeHandler<?>> TYPE = new Type<ColumnResizeHandler<?>>(); | |||
private Column<?, T> column; | |||
/** | |||
* @param column | |||
*/ | |||
public ColumnResizeEvent(Column<?, T> column) { | |||
this.column = column; | |||
} | |||
public static final Type<ColumnResizeHandler<?>> getType() { | |||
return TYPE; | |||
} | |||
@SuppressWarnings({ "rawtypes", "unchecked" }) | |||
@Override | |||
public Type<ColumnResizeHandler<T>> getAssociatedType() { | |||
return (Type) TYPE; | |||
} | |||
@Override | |||
protected void dispatch(ColumnResizeHandler<T> handler) { | |||
handler.onColumnResize(this); | |||
} | |||
/** | |||
* @return the column | |||
*/ | |||
public Column<?, T> getColumn() { | |||
return column; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for a Grid column resize event, called when the Grid's columns has | |||
* been resized. | |||
* | |||
* @param <T> | |||
* 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.6 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface ColumnResizeHandler<T> extends EventHandler { | |||
/** | |||
* A column resize event, fired by Grid when the columns of the Grid have | |||
* been resized. | |||
* | |||
* @param event | |||
* column resize event | |||
*/ | |||
public void onColumnResize(ColumnResizeEvent<T> event); | |||
} |
@@ -0,0 +1,93 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.v7.client.widgets.Grid.Column; | |||
/** | |||
* An event for notifying that the columns in the Grid's have changed | |||
* visibility. | |||
* | |||
* @param <T> | |||
* 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.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ColumnVisibilityChangeEvent<T> | |||
extends GwtEvent<ColumnVisibilityChangeHandler<T>> { | |||
private final static Type<ColumnVisibilityChangeHandler<?>> TYPE = new Type<ColumnVisibilityChangeHandler<?>>(); | |||
public static final Type<ColumnVisibilityChangeHandler<?>> getType() { | |||
return TYPE; | |||
} | |||
private final Column<?, T> column; | |||
private final boolean userOriginated; | |||
private final boolean hidden; | |||
public ColumnVisibilityChangeEvent(Column<?, T> column, boolean hidden, | |||
boolean userOriginated) { | |||
this.column = column; | |||
this.hidden = hidden; | |||
this.userOriginated = userOriginated; | |||
} | |||
/** | |||
* Returns the column where the visibility change occurred. | |||
* | |||
* @return the column where the visibility change occurred. | |||
*/ | |||
public Column<?, T> getColumn() { | |||
return column; | |||
} | |||
/** | |||
* Was the column set hidden or visible. | |||
* | |||
* @return <code>true</code> if the column was hidden <code>false</code> if | |||
* it was set visible | |||
*/ | |||
public boolean isHidden() { | |||
return hidden; | |||
} | |||
/** | |||
* Is the visibility change triggered by user. | |||
* | |||
* @return <code>true</code> if the change was triggered by user, | |||
* <code>false</code> if not | |||
*/ | |||
public boolean isUserOriginated() { | |||
return userOriginated; | |||
} | |||
@SuppressWarnings({ "rawtypes", "unchecked" }) | |||
@Override | |||
public com.google.gwt.event.shared.GwtEvent.Type<ColumnVisibilityChangeHandler<T>> getAssociatedType() { | |||
return (Type) TYPE; | |||
} | |||
@Override | |||
protected void dispatch(ColumnVisibilityChangeHandler<T> handler) { | |||
handler.onVisibilityChange(this); | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for a Grid column visibility change event, called when the Grid's | |||
* columns have changed visibility to hidden or visible. | |||
* | |||
* @param<T> 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.5.0 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface ColumnVisibilityChangeHandler<T> extends EventHandler { | |||
/** | |||
* A column visibility change event, fired by Grid when a column in the Grid | |||
* has changed visibility. | |||
* | |||
* @param event | |||
* column visibility change event | |||
*/ | |||
public void onVisibilityChange(ColumnVisibilityChangeEvent<T> event); | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridClickHandler; | |||
/** | |||
* Handler for {@link GridClickEvent}s that happen in the footer of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface FooterClickHandler extends GridClickHandler { | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridDoubleClickHandler; | |||
/** | |||
* Handler for {@link GridDoubleClickEvent}s that happen in the footer of the | |||
* Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface FooterDoubleClickHandler extends GridDoubleClickHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyDownHandler; | |||
/** | |||
* Handler for {@link GridKeyDownEvent}s that happen when the focused cell is in | |||
* the footer of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface FooterKeyDownHandler extends GridKeyDownHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyPressHandler; | |||
/** | |||
* Handler for {@link GridKeyPressEvent}s that happen when the focused cell is | |||
* in the footer of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface FooterKeyPressHandler extends GridKeyPressHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyUpHandler; | |||
/** | |||
* Handler for {@link GridKeyUpEvent}s that happen when the focused cell is in | |||
* the footer of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface FooterKeyUpHandler extends GridKeyUpHandler { | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridClickHandler; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridMouseEvent; | |||
/** | |||
* Represents native mouse click event in Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridClickEvent extends AbstractGridMouseEvent<GridClickHandler> { | |||
public GridClickEvent(Grid<?> grid, CellReference<?> targetCell) { | |||
super(grid, targetCell); | |||
} | |||
@Override | |||
protected String getBrowserEventType() { | |||
return BrowserEvents.CLICK; | |||
} | |||
@Override | |||
protected void doDispatch(GridClickHandler handler, Section section) { | |||
if ((section == Section.BODY && handler instanceof BodyClickHandler) | |||
|| (section == Section.HEADER | |||
&& handler instanceof HeaderClickHandler) | |||
|| (section == Section.FOOTER | |||
&& handler instanceof FooterClickHandler)) { | |||
handler.onClick(this); | |||
} | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridDoubleClickHandler; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridMouseEvent; | |||
/** | |||
* Represents native mouse double click event in Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridDoubleClickEvent | |||
extends AbstractGridMouseEvent<GridDoubleClickHandler> { | |||
public GridDoubleClickEvent(Grid<?> grid, CellReference<?> targetCell) { | |||
super(grid, targetCell); | |||
} | |||
@Override | |||
protected String getBrowserEventType() { | |||
return BrowserEvents.DBLCLICK; | |||
} | |||
@Override | |||
protected void doDispatch(GridDoubleClickHandler handler, Section section) { | |||
if ((section == Section.BODY | |||
&& handler instanceof BodyDoubleClickHandler) | |||
|| (section == Section.HEADER | |||
&& handler instanceof HeaderDoubleClickHandler) | |||
|| (section == Section.FOOTER | |||
&& handler instanceof FooterDoubleClickHandler)) { | |||
handler.onDoubleClick(this); | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
/** | |||
* An enabled/disabled event, fired by the Grid when it is disabled or enabled. | |||
* | |||
* @since 7.7 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridEnabledEvent extends GwtEvent<GridEnabledHandler> { | |||
/** | |||
* The type of this event | |||
*/ | |||
public static final Type<GridEnabledHandler> TYPE = new Type<GridEnabledHandler>(); | |||
private final boolean enabled; | |||
public GridEnabledEvent(boolean enabled) { | |||
this.enabled = enabled; | |||
} | |||
@Override | |||
public Type<GridEnabledHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
protected void dispatch(final GridEnabledHandler handler) { | |||
handler.onEnabled(enabled); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for a Grid enabled/disabled event, called when the Grid is enabled or | |||
* disabled. | |||
* | |||
* @since 7.7 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface GridEnabledHandler extends EventHandler { | |||
/** | |||
* Called when Grid is enabled or disabled. | |||
* | |||
* @param enabled | |||
* true if status changes from disabled to enabled, otherwise | |||
* false. | |||
*/ | |||
public void onEnabled(boolean enabled); | |||
} |
@@ -0,0 +1,123 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.google.gwt.event.dom.client.KeyCodes; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyDownHandler; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridKeyEvent; | |||
/** | |||
* Represents native key down event in Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridKeyDownEvent extends AbstractGridKeyEvent<GridKeyDownHandler> { | |||
public GridKeyDownEvent(Grid<?> grid, CellReference<?> targetCell) { | |||
super(grid, targetCell); | |||
} | |||
@Override | |||
protected void doDispatch(GridKeyDownHandler handler, Section section) { | |||
if ((section == Section.BODY && handler instanceof BodyKeyDownHandler) | |||
|| (section == Section.HEADER | |||
&& handler instanceof HeaderKeyDownHandler) | |||
|| (section == Section.FOOTER | |||
&& handler instanceof FooterKeyDownHandler)) { | |||
handler.onKeyDown(this); | |||
} | |||
} | |||
@Override | |||
protected String getBrowserEventType() { | |||
return BrowserEvents.KEYDOWN; | |||
} | |||
/** | |||
* Does the key code represent an arrow key? | |||
* | |||
* @param keyCode | |||
* the key code | |||
* @return if it is an arrow key code | |||
*/ | |||
public static boolean isArrow(int keyCode) { | |||
switch (keyCode) { | |||
case KeyCodes.KEY_DOWN: | |||
case KeyCodes.KEY_RIGHT: | |||
case KeyCodes.KEY_UP: | |||
case KeyCodes.KEY_LEFT: | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
/** | |||
* Gets the native key code. These key codes are enumerated in the | |||
* {@link KeyCodes} class. | |||
* | |||
* @return the key code | |||
*/ | |||
public int getNativeKeyCode() { | |||
return getNativeEvent().getKeyCode(); | |||
} | |||
/** | |||
* Is this a key down arrow? | |||
* | |||
* @return whether this is a down arrow key event | |||
*/ | |||
public boolean isDownArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_DOWN; | |||
} | |||
/** | |||
* Is this a left arrow? | |||
* | |||
* @return whether this is a left arrow key event | |||
*/ | |||
public boolean isLeftArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_LEFT; | |||
} | |||
/** | |||
* Is this a right arrow? | |||
* | |||
* @return whether this is a right arrow key event | |||
*/ | |||
public boolean isRightArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_RIGHT; | |||
} | |||
/** | |||
* Is this a up arrow? | |||
* | |||
* @return whether this is a right arrow key event | |||
*/ | |||
public boolean isUpArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_UP; | |||
} | |||
@Override | |||
public String toDebugString() { | |||
return super.toDebugString() + "[" + getNativeKeyCode() + "]"; | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyPressHandler; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridKeyEvent; | |||
/** | |||
* Represents native key press event in Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridKeyPressEvent | |||
extends AbstractGridKeyEvent<GridKeyPressHandler> { | |||
public GridKeyPressEvent(Grid<?> grid, CellReference<?> targetCell) { | |||
super(grid, targetCell); | |||
} | |||
@Override | |||
protected void doDispatch(GridKeyPressHandler handler, Section section) { | |||
if ((section == Section.BODY && handler instanceof BodyKeyPressHandler) | |||
|| (section == Section.HEADER | |||
&& handler instanceof HeaderKeyPressHandler) | |||
|| (section == Section.FOOTER | |||
&& handler instanceof FooterKeyPressHandler)) { | |||
handler.onKeyPress(this); | |||
} | |||
} | |||
@Override | |||
protected String getBrowserEventType() { | |||
return BrowserEvents.KEYPRESS; | |||
} | |||
/** | |||
* Gets the char code for this event. | |||
* | |||
* @return the char code | |||
*/ | |||
public char getCharCode() { | |||
return (char) getUnicodeCharCode(); | |||
} | |||
/** | |||
* Gets the Unicode char code (code point) for this event. | |||
* | |||
* @return the Unicode char code | |||
*/ | |||
public int getUnicodeCharCode() { | |||
return getNativeEvent().getCharCode(); | |||
} | |||
@Override | |||
public String toDebugString() { | |||
return super.toDebugString() + "[" + getCharCode() + "]"; | |||
} | |||
} |
@@ -0,0 +1,123 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.google.gwt.event.dom.client.KeyCodes; | |||
import com.vaadin.shared.ui.grid.GridConstants.Section; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyUpHandler; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
import com.vaadin.v7.client.widgets.Grid.AbstractGridKeyEvent; | |||
/** | |||
* Represents native key up event in Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class GridKeyUpEvent extends AbstractGridKeyEvent<GridKeyUpHandler> { | |||
public GridKeyUpEvent(Grid<?> grid, CellReference<?> targetCell) { | |||
super(grid, targetCell); | |||
} | |||
@Override | |||
protected void doDispatch(GridKeyUpHandler handler, Section section) { | |||
if ((section == Section.BODY && handler instanceof BodyKeyUpHandler) | |||
|| (section == Section.HEADER | |||
&& handler instanceof HeaderKeyUpHandler) | |||
|| (section == Section.FOOTER | |||
&& handler instanceof FooterKeyUpHandler)) { | |||
handler.onKeyUp(this); | |||
} | |||
} | |||
@Override | |||
protected String getBrowserEventType() { | |||
return BrowserEvents.KEYUP; | |||
} | |||
/** | |||
* Does the key code represent an arrow key? | |||
* | |||
* @param keyCode | |||
* the key code | |||
* @return if it is an arrow key code | |||
*/ | |||
public static boolean isArrow(int keyCode) { | |||
switch (keyCode) { | |||
case KeyCodes.KEY_DOWN: | |||
case KeyCodes.KEY_RIGHT: | |||
case KeyCodes.KEY_UP: | |||
case KeyCodes.KEY_LEFT: | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
/** | |||
* Gets the native key code. These key codes are enumerated in the | |||
* {@link KeyCodes} class. | |||
* | |||
* @return the key code | |||
*/ | |||
public int getNativeKeyCode() { | |||
return getNativeEvent().getKeyCode(); | |||
} | |||
/** | |||
* Is this a key down arrow? | |||
* | |||
* @return whether this is a down arrow key event | |||
*/ | |||
public boolean isDownArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_DOWN; | |||
} | |||
/** | |||
* Is this a left arrow? | |||
* | |||
* @return whether this is a left arrow key event | |||
*/ | |||
public boolean isLeftArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_LEFT; | |||
} | |||
/** | |||
* Is this a right arrow? | |||
* | |||
* @return whether this is a right arrow key event | |||
*/ | |||
public boolean isRightArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_RIGHT; | |||
} | |||
/** | |||
* Is this a up arrow? | |||
* | |||
* @return whether this is a right arrow key event | |||
*/ | |||
public boolean isUpArrow() { | |||
return getNativeKeyCode() == KeyCodes.KEY_UP; | |||
} | |||
@Override | |||
public String toDebugString() { | |||
return super.toDebugString() + "[" + getNativeKeyCode() + "]"; | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridClickHandler; | |||
/** | |||
* Handler for {@link GridClickEvent}s that happen in the header of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface HeaderClickHandler extends GridClickHandler { | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridMouseEventHandler.GridDoubleClickHandler; | |||
/** | |||
* Handler for {@link GridDoubleClickEvent}s that happen in the header of the | |||
* Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface HeaderDoubleClickHandler extends GridDoubleClickHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyDownHandler; | |||
/** | |||
* Handler for {@link GridKeyDownEvent}s that happen when the focused cell is in | |||
* the header of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface HeaderKeyDownHandler extends GridKeyDownHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyPressHandler; | |||
/** | |||
* Handler for {@link GridKeyPressEvent}s that happen when the focused cell is | |||
* in the header of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface HeaderKeyPressHandler extends GridKeyPressHandler { | |||
} |
@@ -0,0 +1,28 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.vaadin.v7.client.widget.grid.events.AbstractGridKeyEventHandler.GridKeyUpHandler; | |||
/** | |||
* Handler for {@link GridKeyUpEvent}s that happen when the focused cell is in | |||
* the header of the Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface HeaderKeyUpHandler extends GridKeyUpHandler { | |||
} |
@@ -0,0 +1,40 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
/** | |||
* An event that signifies that a scrollbar bundle has been scrolled | |||
* | |||
* @author Vaadin Ltd | |||
* @since 7.4 | |||
*/ | |||
public class ScrollEvent extends GwtEvent<ScrollHandler> { | |||
/** The type of this event */ | |||
public static final Type<ScrollHandler> TYPE = new Type<ScrollHandler>(); | |||
@Override | |||
public Type<ScrollHandler> getAssociatedType() { | |||
return TYPE; | |||
} | |||
@Override | |||
protected void dispatch(final ScrollHandler handler) { | |||
handler.onScroll(this); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* A handler that gets called whenever a scrollbar bundle is scrolled | |||
* | |||
* @author Vaadin Ltd | |||
* @since 7.4 | |||
*/ | |||
public interface ScrollHandler extends EventHandler { | |||
/** | |||
* A callback method that is called once a scrollbar bundle has been | |||
* scrolled. | |||
* | |||
* @param event | |||
* the scroll event | |||
*/ | |||
public void onScroll(ScrollEvent event); | |||
} |
@@ -0,0 +1,59 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel; | |||
/** | |||
* A select all event, fired by the Grid when it needs all rows in data source | |||
* to be selected. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class SelectAllEvent<T> extends GwtEvent<SelectAllHandler<T>> { | |||
/** | |||
* Handler type. | |||
*/ | |||
private final static Type<SelectAllHandler<?>> TYPE = new Type<SelectAllHandler<?>>();; | |||
private SelectionModel.Multi<T> selectionModel; | |||
public SelectAllEvent(SelectionModel.Multi<T> selectionModel) { | |||
this.selectionModel = selectionModel; | |||
} | |||
public static final Type<SelectAllHandler<?>> getType() { | |||
return TYPE; | |||
} | |||
@SuppressWarnings({ "rawtypes", "unchecked" }) | |||
@Override | |||
public Type<SelectAllHandler<T>> getAssociatedType() { | |||
return (Type) TYPE; | |||
} | |||
@Override | |||
protected void dispatch(SelectAllHandler<T> handler) { | |||
handler.onSelectAll(this); | |||
} | |||
public SelectionModel.Multi<T> getSelectionModel() { | |||
return selectionModel; | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.events; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for a Grid select all event, called when the Grid needs all rows in | |||
* data source to be selected. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public interface SelectAllHandler<T> extends EventHandler { | |||
/** | |||
* Called when select all value in SelectionColumn header changes value. | |||
* | |||
* @param event | |||
* select all event telling that all rows should be selected | |||
*/ | |||
public void onSelectAll(SelectAllEvent<T> event); | |||
} |
@@ -0,0 +1,66 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import com.vaadin.client.data.DataSource.RowHandle; | |||
/** | |||
* An abstract class that adds a consistent API for common methods that's needed | |||
* by Vaadin's server-based selection models to work. | |||
* <p> | |||
* <em>Note:</em> This should be an interface instead of an abstract class, if | |||
* only we could define protected methods in an interface. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* The grid's row type | |||
* @since 7.4 | |||
*/ | |||
public abstract class AbstractRowHandleSelectionModel<T> | |||
implements SelectionModel<T> { | |||
/** | |||
* Select a row, based on its | |||
* {@link com.vaadin.client.data.DataSource.RowHandle RowHandle}. | |||
* <p> | |||
* <em>Note:</em> this method may not fire selection change events. | |||
* | |||
* @param handle | |||
* the handle to select by | |||
* @return <code>true</code> iff the selection state was changed by this | |||
* call | |||
* @throws UnsupportedOperationException | |||
* if the selection model does not support either handles or | |||
* selection | |||
*/ | |||
protected abstract boolean selectByHandle(RowHandle<T> handle); | |||
/** | |||
* Deselect a row, based on its | |||
* {@link com.vaadin.client.data.DataSource.RowHandle RowHandle}. | |||
* <p> | |||
* <em>Note:</em> this method may not fire selection change events. | |||
* | |||
* @param handle | |||
* the handle to deselect by | |||
* @return <code>true</code> iff the selection state was changed by this | |||
* call | |||
* @throws UnsupportedOperationException | |||
* if the selection model does not support either handles or | |||
* deselection | |||
*/ | |||
protected abstract boolean deselectByHandle(RowHandle<T> handle) | |||
throws UnsupportedOperationException; | |||
} |
@@ -0,0 +1,77 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.vaadin.v7.client.widget.grid.events.BodyClickHandler; | |||
import com.vaadin.v7.client.widget.grid.events.GridClickEvent; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Generic class to perform selections when clicking on cells in body of Grid. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ClickSelectHandler<T> { | |||
private Grid<T> grid; | |||
private HandlerRegistration clickHandler; | |||
private boolean deselectAllowed = true; | |||
private class RowClickHandler implements BodyClickHandler { | |||
@Override | |||
public void onClick(GridClickEvent event) { | |||
T row = (T) event.getTargetCell().getRow(); | |||
if (!grid.isSelected(row)) { | |||
grid.select(row); | |||
} else if (deselectAllowed) { | |||
grid.deselect(row); | |||
} | |||
} | |||
} | |||
/** | |||
* Constructor for ClickSelectHandler. This constructor will add all | |||
* necessary handlers for selecting rows by clicking cells. | |||
* | |||
* @param grid | |||
* grid to attach to | |||
*/ | |||
public ClickSelectHandler(Grid<T> grid) { | |||
this.grid = grid; | |||
clickHandler = grid.addBodyClickHandler(new RowClickHandler()); | |||
} | |||
/** | |||
* Clean up function for removing all now obsolete handlers. | |||
*/ | |||
public void removeHandler() { | |||
clickHandler.removeHandler(); | |||
} | |||
/** | |||
* Sets whether clicking the currently selected row should deselect the row. | |||
* | |||
* @param deselectAllowed | |||
* <code>true</code> to allow deselecting the selected row; | |||
* otherwise <code>false</code> | |||
*/ | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
this.deselectAllowed = deselectAllowed; | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
/** | |||
* Marker interface for widgets that fires selection events. | |||
* | |||
* @author Vaadin Ltd | |||
* @since 7.4 | |||
*/ | |||
public interface HasSelectionHandlers<T> { | |||
/** | |||
* Register a selection change handler. | |||
* <p> | |||
* This handler is called whenever a | |||
* {@link com.vaadin.ui.components.grid.selection.SelectionModel | |||
* SelectionModel} detects a change in selection state. | |||
* | |||
* @param handler | |||
* a {@link SelectionHandler} | |||
* @return a handler registration object, which can be used to remove the | |||
* handler. | |||
*/ | |||
public HandlerRegistration addSelectionHandler(SelectionHandler<T> handler); | |||
} |
@@ -0,0 +1,781 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import com.google.gwt.animation.client.AnimationScheduler; | |||
import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; | |||
import com.google.gwt.animation.client.AnimationScheduler.AnimationHandle; | |||
import com.google.gwt.core.client.GWT; | |||
import com.google.gwt.dom.client.BrowserEvents; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.dom.client.NativeEvent; | |||
import com.google.gwt.dom.client.TableElement; | |||
import com.google.gwt.dom.client.TableSectionElement; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.google.gwt.event.dom.client.MouseDownEvent; | |||
import com.google.gwt.event.dom.client.MouseDownHandler; | |||
import com.google.gwt.event.dom.client.TouchStartEvent; | |||
import com.google.gwt.event.dom.client.TouchStartHandler; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
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.google.gwt.user.client.ui.CheckBox; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.v7.client.renderers.ClickableRenderer; | |||
import com.vaadin.v7.client.widget.grid.CellReference; | |||
import com.vaadin.v7.client.widget.grid.RendererCellReference; | |||
import com.vaadin.v7.client.widget.grid.events.GridEnabledEvent; | |||
import com.vaadin.v7.client.widget.grid.events.GridEnabledHandler; | |||
import com.vaadin.v7.client.widget.grid.selection.SelectionModel.Multi.Batched; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Renderer showing multi selection check boxes. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* the type of the associated grid | |||
* @since 7.4 | |||
*/ | |||
public class MultiSelectionRenderer<T> | |||
extends ClickableRenderer<Boolean, CheckBox> { | |||
private static final String SELECTION_CHECKBOX_CLASSNAME = "-selection-checkbox"; | |||
/** The size of the autoscroll area, both top and bottom. */ | |||
private static final int SCROLL_AREA_GRADIENT_PX = 100; | |||
/** The maximum number of pixels per second to autoscroll. */ | |||
private static final int SCROLL_TOP_SPEED_PX_SEC = 500; | |||
/** | |||
* The minimum area where the grid doesn't scroll while the pointer is | |||
* pressed. | |||
*/ | |||
private static final int MIN_NO_AUTOSCROLL_AREA_PX = 50; | |||
/** | |||
* Handler for MouseDown and TouchStart events for selection checkboxes. | |||
* | |||
* @since 7.5 | |||
*/ | |||
private final class CheckBoxEventHandler implements MouseDownHandler, | |||
TouchStartHandler, ClickHandler, GridEnabledHandler { | |||
private final CheckBox checkBox; | |||
/** | |||
* @param checkBox | |||
* checkbox widget for this handler | |||
*/ | |||
private CheckBoxEventHandler(CheckBox checkBox) { | |||
this.checkBox = checkBox; | |||
} | |||
@Override | |||
public void onMouseDown(MouseDownEvent event) { | |||
if (checkBox.isEnabled()) { | |||
if (event.getNativeButton() == NativeEvent.BUTTON_LEFT) { | |||
startDragSelect(event.getNativeEvent(), | |||
checkBox.getElement()); | |||
} | |||
} | |||
} | |||
@Override | |||
public void onTouchStart(TouchStartEvent event) { | |||
if (checkBox.isEnabled()) { | |||
startDragSelect(event.getNativeEvent(), checkBox.getElement()); | |||
} | |||
} | |||
@Override | |||
public void onClick(ClickEvent event) { | |||
// Clicking is already handled with MultiSelectionRenderer | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
} | |||
@Override | |||
public void onEnabled(boolean enabled) { | |||
checkBox.setEnabled(enabled); | |||
} | |||
} | |||
/** | |||
* This class's main objective is to listen when to stop autoscrolling, and | |||
* make sure everything stops accordingly. | |||
*/ | |||
private class TouchEventHandler implements NativePreviewHandler { | |||
@Override | |||
public void onPreviewNativeEvent(final NativePreviewEvent event) { | |||
switch (event.getTypeInt()) { | |||
case Event.ONTOUCHSTART: { | |||
if (event.getNativeEvent().getTouches().length() == 1) { | |||
/* | |||
* Something has dropped a touchend/touchcancel and the | |||
* scroller is most probably running amok. Let's cancel it | |||
* and pretend that everything's going as expected | |||
* | |||
* Because this is a preview, this code is run before the | |||
* event handler in MultiSelectionRenderer.onBrowserEvent. | |||
* Therefore, we can simply kill everything and let that | |||
* method restart things as they should. | |||
*/ | |||
autoScrollHandler.stop(); | |||
/* | |||
* Related TODO: investigate why iOS seems to ignore a | |||
* touchend/touchcancel when frames are dropped, and/or if | |||
* something can be done about that. | |||
*/ | |||
} | |||
break; | |||
} | |||
case Event.ONTOUCHMOVE: | |||
event.cancel(); | |||
break; | |||
case Event.ONTOUCHEND: | |||
case Event.ONTOUCHCANCEL: | |||
/* | |||
* Remember: targetElement is always where touchstart started, | |||
* not where the finger is pointing currently. | |||
*/ | |||
final Element targetElement = Element | |||
.as(event.getNativeEvent().getEventTarget()); | |||
if (isInFirstColumn(targetElement)) { | |||
removeNativeHandler(); | |||
event.cancel(); | |||
} | |||
break; | |||
} | |||
} | |||
private boolean isInFirstColumn(final Element element) { | |||
if (element == null) { | |||
return false; | |||
} | |||
final Element tbody = getTbodyElement(); | |||
if (tbody == null || !tbody.isOrHasChild(element)) { | |||
return false; | |||
} | |||
/* | |||
* The null-parent in the while clause is in the case where element | |||
* is an immediate tr child in the tbody. Should never happen in | |||
* internal code, but hey... | |||
*/ | |||
Element cursor = element; | |||
while (cursor.getParentElement() != null | |||
&& cursor.getParentElement().getParentElement() != tbody) { | |||
cursor = cursor.getParentElement(); | |||
} | |||
final Element tr = cursor.getParentElement(); | |||
return tr.getFirstChildElement().equals(cursor); | |||
} | |||
} | |||
/** | |||
* This class's responsibility is to | |||
* <ul> | |||
* <li>scroll the table while a pointer is kept in a scrolling zone and | |||
* <li>select rows whenever a pointer is "activated" on a selection cell | |||
* </ul> | |||
* <p> | |||
* <em>Techical note:</em> This class is an AnimationCallback because we | |||
* need a timer: when the finger is kept in place while the grid scrolls, we | |||
* still need to be able to make new selections. So, instead of relying on | |||
* events (which won't be fired, since the pointer isn't necessarily | |||
* moving), we do this check on each frame while the pointer is "active" | |||
* (mouse is pressed, finger is on screen). | |||
*/ | |||
private class AutoScrollerAndSelector implements AnimationCallback { | |||
/** | |||
* If the acceleration gradient area is smaller than this, autoscrolling | |||
* will be disabled (it becomes too quick to accelerate to be usable). | |||
*/ | |||
private static final int GRADIENT_MIN_THRESHOLD_PX = 10; | |||
/** | |||
* The speed at which the gradient area recovers, once scrolling in that | |||
* direction has started. | |||
*/ | |||
private static final int SCROLL_AREA_REBOUND_PX_PER_SEC = 1; | |||
private static final double SCROLL_AREA_REBOUND_PX_PER_MS = SCROLL_AREA_REBOUND_PX_PER_SEC | |||
/ 1000.0d; | |||
/** | |||
* The lowest y-coordinate on the {@link Event#getClientY() client} from | |||
* where we need to start scrolling towards the top. | |||
*/ | |||
private int topBound = -1; | |||
/** | |||
* The highest y-coordinate on the {@link Event#getClientY() client} | |||
* from where we need to scrolling towards the bottom. | |||
*/ | |||
private int bottomBound = -1; | |||
/** | |||
* <code>true</code> if the pointer is selecting, <code>false</code> if | |||
* the pointer is deselecting. | |||
*/ | |||
private final boolean selectionPaint; | |||
/** | |||
* The area where the selection acceleration takes place. If < | |||
* {@link #GRADIENT_MIN_THRESHOLD_PX}, autoscrolling is disabled | |||
*/ | |||
private final int gradientArea; | |||
/** | |||
* The number of pixels per seconds we currently are scrolling (negative | |||
* is towards the top, positive is towards the bottom). | |||
*/ | |||
private double scrollSpeed = 0; | |||
private double prevTimestamp = 0; | |||
/** | |||
* This field stores fractions of pixels to scroll, to make sure that | |||
* we're able to scroll less than one px per frame. | |||
*/ | |||
private double pixelsToScroll = 0.0d; | |||
/** Should this animator be running. */ | |||
private boolean running = false; | |||
/** The handle in which this instance is running. */ | |||
private AnimationHandle handle; | |||
/** The pointer's pageX coordinate of the first click. */ | |||
private int initialPageX = -1; | |||
/** The pointer's pageY coordinate. */ | |||
private int pageY; | |||
/** The logical index of the row that was most recently modified. */ | |||
private int lastModifiedLogicalRow = -1; | |||
/** @see #doScrollAreaChecks(int) */ | |||
private int finalTopBound; | |||
/** @see #doScrollAreaChecks(int) */ | |||
private int finalBottomBound; | |||
private boolean scrollAreaShouldRebound = false; | |||
private final int bodyAbsoluteTop; | |||
private final int bodyAbsoluteBottom; | |||
public AutoScrollerAndSelector(final int topBound, | |||
final int bottomBound, final int gradientArea, | |||
final boolean selectionPaint) { | |||
finalTopBound = topBound; | |||
finalBottomBound = bottomBound; | |||
this.gradientArea = gradientArea; | |||
this.selectionPaint = selectionPaint; | |||
bodyAbsoluteTop = getBodyClientTop(); | |||
bodyAbsoluteBottom = getBodyClientBottom(); | |||
} | |||
@Override | |||
public void execute(final double timestamp) { | |||
final double timeDiff = timestamp - prevTimestamp; | |||
prevTimestamp = timestamp; | |||
reboundScrollArea(timeDiff); | |||
pixelsToScroll += scrollSpeed * (timeDiff / 1000.0d); | |||
final int intPixelsToScroll = (int) pixelsToScroll; | |||
pixelsToScroll -= intPixelsToScroll; | |||
if (intPixelsToScroll != 0) { | |||
grid.setScrollTop(grid.getScrollTop() + intPixelsToScroll); | |||
} | |||
int constrainedPageY = Math.max(bodyAbsoluteTop, | |||
Math.min(bodyAbsoluteBottom, pageY)); | |||
int logicalRow = getLogicalRowIndex(WidgetUtil | |||
.getElementFromPoint(initialPageX, constrainedPageY)); | |||
int incrementOrDecrement = (logicalRow > lastModifiedLogicalRow) ? 1 | |||
: -1; | |||
/* | |||
* Both pageY and initialPageX have their initialized (and | |||
* unupdated) values while the cursor hasn't moved since the first | |||
* invocation. This will lead to logicalRow being -1, until the | |||
* pointer has been moved. | |||
*/ | |||
while (logicalRow != -1 && lastModifiedLogicalRow != logicalRow) { | |||
lastModifiedLogicalRow += incrementOrDecrement; | |||
setSelected(lastModifiedLogicalRow, selectionPaint); | |||
} | |||
reschedule(); | |||
} | |||
/** | |||
* If the scroll are has been offset by the pointer starting out there, | |||
* move it back a bit | |||
*/ | |||
private void reboundScrollArea(double timeDiff) { | |||
if (!scrollAreaShouldRebound) { | |||
return; | |||
} | |||
int reboundPx = (int) Math | |||
.ceil(SCROLL_AREA_REBOUND_PX_PER_MS * timeDiff); | |||
if (topBound < finalTopBound) { | |||
topBound += reboundPx; | |||
topBound = Math.min(topBound, finalTopBound); | |||
updateScrollSpeed(pageY); | |||
} else if (bottomBound > finalBottomBound) { | |||
bottomBound -= reboundPx; | |||
bottomBound = Math.max(bottomBound, finalBottomBound); | |||
updateScrollSpeed(pageY); | |||
} | |||
} | |||
private void updateScrollSpeed(final int pointerPageY) { | |||
final double ratio; | |||
if (pointerPageY < topBound) { | |||
final double distance = pointerPageY - topBound; | |||
ratio = Math.max(-1, distance / gradientArea); | |||
} | |||
else if (pointerPageY > bottomBound) { | |||
final double distance = pointerPageY - bottomBound; | |||
ratio = Math.min(1, distance / gradientArea); | |||
} | |||
else { | |||
ratio = 0; | |||
} | |||
scrollSpeed = ratio * SCROLL_TOP_SPEED_PX_SEC; | |||
} | |||
public void start(int logicalRowIndex) { | |||
running = true; | |||
setSelected(logicalRowIndex, selectionPaint); | |||
lastModifiedLogicalRow = logicalRowIndex; | |||
reschedule(); | |||
} | |||
public void stop() { | |||
running = false; | |||
if (handle != null) { | |||
handle.cancel(); | |||
handle = null; | |||
} | |||
} | |||
private void reschedule() { | |||
if (running && gradientArea >= GRADIENT_MIN_THRESHOLD_PX) { | |||
handle = AnimationScheduler.get().requestAnimationFrame(this, | |||
grid.getElement()); | |||
} | |||
} | |||
public void updatePointerCoords(int pageX, int pageY) { | |||
doScrollAreaChecks(pageY); | |||
updateScrollSpeed(pageY); | |||
this.pageY = pageY; | |||
if (initialPageX == -1) { | |||
initialPageX = pageX; | |||
} | |||
} | |||
/** | |||
* This method checks whether the first pointer event started in an area | |||
* that would start scrolling immediately, and does some actions | |||
* accordingly. | |||
* <p> | |||
* If it is, that scroll area will be offset "beyond" the pointer (above | |||
* if pointer is towards the top, otherwise below). | |||
* <p> | |||
* <span style="font-size:smaller">*) This behavior will change in | |||
* future patches (henrik paul 2.7.2014)</span> | |||
*/ | |||
private void doScrollAreaChecks(int pageY) { | |||
/* | |||
* The first run makes sure that neither scroll position is | |||
* underneath the finger, but offset to either direction from | |||
* underneath the pointer. | |||
*/ | |||
if (topBound == -1) { | |||
topBound = Math.min(finalTopBound, pageY); | |||
bottomBound = Math.max(finalBottomBound, pageY); | |||
} | |||
/* | |||
* Subsequent runs make sure that the scroll area grows (but doesn't | |||
* shrink) with the finger, but no further than the final bound. | |||
*/ | |||
else { | |||
int oldTopBound = topBound; | |||
if (topBound < finalTopBound) { | |||
topBound = Math.max(topBound, | |||
Math.min(finalTopBound, pageY)); | |||
} | |||
int oldBottomBound = bottomBound; | |||
if (bottomBound > finalBottomBound) { | |||
bottomBound = Math.min(bottomBound, | |||
Math.max(finalBottomBound, pageY)); | |||
} | |||
final boolean topDidNotMove = oldTopBound == topBound; | |||
final boolean bottomDidNotMove = oldBottomBound == bottomBound; | |||
final boolean wasVerticalMovement = pageY != this.pageY; | |||
scrollAreaShouldRebound = (topDidNotMove && bottomDidNotMove | |||
&& wasVerticalMovement); | |||
} | |||
} | |||
} | |||
/** | |||
* This class makes sure that pointer movemenets are registered and | |||
* delegated to the autoscroller so that it can: | |||
* <ul> | |||
* <li>modify the speed in which we autoscroll. | |||
* <li>"paint" a new row with the selection. | |||
* </ul> | |||
* Essentially, when a pointer is pressed on the selection column, a native | |||
* preview handler is registered (so that selection gestures can happen | |||
* outside of the selection column). The handler itself makes sure that it's | |||
* detached when the pointer is "lifted". | |||
*/ | |||
private class AutoScrollHandler { | |||
private AutoScrollerAndSelector autoScroller; | |||
/** The registration info for {@link #scrollPreviewHandler} */ | |||
private HandlerRegistration handlerRegistration; | |||
private final NativePreviewHandler scrollPreviewHandler = new NativePreviewHandler() { | |||
@Override | |||
public void onPreviewNativeEvent(final NativePreviewEvent event) { | |||
if (autoScroller == null) { | |||
stop(); | |||
return; | |||
} | |||
final NativeEvent nativeEvent = event.getNativeEvent(); | |||
int pageY = 0; | |||
int pageX = 0; | |||
switch (event.getTypeInt()) { | |||
case Event.ONMOUSEMOVE: | |||
case Event.ONTOUCHMOVE: | |||
pageY = WidgetUtil.getTouchOrMouseClientY(nativeEvent); | |||
pageX = WidgetUtil.getTouchOrMouseClientX(nativeEvent); | |||
autoScroller.updatePointerCoords(pageX, pageY); | |||
break; | |||
case Event.ONMOUSEUP: | |||
case Event.ONTOUCHEND: | |||
case Event.ONTOUCHCANCEL: | |||
stop(); | |||
break; | |||
} | |||
} | |||
}; | |||
/** | |||
* The top bound, as calculated from the {@link Event#getClientY() | |||
* client} coordinates. | |||
*/ | |||
private int topBound = -1; | |||
/** | |||
* The bottom bound, as calculated from the {@link Event#getClientY() | |||
* client} coordinates. | |||
*/ | |||
private int bottomBound = -1; | |||
/** The size of the autoscroll acceleration area. */ | |||
private int gradientArea; | |||
public void start(int logicalRowIndex) { | |||
SelectionModel<T> 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 | |||
* whatnot. | |||
*/ | |||
updateScrollBounds(); | |||
assert handlerRegistration == null : "handlerRegistration was not null"; | |||
assert autoScroller == null : "autoScroller was not null"; | |||
handlerRegistration = Event | |||
.addNativePreviewHandler(scrollPreviewHandler); | |||
autoScroller = new AutoScrollerAndSelector(topBound, bottomBound, | |||
gradientArea, !isSelected(logicalRowIndex)); | |||
autoScroller.start(logicalRowIndex); | |||
} | |||
private void updateScrollBounds() { | |||
final int topBorder = getBodyClientTop(); | |||
final int bottomBorder = getBodyClientBottom(); | |||
topBound = topBorder + SCROLL_AREA_GRADIENT_PX; | |||
bottomBound = bottomBorder - SCROLL_AREA_GRADIENT_PX; | |||
gradientArea = SCROLL_AREA_GRADIENT_PX; | |||
// modify bounds if they're too tightly packed | |||
if (bottomBound - topBound < MIN_NO_AUTOSCROLL_AREA_PX) { | |||
int adjustment = MIN_NO_AUTOSCROLL_AREA_PX | |||
- (bottomBound - topBound); | |||
topBound -= adjustment / 2; | |||
bottomBound += adjustment / 2; | |||
gradientArea -= adjustment / 2; | |||
} | |||
} | |||
public void stop() { | |||
if (handlerRegistration != null) { | |||
handlerRegistration.removeHandler(); | |||
handlerRegistration = null; | |||
} | |||
if (autoScroller != null) { | |||
autoScroller.stop(); | |||
autoScroller = null; | |||
} | |||
SelectionModel<T> model = grid.getSelectionModel(); | |||
if (model instanceof Batched) { | |||
Batched<?> batchedModel = (Batched<?>) model; | |||
batchedModel.commitBatchSelect(); | |||
} | |||
removeNativeHandler(); | |||
} | |||
} | |||
private static final String LOGICAL_ROW_PROPERTY_INT = "vEscalatorLogicalRow"; | |||
private final Grid<T> grid; | |||
private HandlerRegistration nativePreviewHandlerRegistration; | |||
private final AutoScrollHandler autoScrollHandler = new AutoScrollHandler(); | |||
public MultiSelectionRenderer(final Grid<T> grid) { | |||
this.grid = grid; | |||
} | |||
@Override | |||
public void destroy() { | |||
if (nativePreviewHandlerRegistration != null) { | |||
removeNativeHandler(); | |||
} | |||
} | |||
@Override | |||
public CheckBox createWidget() { | |||
final CheckBox checkBox = GWT.create(CheckBox.class); | |||
checkBox.setStylePrimaryName( | |||
grid.getStylePrimaryName() + SELECTION_CHECKBOX_CLASSNAME); | |||
CheckBoxEventHandler handler = new CheckBoxEventHandler(checkBox); | |||
// Sink events | |||
checkBox.sinkBitlessEvent(BrowserEvents.MOUSEDOWN); | |||
checkBox.sinkBitlessEvent(BrowserEvents.TOUCHSTART); | |||
checkBox.sinkBitlessEvent(BrowserEvents.CLICK); | |||
// Add handlers | |||
checkBox.addMouseDownHandler(handler); | |||
checkBox.addTouchStartHandler(handler); | |||
checkBox.addClickHandler(handler); | |||
grid.addHandler(handler, GridEnabledEvent.TYPE); | |||
checkBox.setEnabled(grid.isEnabled()); | |||
return checkBox; | |||
} | |||
@Override | |||
public void render(final RendererCellReference cell, final Boolean data, | |||
CheckBox checkBox) { | |||
checkBox.setValue(data, false); | |||
checkBox.setEnabled(grid.isEnabled() && !grid.isEditorActive()); | |||
checkBox.getElement().setPropertyInt(LOGICAL_ROW_PROPERTY_INT, | |||
cell.getRowIndex()); | |||
} | |||
@Override | |||
public Collection<String> getConsumedEvents() { | |||
final HashSet<String> events = new HashSet<String>(); | |||
/* | |||
* this column's first interest is only to attach a NativePreventHandler | |||
* that does all the magic. These events are the beginning of that | |||
* cycle. | |||
*/ | |||
events.add(BrowserEvents.MOUSEDOWN); | |||
events.add(BrowserEvents.TOUCHSTART); | |||
return events; | |||
} | |||
@Override | |||
public boolean onBrowserEvent(final CellReference<?> cell, | |||
final NativeEvent event) { | |||
if (BrowserEvents.TOUCHSTART.equals(event.getType()) | |||
|| (BrowserEvents.MOUSEDOWN.equals(event.getType()) | |||
&& event.getButton() == NativeEvent.BUTTON_LEFT)) { | |||
startDragSelect(event, Element.as(event.getEventTarget())); | |||
return true; | |||
} else { | |||
throw new IllegalStateException( | |||
"received unexpected event: " + event.getType()); | |||
} | |||
} | |||
private void startDragSelect(NativeEvent event, final Element target) { | |||
injectNativeHandler(); | |||
int logicalRowIndex = getLogicalRowIndex(target); | |||
autoScrollHandler.start(logicalRowIndex); | |||
event.preventDefault(); | |||
event.stopPropagation(); | |||
} | |||
private void injectNativeHandler() { | |||
removeNativeHandler(); | |||
nativePreviewHandlerRegistration = Event | |||
.addNativePreviewHandler(new TouchEventHandler()); | |||
} | |||
private void removeNativeHandler() { | |||
if (nativePreviewHandlerRegistration != null) { | |||
nativePreviewHandlerRegistration.removeHandler(); | |||
nativePreviewHandlerRegistration = null; | |||
} | |||
} | |||
private int getLogicalRowIndex(final Element target) { | |||
if (target == null) { | |||
return -1; | |||
} | |||
/* | |||
* We can't simply go backwards until we find a <tr> first element, | |||
* because of the table-in-table scenario. We need to, unfortunately, go | |||
* up from our known root. | |||
*/ | |||
final Element tbody = getTbodyElement(); | |||
Element tr = tbody.getFirstChildElement(); | |||
while (tr != null) { | |||
if (tr.isOrHasChild(target)) { | |||
final Element td = tr.getFirstChildElement(); | |||
assert td != null : "Cell has disappeared"; | |||
final Element checkbox = td.getFirstChildElement(); | |||
assert checkbox != null : "Checkbox has disappeared"; | |||
return checkbox.getPropertyInt(LOGICAL_ROW_PROPERTY_INT); | |||
} | |||
tr = tr.getNextSiblingElement(); | |||
} | |||
return -1; | |||
} | |||
private TableElement getTableElement() { | |||
final Element root = grid.getElement(); | |||
final Element tablewrapper = Element.as(root.getChild(2)); | |||
if (tablewrapper != null) { | |||
return TableElement.as(tablewrapper.getFirstChildElement()); | |||
} else { | |||
return null; | |||
} | |||
} | |||
private TableSectionElement getTbodyElement() { | |||
TableElement table = getTableElement(); | |||
if (table != null) { | |||
return table.getTBodies().getItem(0); | |||
} else { | |||
return null; | |||
} | |||
} | |||
private TableSectionElement getTheadElement() { | |||
TableElement table = getTableElement(); | |||
if (table != null) { | |||
return table.getTHead(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
private TableSectionElement getTfootElement() { | |||
TableElement table = getTableElement(); | |||
if (table != null) { | |||
return table.getTFoot(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
/** Get the "top" of an element in relation to "client" coordinates. */ | |||
private int getClientTop(final Element e) { | |||
return e.getAbsoluteTop(); | |||
} | |||
private int getBodyClientBottom() { | |||
return getClientTop(getTfootElement()) - 1; | |||
} | |||
private int getBodyClientTop() { | |||
// Off by one pixel miscalculation. possibly border related. | |||
return getClientTop(grid.getElement()) | |||
+ getTheadElement().getOffsetHeight() + 1; | |||
} | |||
protected boolean isSelected(final int logicalRow) { | |||
return grid.isSelected(grid.getDataSource().getRow(logicalRow)); | |||
} | |||
protected void setSelected(final int logicalRow, final boolean select) { | |||
T row = grid.getDataSource().getRow(logicalRow); | |||
if (select) { | |||
grid.select(row); | |||
} else { | |||
grid.deselect(row); | |||
} | |||
} | |||
} |
@@ -0,0 +1,178 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import com.google.gwt.event.shared.GwtEvent; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Event object describing a change in Grid row selection state. | |||
* | |||
* @since 7.4 | |||
* @author Vaadin Ltd | |||
*/ | |||
@SuppressWarnings("rawtypes") | |||
public class SelectionEvent<T> extends GwtEvent<SelectionHandler> { | |||
private static final Type<SelectionHandler> eventType = new Type<SelectionHandler>(); | |||
private final Grid<T> grid; | |||
private final List<T> added; | |||
private final List<T> removed; | |||
private final boolean batched; | |||
/** | |||
* Creates an event with a single added or removed row. | |||
* | |||
* @param grid | |||
* grid reference, used for getSource | |||
* @param added | |||
* the added row, or <code>null</code> if a row was not added | |||
* @param removed | |||
* the removed row, or <code>null</code> 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 SelectionEvent(Grid<T> grid, T added, T removed, boolean batched) { | |||
this.grid = grid; | |||
this.batched = batched; | |||
if (added != null) { | |||
this.added = Collections.singletonList(added); | |||
} else { | |||
this.added = Collections.emptyList(); | |||
} | |||
if (removed != null) { | |||
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 | |||
* a collection of added rows, or <code>null</code> if no rows | |||
* were added | |||
* @param removed | |||
* a collection of removed rows, or <code>null</code> 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 SelectionEvent(Grid<T> grid, Collection<T> added, | |||
Collection<T> removed, boolean batched) { | |||
this.grid = grid; | |||
this.batched = batched; | |||
if (added != null) { | |||
this.added = new ArrayList<T>(added); | |||
} else { | |||
this.added = Collections.emptyList(); | |||
} | |||
if (removed != null) { | |||
this.removed = new ArrayList<T>(removed); | |||
} else { | |||
this.removed = Collections.emptyList(); | |||
} | |||
} | |||
/** | |||
* Gets a reference to the Grid object that fired this event. | |||
* | |||
* @return a grid reference | |||
*/ | |||
@Override | |||
public Grid<T> getSource() { | |||
return grid; | |||
} | |||
/** | |||
* Gets all rows added to the selection since the last | |||
* {@link SelectionEvent} . | |||
* | |||
* @return a collection of added rows. Empty collection if no rows were | |||
* added. | |||
*/ | |||
public Collection<T> getAdded() { | |||
return Collections.unmodifiableCollection(added); | |||
} | |||
/** | |||
* Gets all rows removed from the selection since the last | |||
* {@link SelectionEvent}. | |||
* | |||
* @return a collection of removed rows. Empty collection if no rows were | |||
* removed. | |||
*/ | |||
public Collection<T> getRemoved() { | |||
return Collections.unmodifiableCollection(removed); | |||
} | |||
/** | |||
* Gets currently selected rows. | |||
* | |||
* @return a non-null collection containing all currently selected rows. | |||
*/ | |||
public Collection<T> getSelected() { | |||
return grid.getSelectedRows(); | |||
} | |||
/** | |||
* Gets a type identifier for this event. | |||
* | |||
* @return a {@link Type} identifier. | |||
*/ | |||
public static Type<SelectionHandler> getType() { | |||
return eventType; | |||
} | |||
@Override | |||
public Type<SelectionHandler> getAssociatedType() { | |||
return eventType; | |||
} | |||
@Override | |||
@SuppressWarnings("unchecked") | |||
protected void dispatch(SelectionHandler handler) { | |||
handler.onSelect(this); | |||
} | |||
/** | |||
* Checks if this selection change event is fired during a batched | |||
* selection/deselection operation. | |||
* | |||
* @return <code>true</code> iff this event is fired during a batched | |||
* selection/deselection operation | |||
*/ | |||
public boolean isBatchedSelection() { | |||
return batched; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import com.google.gwt.event.shared.EventHandler; | |||
/** | |||
* Handler for {@link SelectionEvent}s. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* The row data type | |||
* @since 7.4 | |||
*/ | |||
public interface SelectionHandler<T> extends EventHandler { | |||
/** | |||
* Called when a selection model's selection state is changed. | |||
* | |||
* @param event | |||
* a selection event, containing info about rows that have been | |||
* added to or removed from the selection. | |||
*/ | |||
public void onSelect(SelectionEvent<T> event); | |||
} |
@@ -0,0 +1,258 @@ | |||
/* | |||
* Copyright 2000-2016 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.v7.client.widget.grid.selection; | |||
import java.util.Collection; | |||
import com.vaadin.v7.client.renderers.Renderer; | |||
import com.vaadin.v7.client.widgets.Grid; | |||
/** | |||
* Common interface for all selection models. | |||
* <p> | |||
* Selection models perform tracking of selected rows in the Grid, as well as | |||
* dispatching events when the selection state changes. | |||
* | |||
* @author Vaadin Ltd | |||
* @param <T> | |||
* Grid's row type | |||
* @since 7.4 | |||
*/ | |||
public interface SelectionModel<T> { | |||
/** | |||
* Return true if the provided row is considered selected under the | |||
* implementing selection model. | |||
* | |||
* @param row | |||
* row object instance | |||
* @return <code>true</code>, if the row given as argument is considered | |||
* selected. | |||
*/ | |||
public boolean isSelected(T row); | |||
/** | |||
* 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<Boolean> getSelectionColumnRenderer(); | |||
/** | |||
* Tells this SelectionModel which Grid it belongs to. | |||
* <p> | |||
* Implementations are free to have this be a no-op. This method is called | |||
* internally by Grid. | |||
* | |||
* @param grid | |||
* a {@link Grid} instance; <code>null</code> when removing from | |||
* Grid | |||
*/ | |||
public void setGrid(Grid<T> grid); | |||
/** | |||
* Resets the SelectionModel to the initial state. | |||
* <p> | |||
* This method can be called internally, for example, when the attached | |||
* Grid's data source changes. | |||
*/ | |||
public void reset(); | |||
/** | |||
* Returns a Collection containing all selected rows. | |||
* | |||
* @return a non-null collection. | |||
*/ | |||
public Collection<T> getSelectedRows(); | |||
/** | |||
* Selection model that allows a maximum of one row to be selected at any | |||
* one time. | |||
* | |||
* @param <T> | |||
* type parameter corresponding with Grid row type | |||
*/ | |||
public interface Single<T> extends SelectionModel<T> { | |||
/** | |||
* Selects a row. | |||
* | |||
* @param row | |||
* a {@link Grid} row object | |||
* @return true, if this row as not previously selected. | |||
*/ | |||
public boolean select(T row); | |||
/** | |||
* Deselects a row. | |||
* <p> | |||
* 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. | |||
*/ | |||
public boolean deselect(T row); | |||
/** | |||
* Returns the currently selected row. | |||
* | |||
* @return a {@link Grid} row object or null, if nothing is selected. | |||
*/ | |||
public T getSelectedRow(); | |||
/** | |||
* Sets whether it's allowed to deselect the selected row through the | |||
* UI. Deselection is allowed by default. | |||
* | |||
* @param deselectAllowed | |||
* <code>true</code> if the selected row can be deselected | |||
* without selecting another row instead; otherwise | |||
* <code>false</code>. | |||
*/ | |||
public void setDeselectAllowed(boolean deselectAllowed); | |||
/** | |||
* Sets whether it's allowed to deselect the selected row through the | |||
* UI. | |||
* | |||
* @return <code>true</code> if deselection is allowed; otherwise | |||
* <code>false</code> | |||
*/ | |||
public boolean isDeselectAllowed(); | |||
} | |||
/** | |||
* Selection model that allows for several rows to be selected at once. | |||
* | |||
* @param <T> | |||
* type parameter corresponding with Grid row type | |||
*/ | |||
public interface Multi<T> extends SelectionModel<T> { | |||
/** | |||
* A multi selection model that can send selections and deselections in | |||
* a batch, instead of committing them one-by-one. | |||
* | |||
* @param <T> | |||
* type parameter corresponding with Grid row type | |||
*/ | |||
public interface Batched<T> extends Multi<T> { | |||
/** | |||
* Starts a batch selection. | |||
* <p> | |||
* 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. | |||
* <p> | |||
* <em>Note:</em> {@link SelectionEvent 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 SelectionEvent#isBatchedSelection()}. | |||
*/ | |||
public void startBatchSelect(); | |||
/** | |||
* Commits and ends a batch selection. | |||
* <p> | |||
* Any and all selections and deselections since the last invocation | |||
* of {@link #startBatchSelect()} will be fired at once as one | |||
* collated {@link SelectionEvent}. | |||
*/ | |||
public void commitBatchSelect(); | |||
/** | |||
* Checks whether or not a batch has been started. | |||
* | |||
* @return <code>true</code> 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<T> getSelectedRowsBatch(); | |||
/** | |||
* Gets all the rows that would become deselected in this batch. | |||
* | |||
* @return a collection of the rows that would become deselected | |||
*/ | |||
public Collection<T> getDeselectedRowsBatch(); | |||
} | |||
/** | |||
* Selects one or more rows. | |||
* | |||
* @param rows | |||
* {@link Grid} row objects | |||
* @return true, if the set of selected rows was changed. | |||
*/ | |||
public boolean select(T... rows); | |||
/** | |||
* Deselects one or more rows. | |||
* | |||
* @param rows | |||
* Grid row objects | |||
* @return true, if the set of selected rows was changed. | |||
*/ | |||
public boolean deselect(T... rows); | |||
/** | |||
* 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. | |||
*/ | |||
public boolean select(Collection<T> rows); | |||
/** | |||
* 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. | |||
*/ | |||
public boolean deselect(Collection<T> rows); | |||
} | |||
/** | |||
* Interface for a selection model that does not allow anything to be | |||
* selected. | |||
* | |||
* @param <T> | |||
* type parameter corresponding with Grid row type | |||
*/ | |||
public interface None<T> extends SelectionModel<T> { | |||
} | |||
} |