Package com.googlecode.mgwt.ui.client.widget.panel.scroll.impl

Source Code of com.googlecode.mgwt.ui.client.widget.panel.scroll.impl.ScrollPanelTouchImpl

package com.googlecode.mgwt.ui.client.widget.panel.scroll.impl;

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.JsArray;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEvent;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

import com.googlecode.mgwt.collection.shared.CollectionFactory;
import com.googlecode.mgwt.collection.shared.LightArray;
import com.googlecode.mgwt.collection.shared.LightArrayInt;
import com.googlecode.mgwt.dom.client.event.animation.TransitionEndEvent;
import com.googlecode.mgwt.dom.client.event.animation.TransitionEndHandler;
import com.googlecode.mgwt.dom.client.event.orientation.OrientationChangeEvent;
import com.googlecode.mgwt.dom.client.event.orientation.OrientationChangeHandler;
import com.googlecode.mgwt.dom.client.event.touch.TouchHandler;
import com.googlecode.mgwt.ui.client.MGWT;
import com.googlecode.mgwt.ui.client.util.CssUtil;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.BeforeScrollEndEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.BeforeScrollMoveEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.BeforeScrollStartEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollAnimationEndEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollAnimationMoveEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollAnimationStartEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollEndEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollMoveEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollPanelAppearance;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollPanelAppearance.ScrollPanelCss;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollRefreshEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollStartEvent;
import com.googlecode.mgwt.ui.client.widget.touch.TouchDelegate;

import java.util.Iterator;
import java.util.logging.Logger;

public class ScrollPanelTouchImpl extends ScrollPanelImpl {

  // TODO fix this by refactoring
  private static final ScrollPanelAppearance SPA = GWT.create(ScrollPanelAppearance.class);

  private static Logger logger = Logger.getLogger(ScrollPanelTouchImpl.class.getName());

  private static double ZOOM_MIN = 1;
  private static double ZOOM_MAX = 4;

  private class TouchListener implements TouchHandler {

    @Override
    public void onTouchStart(TouchStartEvent event) {
      if (!listenForStart)
        return;
      start(event);

    }

    @Override
    public void onTouchMove(TouchMoveEvent event) {
      if (!listenForMoveEvent)
        return;
      move(event);

    }

    @Override
    public void onTouchEnd(TouchEndEvent event) {
      if (!listenForEndEvent)
        return;
      end(event);

    }

    @Override
    public void onTouchCancel(TouchCancelEvent event) {
      if (!listenForCancelEvent) {
        return;
      }
      end(event);
    }

  }

  private static class Step {
    private final int x;

    private final int y;
    private int time;

    public Step(int x, int y, int time) {
      this.x = x;
      this.y = y;
      this.time = time;

    }

    public int getX() {
      return x;
    }

    public int getY() {
      return y;
    }

    public int getTime() {
      return time;
    }

    public void setTime(int i) {
      this.time = 0;

    }

    @Override
    public String toString() {
      return "Step [x=" + x + ", y=" + y + ", time=" + time + "]";
    }

  }

  private static class Momentum {

    public static final Momentum ZERO_MOMENTUM = new Momentum(0, 0);

    private final int time;
    private final int dist;

    /**
     *
     */
    public Momentum(int dist, int time) {
      this.dist = dist;
      this.time = time;

    }

    public int getTime() {
      return time;
    }

    public int getDist() {
      return dist;
    }

  }

  private static class Snap {
    private final int x;
    private final int y;
    private final int time;

    public Snap(int x, int y, int time) {
      this.x = x;
      this.y = y;
      this.time = time;

    }

    public int getX() {
      return x;
    }

    public int getY() {
      return y;
    }

    public int getTime() {
      return time;
    }
  }

  private boolean enabled;
  private int x;
  private int y;
  private LightArray<Step> steps;

  private LightArrayInt pagesX;
  private LightArrayInt pagesY;

  private SimplePanel wrapper;
  private Widget scroller;
  private double scale;

  private double zoomMin;
  private double zoomMax;
  private int wrapperHeight;
  private int wrapperWidth;

  // offset from top
  private int topOffset;
  private int minScrollY;
  private int scrollerWidth;
  private int scrollerHeight;
  private int maxScrollX;
  private int maxScrollY;
  private int dirX;
  private int dirY;

  // enable disable horizontal scroll
  private boolean hScroll;
  // enable disable vertical scroll
  private boolean vScroll;
  private boolean bounceLock;
  private boolean hScrollbar;
  private boolean vScrollbar;
  private int wrapperOffsetLeft;
  private int wrapperOffsetTop;
  private boolean moved;
  private boolean zoomed;
  private boolean animating;
  private boolean useTransform;
  private boolean zoom;
  private boolean useTransistion;
  private int distX;
  private int distY;
  private int absDistX;
  private int absDistY;
  private double touchesDistStart;
  private int originX;
  private int originY;
  private boolean momentum;
  private int absStartX;
  private int absStartY;
  private int startX;
  private int startY;
  private int pointX;
  private int pointY;
  private long startTime;
  private double touchesDist;
  private double lastScale;
  private boolean bounce;
  private double bounceFactor;
  private boolean lockDirection;
  private Timer doubleTapTimer;
  private boolean snap;
  private int snapThreshold;
  private boolean wheelActionZoom;
  private int wheelZoomCount;
  protected AnimationHandle aniTime;
  private int currPageX;
  private int currPageY;
  private String snapSelector;
  private TouchListener touchListener;
  private HandlerRegistration transistionEndRegistration;

  private boolean fixedScrollbar;

  private boolean hideScrollBar;

  private boolean fadeScrollBar;

  private ScrollPanelCss css;

  private boolean shouldHandleResize;

  public ScrollPanelTouchImpl() {

    wrapper = new SimplePanel();
    touchDelegate = new TouchDelegate(wrapper);

    touchListener = new TouchListener();
    setupEvents();

    css = SPA.css();
    css.ensureInjected();

    wrapper.addStyleName(css.scrollPanel());

    initWidget(wrapper);

    shouldHandleResize = true;

    enabled = true;
    steps = CollectionFactory.constructArray();
    scale = 1.0;
    currPageX = 0;
    currPageY = 0;
    pagesX = CollectionFactory.constructIntegerArray();
    pagesY = CollectionFactory.constructIntegerArray();
    wheelZoomCount = 0;

    // setup events!

    // setting standard options
    this.hScroll = true;
    this.vScroll = true;
    this.hScrollDesired = true;
    this.vScrollDesired = true;
    this.x = 0;
    this.y = 0;
    this.bounce = true;
    this.bounceFactor = 2.0;
    this.bounceLock = false;
    this.momentum = true;
    this.lockDirection = true;
    setUseTransform(true);
    setUseTransistion(false);
    this.topOffset = 0;

    // Zoom
    setZoom(false);
    this.zoomMin = ZOOM_MIN;
    this.zoomMax = ZOOM_MAX;

    // snap
    this.snap = false;
    this.snapSelector = null;
    this.snapThreshold = 1;

    this.fixedScrollbar = MGWT.getOsDetection().isAndroid() && !MGWT.getOsDetection().isAndroid4_4_OrHigher();
    this.hideScrollBar = true;
    this.fadeScrollBar = MGWT.getOsDetection().isIOs() && CssUtil.has3d();

    // array for scrollbars
    this.scrollBar = new boolean[2];
    this.scrollBarWrapper = new Element[2];
    this.scrollBarIndicator = new Element[2];
    this.scrollBarSize = new int[2];
    this.scrollbarIndicatorSize = new int[2];
    this.scrollbarMaxScroll = new int[2];
    this.scrollbarProp = new double[2];

    this.scrollBar[DIRECTION.HORIZONTAL.ordinal()] = this.hScroll;
    this.scrollBar[DIRECTION.VERTICAL.ordinal()] = this.vScroll;

  }

