123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800 |
- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.vaadin.client.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.TableRowElement;
- 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.KeyCodes;
- import com.google.gwt.event.dom.client.KeyUpEvent;
- import com.google.gwt.event.dom.client.KeyUpHandler;
- 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.VConsole;
- import com.vaadin.client.WidgetUtil;
- import com.vaadin.client.renderers.ClickableRenderer;
- import com.vaadin.client.widget.grid.CellReference;
- import com.vaadin.client.widget.grid.RendererCellReference;
- import com.vaadin.client.widget.grid.events.GridEnabledHandler;
- import com.vaadin.client.widget.grid.events.GridSelectionAllowedEvent;
- import com.vaadin.client.widget.grid.events.GridSelectionAllowedHandler;
- import com.vaadin.client.widgets.Escalator.AbstractRowContainer;
- import com.vaadin.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, GridSelectionAllowedHandler, KeyUpHandler {
- 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 onKeyUp(KeyUpEvent event) {
- if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE
- || !checkBox.isEnabled()) {
- return;
- }
- int logicalRow = getLogicalRowIndex(grid, checkBox.getElement());
- setSelected(logicalRow, !isSelected(logicalRow));
- event.preventDefault();
- event.stopPropagation();
- }
-
- @Override
- public void onEnabled(boolean enabled) {
- updateEnable();
- }
-
- @Override
- public void onSelectionAllowed(GridSelectionAllowedEvent event) {
- updateEnable();
- }
-
- private void updateEnable() {
- checkBox.setEnabled(grid.isEnabled()
- && grid.getSelectionModel().isSelectionAllowed());
- }
- }
-
- /**
- * 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(grid, 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);
- } else {
- /*
- * Subsequent runs make sure that the scroll area grows (but
- * doesn't shrink) with the finger, but no further than the
- * final bound.
- */
- 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 = 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) {
- /*
- * 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;
- }
-
- removeNativeHandler();
- }
- }
-
- 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);
-
- // label of checkbox should only be visible for assistive devices
- checkBox.addStyleName("v-assistive-device-only-label");
-
- // Sink events
- checkBox.sinkBitlessEvent(BrowserEvents.MOUSEDOWN);
- checkBox.sinkBitlessEvent(BrowserEvents.TOUCHSTART);
- checkBox.sinkBitlessEvent(BrowserEvents.CLICK);
- checkBox.sinkBitlessEvent(BrowserEvents.KEYUP);
-
- // Add handlers
- checkBox.addMouseDownHandler(handler);
- checkBox.addTouchStartHandler(handler);
- checkBox.addClickHandler(handler);
- checkBox.addKeyUpHandler(handler);
- grid.addEnabledHandler(handler);
- grid.addSelectionAllowedHandler(handler);
-
- return checkBox;
- }
-
- @Override
- public void render(final RendererCellReference cell, final Boolean data,
- CheckBox checkBox) {
- checkBox.setValue(data, false);
- // this should be a temp fix.
- checkBox.setText("Selects row number " + getDOMRowIndex(cell) + ".");
- boolean editorOpen = grid.isEditorActive();
- boolean editorBuffered = grid.isEditorBuffered();
- checkBox.setEnabled(
- grid.isEnabled() && !(editorOpen && editorBuffered));
- }
-
- private int getDOMRowIndex(RendererCellReference cell) {
- // getRowIndex starts with zero, that's why we add an additional 1.
- // getDOMRowIndex should include getHeaderRows as well, this number
- // should be equals to aria-rowindex.
- return cell.getGrid().getHeaderRowCount() + cell.getRowIndex() + 1;
- }
-
- @Override
- public Collection<String> getConsumedEvents() {
- final HashSet<String> events = new HashSet<>();
-
- /*
- * 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;
- }
- return false;
- }
-
- private void startDragSelect(NativeEvent event, final Element target) {
- injectNativeHandler();
- int logicalRowIndex = getLogicalRowIndex(grid, 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(Grid<T> grid, 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 ((AbstractRowContainer) grid.getEscalator().getBody())
- .getLogicalRowIndex((TableRowElement) tr);
- }
- 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);
- }
- }
- }
|