gwtquery.plugins.draggable.client.DraggableHandler.java Source code

Java tutorial

Introduction

Here is the source code for gwtquery.plugins.draggable.client.DraggableHandler.java

Source

/*
 * Copyright 2010 The gwtquery plugins team.
 *
 * 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 gwtquery.plugins.draggable.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.GQuery.Offset;
import com.google.gwt.query.client.Properties;
import com.google.gwt.query.client.plugins.Effects;
import com.google.gwt.query.client.plugins.UiPlugin;
import com.google.gwt.query.client.plugins.UiPlugin.Dimension;
import com.google.gwt.query.client.plugins.effects.PropertiesAnimation.Easing;
import com.google.gwt.query.client.plugins.events.GqEvent;
import com.google.gwt.user.client.Window;
import gwtquery.plugins.draggable.client.Draggable.CssClassNames;
import gwtquery.plugins.draggable.client.DraggableOptions.AxisOption;
import gwtquery.plugins.draggable.client.DraggableOptions.CursorAt;
import gwtquery.plugins.draggable.client.DraggableOptions.HelperType;
import gwtquery.plugins.draggable.client.impl.DraggableHandlerImpl;

import static com.google.gwt.query.client.GQuery.$;
import static com.google.gwt.query.client.GQuery.body;
import static gwtquery.plugins.draggable.client.Draggable.DRAGGABLE_HANDLER_KEY;

public class DraggableHandler {

    public static DraggableHandler getInstance(Element draggable) {
        return $(draggable).data(DRAGGABLE_HANDLER_KEY, DraggableHandler.class);
    }

    private DraggableHandlerImpl impl = GWT.create(DraggableHandlerImpl.class);

    private Offset margin;

    private Offset offset;
    private Offset absPosition;
    // from where the click happened relative to the draggable element
    private Offset offsetClick;
    private Offset parentOffset;
    private Offset relativeOffset;
    private int originalEventPageX;
    private int originalEventPageY;
    private Offset position;
    private Offset originalPosition;

    // info from helper
    private String helperCssPosition;
    private GQuery helperScrollParent;
    private GQuery helperOffsetParent;
    private int[] containment;
    private GQuery helper;
    private DraggableOptions options;
    private Dimension helperDimension;
    private boolean cancelHelperRemoval = false;

    // can be instantiate only by Draggable plugin
    DraggableHandler(DraggableOptions options) {
        this.options = options;
    }

    /**
     * convert a relative position to a absolute position and vice versa.
     *
     * @param absolute  if true the position is convert to an absolute position, if
     *                  false it is convert in a relative position
     * @param aPosition position to convert
     * @return
     */
    public Offset convertPositionTo(boolean absolute, Offset aPosition) {
        int mod = absolute ? 1 : -1;
        GQuery scroll = getScrollParent();
        boolean scrollIsRootNode = isRootNode(scroll.get(0));

        int top = aPosition.top + relativeOffset.top * mod + parentOffset.top * mod
                - ("fixed".equals(helperCssPosition) ? -helperScrollParent.scrollTop()
                        : scrollIsRootNode ? 0 : scroll.scrollTop()) * mod;

        int left = aPosition.left + relativeOffset.left * mod + parentOffset.left * mod
                - ("fixed".equals(helperCssPosition) ? -helperScrollParent.scrollLeft()
                        : scrollIsRootNode ? 0 : scroll.scrollLeft()) * mod;

        return new Offset(left, top);

    }

    public int[] getContainment() {
        return containment;
    }

    public GQuery getHelper() {
        return helper;
    }

    public String getHelperCssPosition() {
        return helperCssPosition;
    }

    public Dimension getHelperDimension() {
        return helperDimension;
    }

    public GQuery getHelperOffsetParent() {
        return helperOffsetParent;
    }

    public GQuery getHelperScrollParent() {
        return helperScrollParent;
    }

    public Offset getMargin() {
        return margin;
    }

    public Offset getOffset() {
        return offset;
    }

    public Offset getOffsetClick() {
        return offsetClick;
    }

    public DraggableOptions getOptions() {
        return options;
    }

    public int getOriginalEventPageX() {
        return originalEventPageX;
    }

    public int getOriginalEventPageY() {
        return originalEventPageY;
    }

    public Offset getOriginalPosition() {
        return originalPosition;
    }

    public Offset getParentOffset() {
        return parentOffset;
    }

    public Offset getPosition() {
        return position;
    }

    public Offset getAbsolutePosition() {
        return absPosition;
    }

    public Offset getRelativeOffset() {
        return relativeOffset;
    }

    public void initialize(Element element, GqEvent e) {

        helperCssPosition = helper.css("position");
        helperScrollParent = helper.as(UiPlugin.GQueryUi).scrollParent();
        helperOffsetParent = helper.offsetParent();

        if ("html".equalsIgnoreCase(helperOffsetParent.get(0).getTagName())) {
            helperOffsetParent = $(body);
        }

        setMarginCache(element);

        absPosition = new Offset(element.getAbsoluteLeft(), element.getAbsoluteTop());

        offset = new Offset(absPosition.left - margin.left, absPosition.top - margin.top);

        offsetClick = new Offset(e.pageX() - offset.left, e.pageY() - offset.top);

        parentOffset = calculateParentOffset(element);
        relativeOffset = calculateRelativeHelperOffset(element);

        originalEventPageX = e.pageX();
        originalEventPageY = e.pageY();

        position = calculateOriginalPosition(element, e);
        originalPosition = new Offset(position.left, position.top);

        if (options.getCursorAt() != null) {
            adjustOffsetFromHelper(options.getCursorAt());
        }
        calculateContainment();

    }

    private Offset calculateOriginalPosition(Element element, GqEvent e) {
        if (HelperType.ORIGINAL == options.getHelperType()) {
            return impl.getCssPosition(element);
        } else {
            return generatePosition(e, true);
        }
    }

    public boolean isRootNode(Element e) {
        return "html".equalsIgnoreCase(e.getTagName()) || e == body;
    }

    /**
     * @param firstTime if true, the helper has to be positionned without take
     *                  care to the axis options
     */
    public void moveHelper(boolean firstTime) {
        if (helper == null || helper.size() == 0) {
            return;
        }
        AxisOption axis = options.getAxis();
        if (AxisOption.NONE == axis || AxisOption.X_AXIS == axis || firstTime) {
            helper.get(0).getStyle().setLeft(position.left, Unit.PX);
        }
        if (AxisOption.NONE == axis || AxisOption.Y_AXIS == axis || firstTime) {
            helper.get(0).getStyle().setTop(position.top, Unit.PX);
        }
    }

    public void regeneratePositions(GqEvent e) {
        position = generatePosition(e, false);
        offset = convertPositionTo(true, position);
        absPosition = offset.add(margin.left, margin.top);
    }

    public void revertToOriginalPosition(Function function) {
        Properties oldPosition = Properties.create("{top:'" + String.valueOf(originalPosition.top) + "px',left:'"
                + String.valueOf(originalPosition.left) + "px'}");
        helper.as(Effects.Effects).animate(oldPosition, options.getRevertDuration(), Easing.LINEAR, function);

    }

    public void setHelperDimension(Dimension helperDimension) {
        this.helperDimension = helperDimension;
    }

    public void setMarginCache(Element element) {
        int marginLeft = (int) GQuery.$(element).cur("marginLeft", true);
        int marginTop = (int) GQuery.$(element).cur("marginTop", true);

        margin = new Offset(marginLeft, marginTop);

    }

    public void setOptions(DraggableOptions options) {
        this.options = options;

    }

    public void setPosition(Offset Offset) {
        position = Offset;

    }

    public void setOffsetClick(Offset offsetClick) {
        this.offsetClick = offsetClick;
    }

    void cacheHelperSize() {
        if (helper != null) {
            setHelperDimension(new Dimension(helper.get(0)));
        }

    }

    void clear(Element draggable) {
        if (helper == null) {
            return;
        }
        helper.removeClass(CssClassNames.GWT_DRAGGABLE_DRAGGING);
        if (HelperType.ORIGINAL != options.getHelperType() && !cancelHelperRemoval) {
            impl.removeHelper(helper, options.getHelperType());
        }
        helper = null;
        cancelHelperRemoval = false;

    }

    void createHelper(Element draggable, GqEvent e) {
        helper = options.getHelperType().createHelper(draggable, options.getHelper());
        if (!isElementAttached(helper)) {
            if ("parent".equals(options.getAppendTo())) {
                helper.appendTo(draggable.getParentNode());
            } else {
                helper.appendTo(options.getAppendTo());
            }
        }

        if (options.getHelperType() != HelperType.ORIGINAL && !helper.css("position").matches("(fixed|absolute)")) {
            helper.css("position", Position.ABSOLUTE.getCssName());
        }

    }

    private void adjustOffsetFromHelper(CursorAt cursorAt) {

        if (cursorAt.getLeft() != null) {
            offsetClick.left = cursorAt.getLeft().intValue() + margin.left;
        }

        if (cursorAt.getRight() != null) {
            offsetClick.left = helperDimension.getWidth() - cursorAt.getRight().intValue() + margin.left;
        }

        if (cursorAt.getTop() != null) {
            offsetClick.top = cursorAt.getTop().intValue() + margin.top;
        }

        if (cursorAt.getBottom() != null) {
            offsetClick.top = helperDimension.getHeight() - cursorAt.getBottom().intValue() + margin.top;
        }
    }

    private void calculateContainment() {
        String containmentAsString = options.getContainment();
        int[] containmentAsArray = options.getContainmentAsArray();
        GQuery $containement = options.getContainmentAsGQuery();

        if (containmentAsArray == null && containmentAsString == null && $containement == null) {
            containment = null;
            return;
        }

        if (containmentAsArray != null) {
            containment = containmentAsArray;
            return;
        }

        if (containmentAsString != null) {
            if ("window".equals(containmentAsString)) {
                containment = new int[] { 0 /*- relativeOffset.left - parentOffset.left*/,
                        0 /*- relativeOffset.top - parentOffset.top*/,
                        Window.getClientWidth() - helperDimension.getWidth() - margin.left,
                        Window.getClientHeight() - helperDimension.getHeight() - margin.top };

                return;
            }

            if ("parent".equals(containmentAsString)) {
                $containement = $(helper.get(0).getParentElement());
            } else if ("document".equals(containmentAsString)) {
                $containement = $("body");
            } else {
                $containement = $(containmentAsString);
            }
        }

        Element ce = $containement.get(0);
        if (ce == null) {
            return;
        }

        containment = impl.calculateContainment(this, $containement.offset(), ce,
                (!"hidden".equals($containement.css("overflow"))));

    }

    private Offset calculateParentOffset(Element element) {
        Offset position = helperOffsetParent.offset();

        if ("absolute".equals(helperCssPosition) && isOffsetParentIncludedInScrollParent()) {
            position = position.add(helperScrollParent.scrollLeft(), helperScrollParent.scrollTop());
        }

        if (impl.resetParentOffsetPosition(helperOffsetParent)) {
            position.left = 0;
            position.top = 0;
        }

        position = position.add((int) helperOffsetParent.cur("borderLeftWidth", true),
                (int) helperOffsetParent.cur("borderTopWidth", true));

        return new Offset(position.left, position.top);

    }

    /*
     * This is a relative to absolute position minus the actual position
     * calculation - only used for relative positioned helper
     */
    private Offset calculateRelativeHelperOffset(Element element) {
        if ("relative".equals(helperCssPosition)) {
            return impl.calculateRelativeHelperOffset(element, this);
        }
        return new Offset(0, 0);
    }

    private Offset generatePosition(GqEvent e, boolean initPosition) {

        GQuery scroll = getScrollParent();
        boolean scrollIsRootNode = isRootNode(scroll.get(0));

        int pageX = e.pageX();
        int pageY = e.pageY();

        if (!initPosition) {
            if (containment != null && containment.length == 4) {
                if (e.pageX() - offsetClick.left < containment[0]) {
                    pageX = containment[0] + offsetClick.left;
                }
                if (e.pageY() - offsetClick.top < containment[1]) {
                    pageY = containment[1] + offsetClick.top;
                }
                if (e.pageX() - offsetClick.left > containment[2]) {
                    pageX = containment[2] + offsetClick.left;
                }
                if (e.pageY() - offsetClick.top > containment[3]) {
                    pageY = containment[3] + offsetClick.top;
                }
            }

            if (options.getGrid() != null) {
                int[] grid = options.getGrid();
                int roundedTop = originalEventPageY + Math.round((pageY - originalEventPageY) / grid[1]) * grid[1];
                int roundedLeft = originalEventPageX + Math.round((pageX - originalEventPageX) / grid[0]) * grid[0];

                if (containment != null && containment.length == 4) {
                    boolean isOutOfContainment0 = roundedLeft - offsetClick.left < containment[0];
                    boolean isOutOfContainment1 = roundedTop - offsetClick.top < containment[1];
                    boolean isOutOfContainment2 = roundedLeft - offsetClick.left > containment[2];
                    boolean isOutOfContainment3 = roundedTop - offsetClick.top > containment[3];

                    pageY = !(isOutOfContainment1 || isOutOfContainment3) ? roundedTop
                            : (!isOutOfContainment1) ? roundedTop - grid[1] : roundedTop + grid[1];
                    pageX = !(isOutOfContainment0 || isOutOfContainment2) ? roundedLeft
                            : (!isOutOfContainment0) ? roundedLeft - grid[0] : roundedLeft + grid[0];

                } else {
                    pageY = roundedTop;
                    pageX = roundedLeft;
                }

            }
        }

        int top = pageY - offsetClick.top - relativeOffset.top - parentOffset.top
                + ("fixed".equals(helperCssPosition) ? -helperScrollParent.scrollTop()
                        : scrollIsRootNode ? 0 : scroll.scrollTop());

        int left = pageX - offsetClick.left - relativeOffset.left - parentOffset.left
                + ("fixed".equals(helperCssPosition) ? -helperScrollParent.scrollLeft()
                        : scrollIsRootNode ? 0 : scroll.scrollLeft());
        return new Offset(left, top);
    }

    private GQuery getScrollParent() {
        if ("absolute".equals(helperCssPosition) && !(isOffsetParentIncludedInScrollParent())) {
            return helperOffsetParent;
        } else {
            return helperScrollParent;
        }
    }

    private boolean isElementAttached(GQuery $element) {
        // normally this test helper.parents().filter("body").length() == 0 is
        // sufficient but they are a bug in gwtquery in filter function
        // return helper.parents().filter("body").length() == 0;
        GQuery parents = $element.parents();
        for (Element parent : parents.elements()) {
            if (parent == body) {
                return true;
            }
        }
        return false;
    }

    private boolean isOffsetParentIncludedInScrollParent() {
        assert helperOffsetParent != null && helperScrollParent != null;
        return !"html".equalsIgnoreCase(helperScrollParent.get(0).getTagName())
                && UiPlugin.contains(helperScrollParent.get(0), helperOffsetParent.get(0));
    }

}