  public void setUseTransistion(boolean useTransistion) {
    this.useTransistion = CssUtil.hasTransistionEndEvent() && useTransistion;
  }

  public void setUseTransform(boolean useTransform) {
    this.useTransform = CssUtil.hasTransform() && useTransform;
  }

  public void setZoom(boolean zoom) {
    this.zoom = zoom && useTransform;
  }

  private void checkDOMChanges() {
    if (moved || zoomed || animating || (Math.abs(scrollerWidth - scroller.getOffsetWidth() * scale) < 0.01 && Math.abs(scrollerHeight - scroller.getOffsetHeight() * scale) < 0.01)) {
      return;
    }

    refresh();
  }

  private enum DIRECTION {
    HORIZONTAL, VERTICAL
  };

  private boolean[] scrollBar;
  private Element[] scrollBarWrapper;
  private Element[] scrollBarIndicator;

  private int[] scrollBarSize;
  private int[] scrollbarIndicatorSize;
  private int[] scrollbarMaxScroll;
  private double[] scrollbarProp;

  private boolean hScrollDesired;

  private boolean vScrollDesired;

  private LightArrayInt pagesActualX;

  private LightArrayInt pagesActualY;

  private void scrollBar(final DIRECTION direction) {
    final int dir = direction.ordinal();

    if (!scrollBar[dir]) {
      if (scrollBarWrapper[dir] != null) {
        if (CssUtil.hasTransform()) {
          CssUtil.resetTransForm(scrollBarIndicator[dir]);

        }

        if (scrollBarWrapper[dir].getParentNode() != null) {
          scrollBarWrapper[dir].getParentNode().removeChild(scrollBarWrapper[dir]);
        }

        scrollBarWrapper[dir] = null;
        scrollBarIndicator[dir] = null;

      }

      return;
    }

    Element bar;

    if (scrollBarWrapper[dir] == null) {
      // Create the scrollbar wrapper
      bar = DOM.createDiv();

      CssUtil.setTransitionDuration(bar, (this.fadeScrollBar ? 350 : 0));
      bar.getStyle().setOpacity(this.hideScrollBar ? 0 : 1);

      this.scrollBarWrapper[dir] = bar;
      this.scrollBarWrapper[dir].addClassName(css.scrollBar());
      if (direction == DIRECTION.HORIZONTAL) {
        this.scrollBarWrapper[dir].addClassName(css.scrollBarHorizontal());
        this.scrollBarWrapper[dir].getStyle().setRight(this.scrollBar[DIRECTION.VERTICAL.ordinal()] ? 7 : 2, Unit.PX);
      } else {
        this.scrollBarWrapper[dir].addClassName(css.scrollBarVertical());
        this.scrollBarWrapper[dir].getStyle().setBottom(this.scrollBar[DIRECTION.HORIZONTAL.ordinal()] ? 7 : 2, Unit.PX);
      }

      // Create the scrollbar indicator
      bar = DOM.createDiv();
      bar.addClassName(css.scrollBarBar());

      if (direction == DIRECTION.HORIZONTAL) {

        bar.getStyle().setHeight(100, Unit.PCT);

      } else {

        bar.getStyle().setWidth(100, Unit.PCT);
      }

      scrollBarWrapper[dir].appendChild(bar);
      scrollBarIndicator[dir] = bar;
      this.scrollBarIndicator[dir].addClassName(css.scrollBarBar());

    }

    // only append if size fits!
    if (direction == DIRECTION.HORIZONTAL) {
      if (this.wrapperWidth < this.scrollerWidth) {
        this.wrapper.getElement().appendChild(this.scrollBarWrapper[dir]);

      }
    } else {
      if (this.wrapperHeight < this.scrollerHeight) {
        this.wrapper.getElement().appendChild(this.scrollBarWrapper[dir]);

      }
    }

    int delay = MGWT.getOsDetection().isAndroid() ? 200 : 1;
    new Timer() {
      @Override
      public void run() {
        switch (direction) {
          case HORIZONTAL:
            ScrollPanelTouchImpl.this.scrollBarSize[dir] = ScrollPanelTouchImpl.this.scrollBarWrapper[dir].getClientWidth();
            ScrollPanelTouchImpl.this.scrollbarIndicatorSize[dir] =
                (int) Math.max(Math.round((double) (ScrollPanelTouchImpl.this.scrollBarSize[dir] * ScrollPanelTouchImpl.this.scrollBarSize[dir]) / ScrollPanelTouchImpl.this.scrollerWidth), 8);
            ScrollPanelTouchImpl.this.scrollBarIndicator[dir].getStyle().setWidth(ScrollPanelTouchImpl.this.scrollbarIndicatorSize[dir], Unit.PX);

            ScrollPanelTouchImpl.this.scrollbarMaxScroll[dir] = ScrollPanelTouchImpl.this.scrollBarSize[dir] - ScrollPanelTouchImpl.this.scrollbarIndicatorSize[dir];
            ScrollPanelTouchImpl.this.scrollbarProp[dir] = ((double) (ScrollPanelTouchImpl.this.scrollbarMaxScroll[dir])) / ScrollPanelTouchImpl.this.maxScrollX;
            break;
          case VERTICAL:
            ScrollPanelTouchImpl.this.scrollBarSize[dir] = ScrollPanelTouchImpl.this.scrollBarWrapper[dir].getClientHeight();

            ScrollPanelTouchImpl.this.scrollbarIndicatorSize[dir] =
                (int) Math.max(Math.round((double) (ScrollPanelTouchImpl.this.scrollBarSize[dir] * ScrollPanelTouchImpl.this.scrollBarSize[dir]) / ScrollPanelTouchImpl.this.scrollerHeight), 8);
            ScrollPanelTouchImpl.this.scrollBarIndicator[dir].getStyle().setHeight(ScrollPanelTouchImpl.this.scrollbarIndicatorSize[dir], Unit.PX);
            ScrollPanelTouchImpl.this.scrollbarMaxScroll[dir] = ScrollPanelTouchImpl.this.scrollBarSize[dir] - ScrollPanelTouchImpl.this.scrollbarIndicatorSize[dir];
            ScrollPanelTouchImpl.this.scrollbarProp[dir] = ((double) (ScrollPanelTouchImpl.this.scrollbarMaxScroll[dir])) / ScrollPanelTouchImpl.this.maxScrollY;

            break;

          default:
            break;
        }

        // Reset position
        scrollbarPos(direction, true);
      }
    }.schedule(delay);

  }

  private void resize() {
    int delay = MGWT.getOsDetection().isAndroid() ? 200 : 1;
    new Timer() {
      @Override
      public void run() {
        refresh();
      }
    }.schedule(delay);
  }

