com.allen_sauer.gwt.dnd.client.AbstractDragController.java Source code

Java tutorial

Introduction

Here is the source code for com.allen_sauer.gwt.dnd.client.AbstractDragController.java

Source

/*
 * Copyright 2009 Fred Sauer
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.allen_sauer.gwt.dnd.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

import com.allen_sauer.gwt.dnd.client.util.DOMUtil;
import com.allen_sauer.gwt.dnd.client.util.DragClientBundle;

import java.util.HashMap;
import java.util.Iterator;

/**
 * {@link DragController} which performs the bare essentials such as adding/removing styles,
 * maintaining collections, adding mouse listeners, etc.
 * 
 * <p> Extend this class to implement specialized drag capabilities such table column or panel
 * resizing. For classic drag-and-drop functionality, i.e. the ability to pickup, move around and
 * drop widgets, use {@link PickupDragController}. </p>
 */
public abstract class AbstractDragController implements DragController {
    // CHECKSTYLE_JAVADOC_OFF

    private HashMap<Widget, Widget> dragHandles = new HashMap<Widget, Widget>();

    /**
     * The drag controller's drag context.
     */
    protected final DragContext context;

    /**
     * The boundary panel to which all drag operations are constrained.
     */
    AbsolutePanel boundaryPanel;

    private boolean cancelDocumentSelections = true;

    /**
     * Whether or not widgets are physically constrained to the boundary panel.
     */
    private boolean constrainedToBoundaryPanel;

    /**
     * The current drag end event, created in {@link #previewDragEnd()} and returned a second time in
     * {@link #dragEnd()}.
     */
    private DragEndEvent dragEndEvent;

    /**
     * Collection of registered drag handlers.
     */
    private DragHandlerCollection dragHandlers;

    /**
     * The current drag start event, created in {@link #previewDragStart()} and returned a second time
     * in {@link #dragStart()}.
     */
    private DragStartEvent dragStartEvent;

    /**
     * Drag sensitivity in pixels.
     */
    private int dragStartSensitivityPixels;

    /**
     * This drag controller's mouse drag handler.
     */
    private MouseDragHandler mouseDragHandler;

    /**
     * Whether multiple selection behavior is enabled.
     */
    private boolean multipleSelectionAllowed = false;

    /**
     * Whether scrollIntoView() or it's equivalent is to be called during dragging.
     */
    private boolean scrollIntoView = true;

    /**
     * Create a new drag-and-drop controller. Drag operations will be limited to the specified
     * boundary panel.
     * 
     * @param boundaryPanel the desired boundary panel or <code>RootPanel.get()</code> if entire
     *          document body is to be the boundary
     */
    public AbstractDragController(AbsolutePanel boundaryPanel) {
        assert boundaryPanel != null : "Use 'RootPanel.get()' instead of 'null'.";
        this.boundaryPanel = boundaryPanel;
        context = new DragContext(this);
        mouseDragHandler = new MouseDragHandler(context);
    }

    @Override
    public final void addDragHandler(DragHandler handler) {
        if (dragHandlers == null) {
            dragHandlers = new DragHandlerCollection();
        }
        dragHandlers.add(handler);
    }

    @Override
    public void clearSelection() {
        for (Iterator<Widget> iterator = context.selectedWidgets.iterator(); iterator.hasNext();) {
            Widget widget = iterator.next();
            widget.removeStyleName(DragClientBundle.INSTANCE.css().selected());
            iterator.remove();
        }
    }

    @Override
    public void dragEnd() {
        context.draggable.removeStyleName(DragClientBundle.INSTANCE.css().dragging());
        if (dragHandlers != null) {
            dragHandlers.fireDragEnd(dragEndEvent);
            dragEndEvent = null;
        }
        assert dragEndEvent == null;
    }

    @Override
    public void dragStart() {
        if (!GWT.isScript()) {
            if (DOMUtil.getClientHeight(boundaryPanel.getElement()) == 0) {
                if (boundaryPanel.getElement().equals(RootPanel.getBodyElement())) {
                    DOMUtil.reportFatalAndThrowRuntimeException(
                            "boundary panel (= the BODY element) has zero height;"
                                    + " dragging cannot occur inside an AbsolutePanel that has a height of zero pixels;"
                                    + " you can often remedy this quite easily by adding the following line of"
                                    + " CSS to your application's stylesheet:" + " BODY, HTML { height: 100%; }");
                }
            }
        }
        resetCache();
        if (dragHandlers != null) {
            dragHandlers.fireDragStart(dragStartEvent);
            dragStartEvent = null;
        }
        context.draggable.addStyleName(DragClientBundle.INSTANCE.css().dragging());
        assert dragStartEvent == null;
    }

    @Override
    public boolean getBehaviorCancelDocumentSelections() {
        return cancelDocumentSelections;
    }

    @Override
    public boolean getBehaviorConstrainedToBoundaryPanel() {
        return constrainedToBoundaryPanel;
    }

