fi.jasoft.dragdroplayouts.client.ui.VLayoutDragDropMouseHandler.java Source code

Java tutorial

Introduction

Here is the source code for fi.jasoft.dragdroplayouts.client.ui.VLayoutDragDropMouseHandler.java

Source

/*
 * Copyright 2011 John Ahlroos
 *
 * 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 fi.jasoft.dragdroplayouts.client.ui;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
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.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.BrowserInfo;
import com.vaadin.terminal.gwt.client.Util;
import com.vaadin.terminal.gwt.client.VConsole;
import com.vaadin.terminal.gwt.client.ui.VButton;
import com.vaadin.terminal.gwt.client.ui.VCssLayout;
import com.vaadin.terminal.gwt.client.ui.VSlider;
import com.vaadin.terminal.gwt.client.ui.VTextField;
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager;
import com.vaadin.terminal.gwt.client.ui.dd.VDragEvent;
import com.vaadin.terminal.gwt.client.ui.dd.VTransferable;

/**
 * Mouse handler for starting component drag operations
 * 
 * @author John Ahlroos
 */
public class VLayoutDragDropMouseHandler implements MouseDownHandler, TouchStartHandler {

    public static final String ACTIVE_DRAG_SOURCE_STYLENAME = "v-dd-active-drag-source";

    private LayoutDragMode dragMode = LayoutDragMode.NONE;

    private final Widget root;

    private Widget currentDraggedWidget;

    private HandlerRegistration mouseUpHandlerReg;

    private final List<HandlerRegistration> handlers = new LinkedList<HandlerRegistration>();

    private final List<DragStartListener> dragStartListeners = new ArrayList<VLayoutDragDropMouseHandler.DragStartListener>();

    /**
     * A listener to listen for drag start events
     */
    public interface DragStartListener {
        /**
         * Called when a drag is about to begin
         * 
         * @param widget
         *            The widget which is about to be dragged
         * @param mode
         *            The draggin mode
         * @return Should the dragging be commenced.
         */
        boolean dragStart(Widget widget, LayoutDragMode mode);
    }

    /**
     * Constructor
     * 
     * @param root
     *            The root element
     * @param dragMode
     *            The drag mode of the layout
     */
    public VLayoutDragDropMouseHandler(Widget root, LayoutDragMode dragMode) {
        this.dragMode = dragMode;
        this.root = root;
    }

    /**
     * Is the mouse down event a valid mouse drag event, i.e. left mouse button
     * is pressed without any modifier keys
     * 
     * @param event
     *            The mouse event
     * @return Is the mouse event a valid drag event
     */
    private boolean isMouseDragEvent(NativeEvent event) {
        boolean hasModifierKey = event.getAltKey() || event.getCtrlKey() || event.getMetaKey()
                || event.getShiftKey();
        return !(hasModifierKey || event.getButton() > NativeEvent.BUTTON_LEFT);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.google.gwt.event.dom.client.TouchStartHandler#onTouchStart(com.google
     * .gwt.event.dom.client.TouchStartEvent)
     */
    public void onTouchStart(TouchStartEvent event) {
        initiateDrag(event.getNativeEvent());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google
     * .gwt.event.dom.client.MouseDownEvent)
     */
    public void onMouseDown(MouseDownEvent event) {
        initiateDrag(event.getNativeEvent());
        event.preventDefault();
        event.stopPropagation();
    }