  private void pos(int x, int y) {

    x = this.hScroll ? x : 0;
    y = this.vScroll ? y : 0;

    if (useTransform) {
      CssUtil.translate(scroller.getElement(), x, y);
    } else {
      // TODO
      scroller.getElement().getStyle().setLeft(x, Unit.PX);
      scroller.getElement().getStyle().setTop(y, Unit.PX);

    }

    this.x = x;
    this.y = y;

    scrollbarPos(DIRECTION.HORIZONTAL, false);
    scrollbarPos(DIRECTION.VERTICAL, false);

  }

  private void scrollbarPos(DIRECTION direction, boolean hidden) {

    double pos = direction == DIRECTION.HORIZONTAL ? this.x : this.y;
    int size;
    int dir = direction.ordinal();

    if (!this.scrollBar[dir]) {
      return;
    }

    pos = this.scrollbarProp[dir] * pos;

    if (pos < 0) {
      if (!this.fixedScrollbar) {
        size = (int) (this.scrollbarIndicatorSize[dir] + Math.round(pos * 3));
        if (size < 8)
          size = 8;
        if (direction == DIRECTION.HORIZONTAL) {
          this.scrollBarIndicator[dir].getStyle().setWidth(size, Unit.PX);
        } else {
          this.scrollBarIndicator[dir].getStyle().setHeight(size, Unit.PX);
        }

      }
      pos = 0;
    } else {
      if (pos > this.scrollbarMaxScroll[dir]) {
        if (!this.fixedScrollbar) {
          size = (int) (this.scrollbarIndicatorSize[dir] - Math.round((pos - this.scrollbarMaxScroll[dir]) * 3));

          if (size < 8)
            size = 8;

          if (direction == DIRECTION.HORIZONTAL) {
            this.scrollBarIndicator[dir].getStyle().setWidth(size, Unit.PX);
          } else {
            this.scrollBarIndicator[dir].getStyle().setHeight(size, Unit.PX);
          }
          pos = this.scrollbarMaxScroll[dir] + (this.scrollbarIndicatorSize[dir] - size);

        } else {
          pos = this.scrollbarMaxScroll[dir];
        }
      }
    }

    CssUtil.setTransitionsDelay(this.scrollBarWrapper[dir], 0);
    CssUtil.setOpacity(this.scrollBarWrapper[dir], hidden && this.hideScrollBar ? 0 : 1);
    if (direction == DIRECTION.HORIZONTAL) {
      CssUtil.translate(this.scrollBarIndicator[dir], (int) pos, 0);
    } else {
      CssUtil.translate(this.scrollBarIndicator[dir], 0, (int) pos);

    }

  }

  private void start(TouchStartEvent event) {
    int x, y;
    if (!this.enabled) {
      return;
    }

    fireEvent(new BeforeScrollStartEvent(event));

    if (this.useTransistion || this.zoom) {
      setTransistionTime(0);
    }

    this.moved = false;
    this.animating = false;
    this.zoomed = false;
    this.distX = 0;
    this.distY = 0;
    this.absDistX = 0;
    this.absDistY = 0;
    this.dirX = 0;
    this.dirY = 0;

    JsArray<Touch> touches = event.getTouches();

    if (this.zoom && touches.length() > 1) {
      int c1 = Math.abs(touches.get(0).getPageX() - touches.get(1).getPageX());
      int c2 = Math.abs(touches.get(0).getPageY() - touches.get(1).getPageY());
      this.touchesDistStart = Math.sqrt(c1 * c1 + c2 * c2);

      this.originX = Math.abs(touches.get(0).getPageX() + touches.get(1).getPageX() - this.wrapperOffsetLeft * 2 / 2 - this.x);
      this.originY = Math.abs(touches.get(0).getPageY() + touches.get(1).getPageY() - this.wrapperOffsetTop * 2 / 2 - this.y);

      // TODO call on zoom start
    }

    if (this.momentum) {
      if (this.useTransform) {
        int[] pos = CssUtil.getPositionFromTransForm(scroller.getElement());
        x = pos[0];
        y = pos[1];
      } else {
        x = CssUtil.getLeftPositionFromCssPosition(scroller.getElement());
        y = CssUtil.getTopPositionFromCssPosition(scroller.getElement());
      }

      if (x != this.x || y != this.y) {
        if (this.useTransistion) {
          unbindTransistionEnd();
        } else {
          cancelAnimationFrame();
        }
        this.steps = CollectionFactory.constructArray();
        pos(x, y);
      }
    }

    this.absStartX = this.x;
    this.absStartY = this.y;

    this.startX = this.x;
    this.startY = this.y;
    this.pointX = touches.get(0).getPageX();
    this.pointY = touches.get(0).getPageY();

    this.startTime = System.currentTimeMillis();

    fireEvent(new ScrollStartEvent(event));

    bindMoveEvent();
    bindEndEvent();
    bindCancelEvent();
  }

  private void move(TouchMoveEvent event) {
    // old android needs to prevent default on move
    if (MGWT.getOsDetection().isAndroid()) {
      event.preventDefault();
    }

    JsArray<Touch> touches = event.getTouches();
    int deltaX = touches.get(0).getPageX() - this.pointX;
    int deltaY = touches.get(0).getPageY() - this.pointY;
    int newX = this.x + deltaX;
    int newY = this.y + deltaY;
    long timeStamp = System.currentTimeMillis();

    // fire onbeforescroll event
    fireEvent(new BeforeScrollMoveEvent(event));

    if (zoom && touches.length() > 1) {
      int c1 = Math.abs(touches.get(0).getPageX() - touches.get(1).getPageX());
      int c2 = Math.abs(touches.get(0).getPageY() - touches.get(1).getPageY());
      this.touchesDist = Math.sqrt(c1 * c1 + c2 * c2);
      this.zoomed = true;

      double scale = 1 / this.touchesDistStart * this.touchesDist * this.scale;
      if (scale < this.zoomMin) {
        scale = 0.5 * zoomMin * Math.pow(2.0, scale / zoomMin);
      } else {
        if (scale > zoomMax) {
          scale = 2.0 * zoomMax * Math.pow(0.5, zoomMax / scale);
        }
      }
      this.lastScale = scale / this.scale;

      newX = (int) Math.round(this.originX - this.originX * this.lastScale + this.x);
      newY = (int) Math.round(this.originY - this.originY * this.lastScale + this.y);

      CssUtil.setTranslateAndZoom(this.scroller.getElement(), newX, newY, scale);

      // TODO call on zoom
      return;
    }

    this.pointX = touches.get(0).getPageX();
    this.pointY = touches.get(0).getPageY();

    // slower outside the bounds!
    if (newX > 0 || newX < this.maxScrollX) {
      if (bounce) {
        newX = (int) (this.x + Math.round(deltaX / this.bounceFactor));
      } else {
        if (newX >= 0 || this.maxScrollX >= 0) {
          newX = 0;
        } else {
          newX = this.maxScrollX;
        }
      }
    }

    if (newY > this.minScrollY || newY < this.maxScrollY) {
      if (bounce) {
        newY = (int) (this.y + Math.round(deltaY / this.bounceFactor));
      } else {
        if (newY >= this.minScrollY || this.maxScrollY >= 0) {
          newY = this.minScrollY;
        } else {
          newY = this.maxScrollY;
        }
      }
    }

    this.distX += deltaX;
    this.distY += deltaY;
    this.absDistX = Math.abs(this.distX);
    this.absDistY = Math.abs(this.distY);

    if (this.absDistX < 6 && this.absDistY < 6) {
      return;
    }

    if (lockDirection) {
      if (this.absDistX > this.absDistY + 5) {
        newY = this.y;
        deltaY = 0;
      } else {
        if (this.absDistY > this.absDistX + 5) {
          newX = this.x;
          deltaX = 0;
        }
      }
    }

    this.moved = true;
    pos(newX, newY);

    this.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
    this.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;

    if (timeStamp - this.startTime > 300) {
      this.startTime = timeStamp;
      this.startX = this.x;
      this.startY = this.y;
    }

    fireEvent(new ScrollMoveEvent(event));
  }

