com.sencha.gxt.dnd.core.client.DropTarget.java Source code

Java tutorial

Introduction

Here is the source code for com.sencha.gxt.dnd.core.client.DropTarget.java

Source

/**
 * Sencha GXT 4.0.0 - Sencha for GWT
 * Copyright (c) 2006-2015, Sencha Inc.
 *
 * licensing@sencha.com
 * http://www.sencha.com/products/gxt/license/
 *
 * ================================================================================
 * Open Source License
 * ================================================================================
 * This version of Sencha GXT is licensed under the terms of the Open Source GPL v3
 * license. You may use this license only if you are prepared to distribute and
 * share the source code of your application under the GPL v3 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * If you are NOT prepared to distribute and share the source code of your
 * application under the GPL v3 license, other commercial and oem licenses
 * are available for an alternate download of Sencha GXT.
 *
 * Please see the Sencha GXT Licensing page at:
 * http://www.sencha.com/products/gxt/license/
 *
 * For clarification or additional options, please contact:
 * licensing@sencha.com
 * ================================================================================
 *
 *
 * ================================================================================
 * Disclaimer
 * ================================================================================
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 * ================================================================================
 */
package com.sencha.gxt.dnd.core.client;

import java.util.List;

import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.Point;
import com.sencha.gxt.core.shared.event.CancellableEvent;
import com.sencha.gxt.dnd.core.client.DND.Feedback;
import com.sencha.gxt.dnd.core.client.DND.Operation;
import com.sencha.gxt.dnd.core.client.DndDragCancelEvent.DndDragCancelHandler;
import com.sencha.gxt.dnd.core.client.DndDragCancelEvent.HasDndDragCancelHandlers;
import com.sencha.gxt.dnd.core.client.DndDragEnterEvent.DndDragEnterHandler;
import com.sencha.gxt.dnd.core.client.DndDragEnterEvent.HasDndDragEnterHandlers;
import com.sencha.gxt.dnd.core.client.DndDragLeaveEvent.DndDragLeaveHandler;
import com.sencha.gxt.dnd.core.client.DndDragLeaveEvent.HasDndDragLeaveHandlers;
import com.sencha.gxt.dnd.core.client.DndDragMoveEvent.DndDragMoveHandler;
import com.sencha.gxt.dnd.core.client.DndDragMoveEvent.HasDndDragMoveHandlers;
import com.sencha.gxt.dnd.core.client.DndDropEvent.DndDropHandler;
import com.sencha.gxt.dnd.core.client.DndDropEvent.HasDndDropHandlers;
import com.sencha.gxt.widget.core.client.Component;
import com.sencha.gxt.widget.core.client.event.XEvent;

/**
 * Enables a component to act as the target of a drag and drop operation (i.e. a
 * user can drop data on the component).
 * <p />
 * While the cursor is over a target, the target is responsible for determining
 * if the drop is valid and showing any visual indicators for the drop. The
 * {@link StatusProxy} object should be used to specify if the drop is valid,
 * and can also be used to change the values of the proxy object displayed by
 * the cursor.
 */