    @Override
    public int getBehaviorDragStartSensitivity() {
        return dragStartSensitivityPixels;
    }

    @Override
    public boolean getBehaviorMultipleSelection() {
        return multipleSelectionAllowed;
    }

    @Override
    public boolean getBehaviorScrollIntoView() {
        return scrollIntoView;
    }

    @Override
    public final AbsolutePanel getBoundaryPanel() {
        return boundaryPanel;
    }

    /**
     * Attaches a {@link MouseDragHandler} to the widget, applies styles to the draggable and the
     * handle.
     * 
     * @see #makeDraggable(Widget, Widget)
     * @see HasDragHandle
     * 
     * @param draggable the widget to be made draggable
     */
    @Override
    public void makeDraggable(Widget draggable) {
        if (draggable instanceof HasDragHandle) {
            makeDraggable(draggable, ((HasDragHandle) draggable).getDragHandle());
        } else {
            makeDraggable(draggable, draggable);
        }
    }

    /**
     * Similar to {@link #makeDraggable(Widget)}, but allow separate, child to be specified as the
     * drag handle by which the first widget can be dragged.
     * 
     * @param draggable the widget to be made draggable
     * @param dragHandle the widget by which widget can be dragged
     */
    @Override
    public void makeDraggable(Widget draggable, Widget dragHandle) {
        mouseDragHandler.makeDraggable(draggable, dragHandle);
        draggable.addStyleName(DragClientBundle.INSTANCE.css().draggable());
        dragHandle.addStyleName(DragClientBundle.INSTANCE.css().handle());
        dragHandles.put(draggable, dragHandle);
    }

    /**
     * Performs the reverse of {@link #makeDraggable(Widget)}, detaching the {@link MouseDragHandler}
     * from the widget and removing any styling which was applied when making the widget draggable.
     * 
     * @param draggable the widget to no longer be draggable
     */
    @Override
    public void makeNotDraggable(Widget draggable) {
        Widget dragHandle = dragHandles.remove(draggable);
        mouseDragHandler.makeNotDraggable(dragHandle);
        draggable.removeStyleName(DragClientBundle.INSTANCE.css().draggable());
        dragHandle.removeStyleName(DragClientBundle.INSTANCE.css().handle());
    }

    @Override
    public void previewDragEnd() throws VetoDragException {
        assert dragEndEvent == null;
        if (dragHandlers != null) {
            dragEndEvent = new DragEndEvent(context);
            dragHandlers.firePreviewDragEnd(dragEndEvent);
        }
    }

    @Override
    public void previewDragStart() throws VetoDragException {
        assert dragStartEvent == null;
        if (dragHandlers != null) {
            dragStartEvent = new DragStartEvent(context);
            try {
                dragHandlers.firePreviewDragStart(dragStartEvent);
            } catch (VetoDragException ex) {
                dragStartEvent = null;
                throw ex;
            }
        }
    }

    @Override
    public final void removeDragHandler(DragHandler handler) {
        if (dragHandlers != null) {
            dragHandlers.remove(handler);
        }
    }

    @Override
    public void resetCache() {
    }

    @Override
    public void setBehaviorCancelDocumentSelections(boolean cancelDocumentSelections) {
        this.cancelDocumentSelections = cancelDocumentSelections;
    }

    @Override
    public void setBehaviorConstrainedToBoundaryPanel(boolean constrainedToBoundaryPanel) {
        this.constrainedToBoundaryPanel = constrainedToBoundaryPanel;
    }

    @Override
    public void setBehaviorDragStartSensitivity(int pixels) {
        assert pixels >= 0;
        dragStartSensitivityPixels = pixels;
    }

    @Override
    public void setBehaviorMultipleSelection(boolean multipleSelectionAllowed) {
        this.multipleSelectionAllowed = multipleSelectionAllowed;
        for (Iterator<Widget> iterator = context.selectedWidgets.iterator(); iterator.hasNext();) {
            Widget widget = iterator.next();
            widget.removeStyleName(DragClientBundle.INSTANCE.css().selected());
            iterator.remove();
        }
    }

    @Override
    public void setBehaviorScrollIntoView(boolean scrollIntoView) {
        this.scrollIntoView = scrollIntoView;
    }

    public void setConstrainWidgetToBoundaryPanel(boolean constrainWidgetToBoundaryPanel) {
        setBehaviorConstrainedToBoundaryPanel(constrainWidgetToBoundaryPanel);
    }

    @Override
    public void toggleSelection(Widget draggable) {
        assert draggable != null;
        if (context.selectedWidgets.remove(draggable)) {
            draggable.removeStyleName(DragClientBundle.INSTANCE.css().selected());
        } else if (multipleSelectionAllowed) {
            context.selectedWidgets.add(draggable);
            draggable.addStyleName(DragClientBundle.INSTANCE.css().selected());
        } else {
            context.selectedWidgets.clear();
            context.selectedWidgets.add(draggable);
        }
    }
}