  private void end(final TouchEvent<?> event) {
    if (event != null && event.getTouches().length() != 0) {
      return;
    }

    long duration = System.currentTimeMillis() - this.startTime;
    int newPosX = this.x;
    int newPosY = this.y;
    Momentum momentumX = Momentum.ZERO_MOMENTUM;
    Momentum momentumY = Momentum.ZERO_MOMENTUM;

    unbindMoveEvent();
    unbindEndEvent();
    unbindCancelEvent();

    // fire on before scroll end
    fireEvent(new BeforeScrollEndEvent(event));

    if (zoomed) {
      double scale = this.scale * this.lastScale;
      scale = Math.max(this.zoomMin, scale);
      scale = Math.min(this.zoomMax, scale);
      this.lastScale = scale / this.scale;

      this.x = (int) Math.round(this.originX - this.originX * this.lastScale + this.x);
      this.y = (int) Math.round(this.originY - this.originY * this.lastScale + this.y);

      CssUtil.setTransitionDuration(this.scroller.getElement(), 200);
      CssUtil.setTranslateAndZoom(this.scroller.getElement(), this.x, this.y, this.scale);

      this.zoomed = false;
      this.refresh();

      // TODO fire onzoomend

      return;

    }

    if (!this.moved) {
      if (this.doubleTapTimer != null && zoom) {
        doubleTapTimer.cancel();
        doubleTapTimer = null;

        // TODO fire on zoom start

        // TODO fire zoom end after duration
      } else {
        this.doubleTapTimer = new Timer() {

          @Override
          public void run() {
            doubleTapTimer = null;

            // TODO dispatch tap event

          }
        };
        this.doubleTapTimer.schedule(this.zoom ? 250 : 1);
      }

      resetPos(200);

      // TODO fire touchend!
      return;
    }

    if (duration < 300 && momentum) {
      if (newPosX != 0) {
        momentumX = momentum(newPosX - this.startX, duration, -this.x, this.scrollerWidth - this.wrapperWidth + this.x, this.bounce ? this.wrapperWidth : 0);
      }
      if (newPosY != 0) {
        momentumY =
            momentum(newPosY - this.startY, duration, -this.y, (this.maxScrollY < 0 ? this.scrollerHeight - this.wrapperHeight + this.y - this.minScrollY : 0), this.bounce ? this.wrapperHeight : 0);
      }

      newPosX = this.x + momentumX.getDist();
      newPosY = this.y + momentumY.getDist();

      if ((this.x > 0 && newPosX > 0) || (this.x < this.maxScrollX && newPosX < this.maxScrollX)) {
        momentumX = Momentum.ZERO_MOMENTUM;
      }

      if ((this.y > this.minScrollY && newPosY > this.minScrollY) || (this.y < this.maxScrollY && newPosY < this.maxScrollY)) {
        momentumY = Momentum.ZERO_MOMENTUM;
      }

    }

    int distX = 0;
    int distY = 0;

    if (momentumX.getDist() != 0 || momentumY.getDist() != 0) {

      int newDuration = Math.max(Math.max(momentumX.getTime(), momentumY.getTime()), 10);

      if (this.snap) {
        distX = newPosX - this.absStartX;
        distY = newPosY - this.absStartY;

        if (Math.abs(distX) < this.snapThreshold && Math.abs(distY) < this.snapThreshold) {
          scrollTo(this.absStartX, this.absStartY, 200);
        } else {
          Snap snap = snap(newPosX, newPosY);
          newPosX = snap.getX();
          newPosY = snap.getY();
          newDuration = Math.max(snap.getTime(), newDuration);
        }

      }

      scrollTo(newPosX, newPosY, newDuration);

      // TODO fire touch end!
      return;
    }

    if (this.snap) {
      distX = newPosX - this.absDistX;
      distY = newPosY - this.absDistY;

      if (Math.abs(distX) < this.snapThreshold && Math.abs(distY) < this.snapThreshold) {
        scrollTo(this.absStartX, this.absStartY, 200);
      } else {
        Snap snap = snap(this.x, this.y);
        if (snap.x != this.x || snap.y != this.y) {
          scrollTo(snap.x, snap.y, snap.time);
        }
      }

      // fire on touch end
      return;

    }

    resetPos(200);
    // TODO fire on touch end

  }

  private void resetPos(int time) {

    int resetX = this.x >= 0 ? 0 : this.x < this.maxScrollX ? this.maxScrollX : this.x;

    int resetY = this.y >= this.minScrollY || this.maxScrollY > 0 ? this.minScrollY : this.y < this.maxScrollY ? this.maxScrollY : this.y;

    if (resetX == this.x && resetY == this.y) {
      if (this.moved) {
        this.moved = false;
        // fire on scroll end
        fireEvent(new ScrollEndEvent());
      }

      if (this.scrollBar[DIRECTION.HORIZONTAL.ordinal()] && this.hideScrollBar) {
        CssUtil.setTransitionsDelay(this.scrollBarWrapper[DIRECTION.HORIZONTAL.ordinal()], 300);
        CssUtil.setOpacity(this.scrollBarWrapper[DIRECTION.HORIZONTAL.ordinal()], 0);

      }

      if (this.scrollBar[DIRECTION.VERTICAL.ordinal()] && this.hideScrollBar) {
        CssUtil.setTransitionsDelay(this.scrollBarWrapper[DIRECTION.VERTICAL.ordinal()], 300);
        CssUtil.setOpacity(this.scrollBarWrapper[DIRECTION.VERTICAL.ordinal()], 0);

      }

      return;
    }

    scrollTo(resetX, resetY, time);

  }