public class DropTarget implements HasDndDragEnterHandlers, HasDndDragLeaveHandlers, HasDndDragCancelHandlers,
        HasDndDragMoveHandlers, HasDndDropHandlers {

    protected Widget dropWidget;
    protected Feedback feedback;
    protected Operation operation;
    protected String overStyle;

    private boolean allowSelfAsSource;
    private SimpleEventBus eventBus;
    private HandlerRegistration dropWidgetRegistration;
    private AttachEvent.Handler dropWidgetHandler = new Handler() {
        @Override
        public void onAttachOrDetach(AttachEvent event) {
            if (event.isAttached()) {
                onDropWidgetAttach();
            } else {
                onDropWidgetDetach();
            }
        }
    };

    private boolean enabled = true;
    private String group = "";

    /**
     * Creates a new drop dropWidget.
     * 
     * @param dropWidget the dropWidget widget
     */
    public DropTarget(Widget dropWidget) {
        this.dropWidget = dropWidget;
        this.operation = Operation.MOVE;
        this.feedback = Feedback.APPEND;

        dropWidgetRegistration = this.dropWidget.addAttachHandler(dropWidgetHandler);

        if (this.dropWidget.isAttached()) {
            onDropWidgetAttach();
        }
    }

    @Override
    public HandlerRegistration addDragCancelHandler(DndDragCancelHandler handler) {
        return ensureHandlers().addHandler(DndDragCancelEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addDragEnterHandler(DndDragEnterHandler handler) {
        return ensureHandlers().addHandler(DndDragEnterEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addDragLeaveHandler(DndDragLeaveHandler handler) {
        return ensureHandlers().addHandler(DndDragLeaveEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addDragMoveHandler(DndDragMoveHandler handler) {
        return ensureHandlers().addHandler(DndDragMoveEvent.getType(), handler);
    }

    @Override
    public HandlerRegistration addDropHandler(DndDropHandler handler) {
        return ensureHandlers().addHandler(DndDropEvent.getType(), handler);
    }

    /**
     * Disables the drag source.
     */
    public void disable() {
        enabled = false;
    }

    /**
     * Enables the drag source.
     */
    public void enable() {
        enabled = true;
    }

    /**
     * Returns the target's feedback setting.
     * 
     * @return the feedback
     */
    public Feedback getFeedback() {
        return feedback;
    }

    /**
     * Returns the target's group name.
     * 
     * @return the group name
     */
    public String getGroup() {
        return group;
    }

    /**
     * Returns the target's operation.
     * 
     * @return the operation
     */
    public Operation getOperation() {
        return operation;
    }

    /**
     * Returns the target's over style.
     * 
     * @return the over style
     */
    public String getOverStyle() {
        return overStyle;
    }

    /**
     * Returns the target's widget.
     * 
     * @return the widget
     */
    public Widget getWidget() {
        return dropWidget;
    }

    /**
     * Returns true if internal drops are allowed.
     * 
     * @return true for internal drops
     */
    public boolean isAllowSelfAsSource() {
        return allowSelfAsSource;
    }

    /**
     * Returns true if the drag source is enabled.
     * 
     * @return true for enabled
     */
    public boolean isEnabled() {
        return enabled && (dropWidget instanceof Component ? ((Component) dropWidget).isEnabled() : true);
    }

    /**
     * Unregisters the target as a drop target.
     */
    public void release() {
        dropWidgetRegistration.removeHandler();

        if (dropWidget.isAttached()) {
            onDropWidgetDetach();
        }
    }

    /**
     * Sets whether internal drops are allowed (defaults to false).
     * 
     * @param allowSelfAsSource true to allow internal drops
     */
    public void setAllowSelfAsSource(boolean allowSelfAsSource) {
        this.allowSelfAsSource = allowSelfAsSource;
    }

    /**
     * Sets the target's feedback. Feedback determines the type of visual
     * indicators a drop target supports. Subclasses will determine range of valid
     * values.
     * 
     * @param feedback the feedback
     */
    public void setFeedback(Feedback feedback) {
        this.feedback = feedback;
    }

    /**
     * Sets the drag group. If specified, only drag sources with the same group
     * value are allowed.
     * 
     * @param group the group name
     */
    public void setGroup(String group) {
        this.group = group;
    }

    /**
     * Sets the operation for the drop target which specifies if data should be
     * moved or copied when dropped. Drag sources use this value to determine if
     * the target data should be removed from the source widget.
     * 
     * @param operation the operation
     */
    public void setOperation(Operation operation) {
        this.operation = operation;
    }

    /**
     * Sets the style name to be applied when the cursor is over the target
     * (defaults to null).
     * 
     * @param overStyle the over style
     */
    public void setOverStyle(String overStyle) {
        this.overStyle = overStyle;
    }

    /**
     * Obtain potential top-most target element associated with provided event.
     *
     * For touch devices, this method will attempt to find element from contained Widget.
     * This is due to touch events "getEventTarget" always returning the element you started
     * the gesture on, regardless if you moved outside of the region of said element.  If the
     * event coordinates do not match up with any dropTarget elements, then null will be returned.
     *
     * @param event
     * @return
     */
    protected XElement getElementFromEvent(NativeEvent event) {
        if (GXT.isTouch()) {
            Point eventXY = event.<XEvent>cast().getXY();
            XElement dropTargetElement = getWidget().getElement().cast();
            return elementFromPoint(dropTargetElement, eventXY.getX(), eventXY.getY());
        }
        return event.getEventTarget().cast();
    }

    protected void onDropWidgetAttach() {
        DNDManager.get().registerDropTarget(this);
    }

    protected void onDropWidgetDetach() {
        DNDManager.get().unregisterDropTarget(this);
    }

    /**
     * Called if the user cancels the drag operations while the mouse is over the
     * target.
     * 
     * @param event the drag cancel event
     */
    protected void onDragCancelled(DndDragCancelEvent event) {
        Insert.get().hide();
    }

    /**
     * Called when the user releases the mouse over the target widget.
     * 
     * @param event the drop event
     */
    protected void onDragDrop(DndDropEvent event) {
    }

    /**
     * Called when the cursor first enters the bounds of the drop target.
     * Subclasses or listeners can change the status of status proxy via the
     * passed event.
     * 
     * @param event the drag enter event
     */
    protected void onDragEnter(DndDragEnterEvent event) {
    }

    /**
     * Called when a drop fails.
     * 
     * @param event the drop event
     */
    protected void onDragFail(DndDropEvent event) {
    }

    /**
     * Called when the cursor leaves the target.
     * 
     * @param event the drag leave event
     */
    protected void onDragLeave(DndDragLeaveEvent event) {
    }

    /**
     * Called when the cursor is moved within the target widget. Subclasses or
     * listeners can change the status of status proxy via the passed event. If
     * either a subclass or listener sets
     * {@link CancellableEvent#setCancelled(boolean)} to true,
     * {@link #showFeedback(DndDragMoveEvent)} will be called.
     * 
     * @param event the dd event
     */
    protected void onDragMove(DndDragMoveEvent event) {
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected List<Object> prepareDropData(Object data, boolean convertTreeStoreModel) {
        if (data instanceof List) {
            return (List) data;
        }
        // TODO why is this commented out or remove
        // List<ModelData> models = new ArrayList<ModelData>();
        // if (data instanceof ModelData) {
        // if (convertTreeStoreModel && data instanceof TreeStoreModel) {
        // models.add(((TreeStoreModel) data).getModel());
        // } else {
        // models.add((ModelData) data);
        // }
        // } else if (data instanceof List) {
        // for (Object obj : (List) data) {
        // if (obj instanceof ModelData) {
        // if (convertTreeStoreModel && obj instanceof TreeStoreModel) {
        // models.add(((TreeStoreModel) obj).getModel());
        // } else {
        // models.add((ModelData) obj);
        // }
        // }
        // }
        // }
        return null;// models;
    }

    /**
     * Called as the mouse is moved over the target widget. The default
     * implementation does nothing.
     * 
     * @param event the dd event
     */
    protected void showFeedback(DndDragMoveEvent event) {
    }

    SimpleEventBus ensureHandlers() {
        return eventBus == null ? eventBus = new SimpleEventBus() : eventBus;
    }

    boolean handleDragEnter(DndDragMoveEvent event) {
        DndDragEnterEvent e = new DndDragEnterEvent(dropWidget, event.getDragSource(), event.getDragMoveEvent(),
                event.getStatusProxy());
        event.setCancelled(false);
        event.getStatusProxy().setStatus(true);
        onDragEnter(e);

        ensureHandlers().fireEventFromSource(e, this);

        if (e.isCancelled()) {
            event.getStatusProxy().setStatus(false);
            return false;
        }
        if (overStyle != null) {
            dropWidget.addStyleName(overStyle);
        }
        return true;
    }

    void handleDragLeave(DndDragMoveEvent event) {
        if (overStyle != null) {
            dropWidget.removeStyleName(overStyle);
        }

        event.getStatusProxy().setStatus(false);
        Insert.get().hide();

        DndDragLeaveEvent e = new DndDragLeaveEvent(dropWidget, event.getDragSource(), event.getDragMoveEvent(),
                event.getStatusProxy());

        onDragLeave(e);
        ensureHandlers().fireEventFromSource(e, this);
    }

    void handleDragMove(DndDragMoveEvent event) {
        showFeedback(event);
        onDragMove(event);
    }

    void handleDrop(DndDropEvent event) {
        Insert.get().hide();
        if (overStyle != null) {
            dropWidget.removeStyleName(overStyle);
        }
        onDragDrop(event);
    }

    private XElement elementFromPoint(XElement element, int x, int y) {
        if (!element.getBounds().contains(x, y)) {
            return null;
        }
        if (!element.hasChildNodes()) {
            return element;
        }

        NodeList<Node> childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.getItem(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                XElement child = node.cast();
                if (child.getBounds().contains(x, y)) {
                    return elementFromPoint(child, x, y);
                }
            }
        }

        // children are not within bounds, return this
        return element;
    }
}