    /**
     * Called when the dragging a component should be initiated by both a mouse
     * down event as well as a touch start event
     * 
     * @param event
     */
    protected void initiateDrag(NativeEvent event) {
        // Check that dragging is enabled
        if (dragMode == LayoutDragMode.NONE) {
            return;
        }

        // Dragging can only be done with left mouse button and no modifier keys
        if (!isMouseDragEvent(event) && !Util.isTouchEvent(event)) {
            return;
        }

        // Create the transfarable
        VTransferable transferable = VDragDropUtil.createLayoutTransferableFromMouseDown(event, root);

        // Are we trying to drag the root layout
        if (transferable == null) {
            VConsole.error("Creating transferable on mouse down returned null");
            return;
        }

        // Resolve the component
        final Widget w;
        if (root instanceof VDDAccordion && transferable.getData(Constants.TRANSFERABLE_DETAIL_CAPTION) != null) {
            w = (Widget) transferable.getData(Constants.TRANSFERABLE_DETAIL_CAPTION);
        } else if (transferable.getData(Constants.TRANSFERABLE_DETAIL_COMPONENT) != null) {
            w = (Widget) transferable.getData(Constants.TRANSFERABLE_DETAIL_COMPONENT);
        } else {
            // Failsafe if no widget was found
            w = root;
            VConsole.log("Could not resolve component, using root as component");
        }

        // Announce drag start to listeners
        for (DragStartListener dl : dragStartListeners) {
            if (!dl.dragStart(w, dragMode)) {
                VDragAndDropManager.get().interruptDrag();
                return;
            }
        }

        /*
         * A hack to remove slider popup when dragging. This is done by first
         * focusing the slider and then unfocusing so we get a blur event which
         * will remove the popup.
         */
        if (w instanceof VSlider) {
            VSlider slider = (VSlider) w;
            slider.setFocus(true);
            slider.setFocus(false);
        }

        currentDraggedWidget = w;

        // Announce to handler that we are starting a drag operation
        VDragEvent currentDragEvent = VDragAndDropManager.get().startDrag(transferable, event, true);

        if (w instanceof VButton && BrowserInfo.get().isIE()) {
            /*
             * Due to Buttons crazy implementation we need to simulate a mouse
             * move to get the drag going in IE without first focusing the
             * button. Crazy shit.
             */
            VButton button = (VButton) w;
            NativeEvent evt = Document.get().createMouseMoveEvent(1, event.getScreenX(), event.getScreenY(),
                    Util.getTouchOrMouseClientX(event), Util.getTouchOrMouseClientY(event), false, false, false,
                    false, 0);
            button.getElement().dispatchEvent(evt);
        }

        // Create the drag image
        if (root instanceof VCssLayout) {
            /*
             * CSS Layout does not have an enclosing div so we just use the
             * component dov
             */
            currentDragEvent.createDragImage((Element) w.getElement().cast(), true);

        } else {
            /*
             * Other layouts uses a enclosing div so we use it.
             */
            currentDragEvent.createDragImage((Element) w.getElement().getParentNode().cast(), true);
        }

        if (BrowserInfo.get().isIE7() && w instanceof VTextField) {
            currentDragEvent.createDragImage(w.getElement(), true);
            // Capture something so focus is not applied to elements
            Event.setCapture(RootPanel.getBodyElement());
        }

        Element clone = currentDragEvent.getDragImage();
        Style cloneStyle = clone.getStyle();
        if (BrowserInfo.get().isIE()) {
            // Fix IE not aligning the drag image correctly when dragging
            // layouts
            cloneStyle.setPosition(Position.ABSOLUTE);
        }

        currentDraggedWidget.addStyleName(ACTIVE_DRAG_SOURCE_STYLENAME);

        // Listen to mouse up for cleanup
        mouseUpHandlerReg = Event.addNativePreviewHandler(new Event.NativePreviewHandler() {
            public void onPreviewNativeEvent(NativePreviewEvent event) {
                if (event.getTypeInt() == Event.ONMOUSEUP || event.getTypeInt() == Event.ONTOUCHEND
                        || event.getTypeInt() == Event.ONTOUCHCANCEL) {
                    if (mouseUpHandlerReg != null) {
                        mouseUpHandlerReg.removeHandler();
                        if (currentDraggedWidget != null) {
                            currentDraggedWidget.removeStyleName(ACTIVE_DRAG_SOURCE_STYLENAME);
                            currentDraggedWidget = null;
                        }
                    }

                    // Ensure capturing is turned off at mouse up
                    Event.releaseCapture(RootPanel.getBodyElement());
                }
            }
        });

    }

    /**
     * Set the current drag mode
     * 
     * @param dragMode
     *            The drag mode to use
     */
    public void updateDragMode(LayoutDragMode dragMode) {
        this.dragMode = dragMode;
    }

    /**
     * Add a drag start listener to monitor drag starts
     * 
     * @param listener
     */
    public void addDragStartListener(DragStartListener listener) {
        dragStartListeners.add(listener);
    }

    /**
     * Remove a drag start listener
     * 
     * @param listener
     */
    public void removeDragStartListener(DragStartListener listener) {
        dragStartListeners.remove(listener);
    }

    /**
     * Start listening to events
     */
    public void attach() {
        if (handlers.isEmpty()) {
            handlers.add(root.addDomHandler(this, MouseDownEvent.getType()));
            handlers.add(root.addDomHandler(this, TouchStartEvent.getType()));
        }
    }

    /**
     * Attach to another widget than root
     * 
     * @param widget
     *            The widget to attach to
     */
    public void attachTo(Widget widget) {
        if (handlers.isEmpty()) {
            handlers.add(widget.addDomHandler(this, MouseDownEvent.getType()));
            handlers.add(widget.addDomHandler(this, TouchStartEvent.getType()));
        }
    }

    /**
     * Stop listening to events
     */
    public void detach() {
        for (HandlerRegistration reg : handlers) {
            reg.removeHandler();
        }
        handlers.clear();
    }
}