  private void wheel(int wheelDeltaX, int wheelDeltaY, int pageX, int pageY) {

    if (wheelActionZoom) {
      double deltaScale = this.scale * Math.pow(2, 1.0 / 3 * (wheelDeltaY != 0 ? wheelDeltaY / Math.abs(wheelDeltaY) : 0));
      if (deltaScale < this.zoomMin)
        deltaScale = this.zoomMin;
      if (deltaScale > this.zoomMax)
        deltaScale = this.zoomMax;

      if (Math.abs(deltaScale - this.scale) < 0.00001) {
        if (this.wheelZoomCount == 0) {
          // TODO maybe fire on zoom start
        }
        this.wheelZoomCount++;

        zoom(pageX, pageY, deltaScale, 400);

        new Timer() {

          @Override
          public void run() {
            ScrollPanelTouchImpl.this.wheelZoomCount--;
            if (ScrollPanelTouchImpl.this.wheelZoomCount == 0) {
              // TODO maybe fire zoom end
            }

          }

        }.schedule(400);
      }
      return;
    }

    int deltaX = this.x + wheelDeltaX;
    int deltaY = this.y + wheelDeltaY;

    if (deltaX > 0)
      deltaX = 0;
    else if (deltaX < this.maxScrollX)
      deltaX = this.maxScrollX;

    if (deltaY > this.minScrollY)
      deltaY = this.minScrollY;
    else if (deltaY < this.maxScrollY)
      deltaY = this.maxScrollY;

    scrollTo(deltaX, deltaY, 0);

  }

  private void mouseOut(MouseOutEvent event) {

    EventTarget relatedTarget = event.getRelatedTarget();

    if (relatedTarget == null) {

      end(null);
      return;
    }

    if (!Node.is(relatedTarget)) {
      end(null);
      return;
    }

    Node tmp = relatedTarget.cast();

    while (true) {

      tmp = tmp.getParentNode();

      if (tmp == this.wrapper.getElement()) {
        return;
      }

      if (tmp == null) {
        break;
      }

    }

    this.end(null);

  }

  private void onTransistionEnd(TransitionEndEvent event) {
    EventTarget eventTarget = event.getNativeEvent().getEventTarget();
    if (Node.is(eventTarget)) {
      if (Element.is(eventTarget)) {
        Element target = eventTarget.cast();
        Element scrollerElement = this.scroller.getElement();
        // reference id should be okay according to
        // http://google-web-toolkit.googlecode.com/svn/javadoc/latest/com/google/gwt/user/client/DOM.html#compare(com.google.gwt.user.client.Element,
        // com.google.gwt.user.client.Element)
        if (target != scrollerElement) {
          return;
        }

      }
    }

    unbindTransistionEnd();

    startAnimation();

  }

  private void startAnimation() {
    if (this.animating)
      return;

    final int startX = this.x;
    final int startY = this.y;

    if (this.steps.length() == 0) {
      resetPos(400);
      return;
    }
    fireEvent(new ScrollAnimationStartEvent());
    final Step step = this.steps.shift();

    if (step.getX() == startX && step.getY() == startY) {
      step.setTime(0);
    }

    this.animating = true;
    this.moved = true;

    if (this.useTransistion) {
      setTransistionTime(step.getTime());
      pos(step.getX(), step.getY());
      this.animating = false;
      if (step.getTime() != 0) {
        bindTransistionEndEvent();
      } else {
        resetPos(0);
      }
      return;
    }

    final long startTime = System.currentTimeMillis();

    final AnimationCallback animationCallback = new AnimationCallback() {

      @Override
      public void execute(double now) {

        if (now >= startTime + step.getTime()) {
          ScrollPanelTouchImpl.this.pos(step.x, step.y);
          ScrollPanelTouchImpl.this.animating = false;
          fireEvent(new ScrollAnimationEndEvent());
          ScrollPanelTouchImpl.this.startAnimation();
          return;
        }

        now = (now - startTime) / step.getTime() - 1;
        double easeOut = Math.sqrt(1 - now * now);
        int newX = (int) Math.round((step.getX() - startX) * easeOut + startX);
        int newY = (int) Math.round((step.getY() - startY) * easeOut + startY);
        ScrollPanelTouchImpl.this.pos(newX, newY);
        fireEvent(new ScrollAnimationMoveEvent());
        if (ScrollPanelTouchImpl.this.animating)
          ScrollPanelTouchImpl.this.aniTime = AnimationScheduler.get().requestAnimationFrame(this);

      }
    };

    animationCallback.execute(startTime);

  }

  private void setTransistionTime(int time) {

    CssUtil.setTransitionDuration(scroller.getElement(), time);

    if (vScrollbar) {
      CssUtil.setTransitionDuration(this.scrollBarIndicator[DIRECTION.VERTICAL.ordinal()], time);
    }

    if (hScrollbar) {
      CssUtil.setTransitionDuration(this.scrollBarIndicator[DIRECTION.HORIZONTAL.ordinal()], time);
    }

  }

  private Momentum momentum(int dist, long time, int maxDistUpper, int maxDistLower, int size) {
    double deceleration = 0.0006;
    double speed = ((double) (Math.abs(dist))) / time;
    double newDist = (speed * speed) / (2 * deceleration);
    double newTime = 0;
    double outSideDist = 0;

    // Proportinally reduce speed if we are outside of the boundaries
    if (dist > 0 && newDist > maxDistUpper) {
      outSideDist = size / (6 / (newDist / speed * deceleration));
      maxDistUpper = (int) (maxDistUpper + outSideDist);
      speed = speed * maxDistUpper / newDist;
      newDist = maxDistUpper;
    } else if (dist < 0 && newDist > maxDistLower) {
      outSideDist = size / (6 / (newDist / speed * deceleration));
      maxDistLower = (int) (maxDistLower + outSideDist);
      speed = speed * maxDistLower / newDist;
      newDist = maxDistLower;
    }

    newDist = newDist * (dist < 0 ? -1 : 1);
    newTime = speed / deceleration;

    return new Momentum((int) Math.round(newDist), (int) Math.round(newTime));
  }

  private int[] offSet(com.google.gwt.dom.client.Element el) {
    int left = -el.getOffsetLeft();
    int top = -el.getOffsetTop();

    com.google.gwt.dom.client.Element domElem = el;
    while (true) {
      domElem = domElem.getOffsetParent();
      if (domElem == null)
        break;
      left -= domElem.getOffsetLeft();
      top -= domElem.getOffsetTop();
    }

    if (el != this.wrapper.getElement()) {
      left *= this.scale;
      top *= this.scale;
    }

    return new int[] {left, top};
  }

  private Snap snap(int x, int y) {

    // Check page X
    int page = this.pagesX.length() - 1;
    for (int i = 0, l = this.pagesX.length(); i < l; i++) {
      if (x >= this.pagesX.get(i)) {
        page = i;
        break;
      }
    }
    if (page == this.currPageX && page > 0 && this.dirX < 0)
      page--;
    x = this.pagesX.get(page);
    int sizeX = Math.abs(x - this.pagesX.get(this.currPageX));
    sizeX = sizeX != 0 ? Math.abs(this.x - x) / sizeX * 500 : 0;
    this.currPageX = page;

    // Check page Y
    page = this.pagesY.length() - 1;
    for (int i = 0; i < page; i++) {
      if (y >= this.pagesY.get(i)) {
        page = i;
        break;
      }
    }
    if (page == this.currPageY && page > 0 && this.dirY < 0)
      page--;
    y = this.pagesY.get(page);
    int sizeY = Math.abs(y - this.pagesY.get(this.currPageY));
    sizeY = sizeY != 0 ? Math.abs(this.y - y) / sizeY * 500 : 0;
    this.currPageY = page;

    // Snap with constant speed (proportional duration)
    int time = Math.max(sizeX, sizeY);
    if (time == 0)
      time = 200;

    return new Snap(x, y, time);

  }

  private void bindTransistionEndEvent() {
    if (CssUtil.hasTransistionEndEvent()) {
      transistionEndRegistration = scroller.addDomHandler(new TransitionEndHandler() {

        @Override
        public void onTransitionEnd(TransitionEndEvent event) {
          onTransistionEnd(event);

        }
      }, TransitionEndEvent.getType());
    }

  }

  private void unbindTransistionEnd() {
    if (transistionEndRegistration != null) {
      transistionEndRegistration.removeHandler();
      transistionEndRegistration = null;
    }

  }

  @Override
  public void refresh() {
    if (!isAttached())
      return;

    if (scale < zoomMin) {
      scale = zoomMin;
    }
    wrapperHeight = getClientHeight(wrapper.getElement());
    if (wrapperHeight == 0) {
      wrapperHeight = 1;
    }
    wrapperWidth = getClientWidth(wrapper.getElement());
    if (wrapperWidth == 0) {
      wrapperWidth = 1;
    }

    minScrollY = -topOffset;

    scrollerWidth = (int) Math.round((scroller.getOffsetWidth() + getMarginWidth(scroller.getElement())) * scale);
    scrollerHeight = (int) Math.round((scroller.getOffsetHeight() + minScrollY + +getMarginHeight(scroller.getElement())) * scale);

    maxScrollX = wrapperWidth - scrollerWidth;

    maxScrollY = wrapperHeight - scrollerHeight + minScrollY + offsetMaxY;

    dirX = 0;
    dirY = 0;

    hScroll = (hScrollDesired && maxScrollX < 0);
    vScroll = vScrollDesired && (!bounceLock && !hScroll || scrollerHeight > wrapperHeight);

    hScrollbar = hScroll && hScrollbar;
    vScrollbar = vScroll && vScrollbar && scrollerHeight > wrapperHeight;

    int[] offSet = offSet(ScrollPanelTouchImpl.this.wrapper.getElement());

    wrapperOffsetLeft = -offSet[0];
    wrapperOffsetTop = -offSet[1];

    // prep stuff
    if (ScrollPanelTouchImpl.this.snapSelector != null) {
      ScrollPanelTouchImpl.this.pagesX = CollectionFactory.constructIntegerArray();
      ScrollPanelTouchImpl.this.pagesY = CollectionFactory.constructIntegerArray();

      ScrollPanelTouchImpl.this.pagesActualX = CollectionFactory.constructIntegerArray();
      ScrollPanelTouchImpl.this.pagesActualY = CollectionFactory.constructIntegerArray();

      JsArray<com.google.gwt.dom.client.Element> elements = querySelectorAll(ScrollPanelTouchImpl.this.scroller.getElement(), snapSelector);

      for (int i = 0; i < elements.length(); i++) {
        int[] pos = offSet(elements.get(i));
        int left = pos[0] + ScrollPanelTouchImpl.this.wrapperOffsetLeft;
        int top = pos[1] + ScrollPanelTouchImpl.this.wrapperOffsetTop;
        ScrollPanelTouchImpl.this.pagesX.push((int) (left < ScrollPanelTouchImpl.this.maxScrollX ? ScrollPanelTouchImpl.this.maxScrollX : left * ScrollPanelTouchImpl.this.scale));
        ScrollPanelTouchImpl.this.pagesY.push((int) (top < ScrollPanelTouchImpl.this.maxScrollY ? ScrollPanelTouchImpl.this.maxScrollY : top * ScrollPanelTouchImpl.this.scale));

        ScrollPanelTouchImpl.this.pagesActualX.push((int) (left * ScrollPanelTouchImpl.this.scale));
        ScrollPanelTouchImpl.this.pagesActualY.push((int) (top * ScrollPanelTouchImpl.this.scale));
      }
    } else {
      if (ScrollPanelTouchImpl.this.snap) {
        int pos = 0;
        int page = 0;
        ScrollPanelTouchImpl.this.pagesX = CollectionFactory.constructIntegerArray();

        while (pos >= ScrollPanelTouchImpl.this.maxScrollX) {
          ScrollPanelTouchImpl.this.pagesX.set(page, pos);
          pos = pos - ScrollPanelTouchImpl.this.wrapperWidth;
          page++;
        }
        if (ScrollPanelTouchImpl.this.maxScrollX % ScrollPanelTouchImpl.this.wrapperWidth != 0)
          ScrollPanelTouchImpl.this.pagesX.set(ScrollPanelTouchImpl.this.pagesX.length(), ScrollPanelTouchImpl.this.maxScrollX
              - ScrollPanelTouchImpl.this.pagesX.get(ScrollPanelTouchImpl.this.pagesX.length() - 1) + ScrollPanelTouchImpl.this.pagesX.get(ScrollPanelTouchImpl.this.pagesX.length() - 1));

        pos = 0;
        page = 0;
        ScrollPanelTouchImpl.this.pagesY = CollectionFactory.constructIntegerArray();
        while (pos >= ScrollPanelTouchImpl.this.maxScrollY) {
          ScrollPanelTouchImpl.this.pagesY.set(page, pos);
          pos = pos - ScrollPanelTouchImpl.this.wrapperHeight;
          page++;
        }
        if (ScrollPanelTouchImpl.this.maxScrollY % ScrollPanelTouchImpl.this.wrapperHeight != 0)
          ScrollPanelTouchImpl.this.pagesY.set(ScrollPanelTouchImpl.this.pagesY.length(), ScrollPanelTouchImpl.this.maxScrollY
              - ScrollPanelTouchImpl.this.pagesY.get(ScrollPanelTouchImpl.this.pagesY.length() - 1) + ScrollPanelTouchImpl.this.pagesY.get(ScrollPanelTouchImpl.this.pagesY.length() - 1));

        ScrollPanelTouchImpl.this.pagesActualX = this.pagesX;
        ScrollPanelTouchImpl.this.pagesActualY = this.pagesY;
      }
    }

    scrollBar(DIRECTION.HORIZONTAL);
    scrollBar(DIRECTION.VERTICAL);

    if (!ScrollPanelTouchImpl.this.zoomed) {
      CssUtil.setTransitionDuration(ScrollPanelTouchImpl.this.scroller.getElement(), 0);
      resetPos(200);
    }

    updateDefaultStyles();

    // fire refresh event
    fireEvent(new ScrollRefreshEvent());

  }

  @Override
  public void scrollTo(int x, int y, int time) {
    scrollTo(x, y, time, false);

  }

  public void scrollTo(int x, int y, int time, boolean relative) {
    stop();

    int destX;
    int destY;

    if (relative) {
      destX = this.x - x;
      destY = this.y - y;
    } else {
      destX = x;
      destY = y;
    }

    Step step = new Step(destX, destY, time);

    this.steps.push(step);

    startAnimation();

  }

  public void scrollToElement(com.google.gwt.dom.client.Element el, int time) {

    int[] offSet = offSet(el);
    int left = offSet[0] + this.wrapperOffsetLeft;
    int top = offSet[1] + this.wrapperOffsetTop;

    left = left > 0 ? 0 : left < this.maxScrollX ? this.maxScrollX : left;
    top = top > this.minScrollY ? this.minScrollY : top < this.maxScrollY ? this.maxScrollY : top;

    scrollTo(left, top, time);
  }

  public void scrollToPage(int pageX, int pageY) {
    scrollToPage(pageX, pageY, 400);
  }

  public void scrollToPage(int pageX, int pageY, int time) {

    fireEvent(new ScrollStartEvent(null));

    int x, y;
    if (this.snap || snapSelector != null) {

      pageX = pageX < 0 ? 0 : pageX > this.pagesX.length() - 1 ? this.pagesX.length() - 1 : pageX;
      pageY = pageY < 0 ? 0 : pageY > this.pagesY.length() - 1 ? this.pagesY.length() - 1 : pageY;

      this.currPageX = pageX;
      this.currPageY = pageY;
      x = this.pagesX.get(pageX);
      y = this.pagesY.get(pageY);
    } else {
      x = -this.wrapperWidth * pageX;
      y = -this.wrapperHeight * pageY;
      if (x < this.maxScrollX)
        x = this.maxScrollX;
      if (y < this.maxScrollY)
        y = this.maxScrollY;
    }

    scrollTo(x, y, time);

  }

  public void disable() {
    stop();
    resetPos(0);
    this.enabled = false;

    unbindMoveEvent();
    unbindEndEvent();
    unbindCancelEvent();
  }

  public void enable() {
    this.enabled = true;
  }

  public void stop() {
    if (this.useTransistion) {
      unbindTransistionEnd();
    } else {
      if (this.aniTime != null)
        this.aniTime.cancel();
    }

    this.steps = CollectionFactory.constructArray();
    this.moved = false;
    this.animating = false;
  }

  public void zoom(int x, int y, double scale, int time) {

    if (!this.useTransform)
      return;

    double relScale = scale / this.scale;

    this.zoomed = true;

    x = x - this.wrapperOffsetLeft - this.x;
    y = y - this.wrapperOffsetTop - this.y;

    this.x = (int) Math.round(x - x * relScale + this.x);
    this.y = (int) Math.round(y - y * relScale + this.y);

    this.scale = scale;
    this.refresh();

    this.x = this.x > 0 ? 0 : this.x < this.maxScrollX ? this.maxScrollX : this.x;
    this.y = this.y > this.minScrollY ? this.minScrollY : this.y < this.maxScrollY ? this.maxScrollY : this.y;

    CssUtil.setTransitionDuration(this.scroller.getElement(), time);
    CssUtil.setTranslateAndZoom(this.scroller.getElement(), x, y, scale);
    this.zoomed = true;

  }

  public boolean isReady() {
    return !this.moved && !this.zoomed && !this.animating;
  }

  /*
   * Helpers!
   */

  // TODO move in util
  private native int getClientHeight(Element element)/*-{
    return element.clientHeight || 0;
  }-*/;

  private native int getClientWidth(Element element) /*-{
    return element.clientWidth || 0;
  }-*/;

  private native JsArray<com.google.gwt.dom.client.Element> querySelectorAll(Element el, String selector)/*-{
    return el.querySelectorAll(selector);
  }-*/;

  @Override
  public void add(Widget w) {
    if (scroller != null) {
      throw new IllegalStateException("scrollpanel can only have one child");
    }
    setWidget(w);

  }

  /*
   * GWT stuff
   */

  @Override
  public void clear() {
    setWidget(null);

  }

  @Override
  public Iterator<Widget> iterator() {
    return wrapper.iterator();
  }

  @Override
  public boolean remove(Widget w) {
    if (w == scroller) {
      scroller = null;
      return wrapper.remove(w);
    }
    return false;
  }

  @Override
  public void setUsePos(boolean pos) {
    this.useTransform = !pos;
  }

  @Override
  public boolean isScrollingEnabledX() {
    return hScroll;
  }

  @Override
  public void setScrollingEnabledX(boolean scrollingEnabledX) {
    this.hScrollDesired = scrollingEnabledX;
    // this.hScroll = scrollingEnabledX;
    // this.scrollBar[DIRECTION.HORIZONTAL.ordinal()] = scrollingEnabledX;

  }

  @Override
  public boolean isScrollingEnabledY() {
    return this.vScroll;
  }

  @Override
  public void setScrollingEnabledY(boolean scrollingEnabledY) {
    this.vScrollDesired = scrollingEnabledY;
    // this.vScroll = scrollingEnabledY;
    // this.scrollBar[DIRECTION.VERTICAL.ordinal()] = scrollingEnabledY;
  }

  @Override
  public void setWidget(IsWidget child) {
    setWidget(child.asWidget() != null ? child.asWidget() : null);

  }

  private HandlerRegistration touchStartRegistration;
  private HandlerRegistration orientationChangeRegistration;
  private TouchDelegate touchDelegate;
  private HandlerRegistration mouseOutRegistration;
  private HandlerRegistration mouseWheelRegistration;
  private HandlerRegistration touchCancelRegistration;
  private HandlerRegistration touchEndRegistration;
  private HandlerRegistration touchMoveRegistration;

  private boolean listenForStart;

  private boolean listenForCancelEvent;

  private boolean listenForEndEvent;

  private boolean listenForMoveEvent;

  private int offsetMaxY;

  public void setWidget(Widget w) {

    // clear old event handlers
    unbindStartEvent();
    unbindResizeEvent();
    if (MGWT.getOsDetection().isDesktop()) {
      unbindMouseoutEvent();
      unbindMouseWheelEvent();
    }

    if (scroller != null) {
      // clean up
      scroller.removeStyleName(css.container());
      remove(scroller);

    }

    scroller = w;

    if (scroller != null) {
      wrapper.setWidget(scroller);
      scroller.addStyleName(css.container());
      if (isAttached()) {
        bindResizeEvent();
        bindStartEvent();
        if (MGWT.getOsDetection().isDesktop()) {
          bindMouseoutEvent();
          bindMouseWheelEvent();
        }
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {

          @Override
          public void execute() {
            refresh();

          }
        });

      }

    }

  }

  @Override
  protected void onDetach() {
    super.onDetach();
    unbindResizeEvent();
  }

  @Override
  protected void onAttach() {
    super.onAttach();

    if (scroller != null) {

      // bind events
      bindResizeEvent();
      bindStartEvent();
      if (MGWT.getOsDetection().isDesktop()) {
        bindMouseoutEvent();
        bindMouseWheelEvent();
      }

      Scheduler.get().scheduleDeferred(new ScheduledCommand() {

        @Override
        public void execute() {
          refresh();

        }
      });

    }

  }

  private void setupEvents() {
    touchMoveRegistration = touchDelegate.addTouchMoveHandler(touchListener);
    touchStartRegistration = touchDelegate.addTouchStartHandler(touchListener);
    touchCancelRegistration = touchDelegate.addTouchCancelHandler(touchListener);
    touchEndRegistration = touchDelegate.addTouchEndHandler(touchListener);

  }

  private void bindMouseWheelEvent() {
    mouseWheelRegistration = scroller.addDomHandler(new MouseWheelHandler() {

      @Override
      public void onMouseWheel(MouseWheelEvent event) {
        int wheelDeltaX = 0;
        int wheelDeltaY = 0;

        if (isScrollingEnabledX()) {
          wheelDeltaX = getMouseWheelVelocityX(event.getNativeEvent()) / 10;
        }

        if (isScrollingEnabledY()) {
          wheelDeltaY = getMouseWheelVelocityY(event.getNativeEvent()) / 10;
        }
        wheel(wheelDeltaX, wheelDeltaY, event.getClientX(), event.getClientY());

      }
    }, MouseWheelEvent.getType());

  }

  private native int getMouseWheelVelocityX(NativeEvent evt)/*-{
    return Math.round(-evt.wheelDeltaX) || 0;
  }-*/;

  private native int getMouseWheelVelocityY(NativeEvent evt)/*-{

    var val = (evt.detail * 40) || -evt.wheelDeltaY || 0;
    return Math.round(val);
  }-*/;

  private void unbindMouseWheelEvent() {
    if (mouseWheelRegistration != null) {
      mouseWheelRegistration.removeHandler();
      mouseWheelRegistration = null;
    }

  }

  private void bindMouseoutEvent() {
    mouseOutRegistration = this.wrapper.addDomHandler(new MouseOutHandler() {

      @Override
      public void onMouseOut(MouseOutEvent event) {
        mouseOut(event);

      }
    }, MouseOutEvent.getType());

  }

  private void unbindMouseoutEvent() {
    if (mouseOutRegistration != null) {
      mouseOutRegistration.removeHandler();
      mouseOutRegistration = null;
    }

  }

  private void bindStartEvent() {
    listenForStart = true;
  }

  private void unbindStartEvent() {
    listenForStart = false;

  }

  private void bindCancelEvent() {
    listenForCancelEvent = true;
  }

  private void bindEndEvent() {
    listenForEndEvent = true;

  }

  private void bindMoveEvent() {
    listenForMoveEvent = true;
  }

  private void unbindCancelEvent() {
    listenForCancelEvent = false;

  }

  private void unbindEndEvent() {
    listenForEndEvent = false;
  }

  private void unbindMoveEvent() {
    listenForMoveEvent = false;
  }

  /**
   *
   */
  private void bindResizeEvent() {
    if (!MGWT.getFormFactor().isDesktop()) {
      orientationChangeRegistration = MGWT.addOrientationChangeHandler(new OrientationChangeHandler() {

        @Override
        public void onOrientationChanged(OrientationChangeEvent event) {
          if (shouldHandleResize) {
            resize();
          }

        }
      });
    } else {
      orientationChangeRegistration = Window.addResizeHandler(new ResizeHandler() {

        @Override
        public void onResize(ResizeEvent event) {
          if (shouldHandleResize) {
            resize();
          }

        }
      });
    }

  }

  private void unbindResizeEvent() {
    if (orientationChangeRegistration != null) {
      orientationChangeRegistration.removeHandler();
      orientationChangeRegistration = null;
    }
  }

  private void updateDefaultStyles() {
    if (scroller != null) {

      CssUtil.setTransistionProperty(scroller.getElement(), useTransform ? CssUtil.getTransformProperty() : "top left");
      CssUtil.setTransitionDuration(scroller.getElement(), 0);
      CssUtil.setTransFormOrigin(scroller.getElement(), 0, 0);
      if (useTransistion) {
        CssUtil.setTransistionTimingFunction(scroller.getElement(), "cubic-bezier(0.33,0.66,0.66,1)");
      }
      if (useTransform) {
        CssUtil.translate(scroller.getElement(), this.x, this.y);
      } else {
        scroller.getElement().getStyle().setPosition(Position.ABSOLUTE);
        scroller.getElement().getStyle().setLeft(this.x, Unit.PX);
        scroller.getElement().getStyle().setTop(this.y, Unit.PX);

      }

      if (useTransistion) {
        this.fixedScrollbar = true;
      }

    }

  }

  private void cancelAnimationFrame() {
    if (aniTime != null) {
      aniTime.cancel();
      aniTime = null;
    }

  }

  @Override
  public void setOffSetY(int y) {
    this.topOffset = y;

  }

  @Override
  public void setMaxScrollY(int y) {
    this.maxScrollY = y;

  }

  @Override
  public int getMaxScrollY() {
    return this.maxScrollY;
  }

  @Override
  public void setMinScrollY(int y) {
    this.minScrollY = y;

  }

  @Override
  public int getMinScrollY() {
    return this.minScrollY;
  }

  @Override
  public void setBounce(boolean bounce) {
    this.bounce = bounce;

  }

  @Override
  public void setMomentum(boolean momentum) {
    this.momentum = momentum;

  }

  @Override
  public void setSnap(boolean snap) {
    this.snap = snap;

  }

  @Override
  public void setSnapThreshold(int threshold) {
    this.snapThreshold = threshold;

  }

  private native int getMarginWidth(Element el)/*-{
    var left = 0;
    var right = 0;
    var style = $wnd.getComputedStyle(el);

    left = parseInt(style.marginLeft, 10) || 0;
    right = parseInt(style.marginRight, 10) || 0;

    return left + right;
  }-*/;

  private native int getMarginHeight(Element el)/*-{

    var top = 0;
    var bottom = 0;
    var style = $wnd.getComputedStyle(el);

    top = parseInt(style.marginTop, 10) || 0;
    bottom = parseInt(style.marginBottom, 10) || 0;

    return top + bottom;
  }-*/;

  @Override
  public int getY() {
    return y;
  }

  @Override
  public int getX() {
    return x;
  }

  @Override
  public void setBounceFactor(double factor) {
    this.bounceFactor = factor;
  }

  @Deprecated
  @Override
  public void setShowScrollBarX(boolean show) {
    this.hScrollbar = show;
    scrollBar[DIRECTION.VERTICAL.ordinal()] = show;
  }

  @Deprecated
  @Override
  public void setShowScrollBarY(boolean show) {
    this.vScrollbar = show;
    scrollBar[DIRECTION.HORIZONTAL.ordinal()] = show;
  }

  @Override
  public void setShowHorizontalScrollBar(boolean show) {
    this.hScrollbar = show;
    scrollBar[DIRECTION.HORIZONTAL.ordinal()] = show;
  }

  @Override
  public void setShowVerticalScrollBar(boolean show) {
    this.vScrollbar = show;
    scrollBar[DIRECTION.VERTICAL.ordinal()] = show;
  }

  @Override
  public int getCurrentPageX() {
    return currPageX;
  }

  @Override
  public int getCurrentPageY() {
    return currPageY;
  }

  @Override
  public void setAutoHandleResize(boolean handle) {
    shouldHandleResize = handle;

  }

  @Override
  public void setOffSetMaxY(int height) {
    this.offsetMaxY = height;

  }

  @Override
  public void setSnapSelector(String selector) {
    this.snapSelector = selector;

  }

  @Override
  public LightArrayInt getPagesY() {
    return this.pagesActualY;
  }

  @Override
  public LightArrayInt getPagesX() {
    return this.pagesActualX;
  }

  public void setHideScrollBar(boolean hideScrollBar) {
    this.hideScrollBar = hideScrollBar;
  }

  @Override
  public void setScrollLock(boolean lock) {
    this.lockDirection = lock;
  }
}
TOP

Related Classes of com.googlecode.mgwt.ui.client.widget.panel.scroll.impl.ScrollPanelTouchImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.