app.dnd.drag.DraggableCellDecorator.java Source code

Java tutorial

Introduction

Here is the source code for app.dnd.drag.DraggableCellDecorator.java

Source

/*
 * Copyright 2010 Google Inc.
 *
 * 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 app.dnd.drag;

import app.dnd.DNDContext;
import app.dnd.resources.DNDResources;
import com.google.gwt.cell.client.*;
import com.google.gwt.core.client.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.event.dom.client.MouseDownEvent;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.HasVerticalAlignment;

import java.util.HashSet;
import java.util.Set;

/**
 * this is slightly tweaked version of Google's IconCellDecorator
 * 
 * A {@link Cell} decorator that adds an icon to another {@link Cell}.
 *
 * @param <T> the type that this Cell represents
 * @param <C> the type that this Cell represents
 */
@SuppressWarnings({ "JavaDoc" })
public abstract class DraggableCellDecorator<T, C> implements Cell<T> {
    public static final String MOUSE_DOWN = MouseDownEvent.getType().getName();
    private final String dragHandlerClass;

    interface Template extends SafeHtmlTemplates {
        @Template("<div style=\"position:relative;padding-{0}:{1}px;zoom:1;\">{2}<div>{3}</div></div>")
        SafeHtml outerDiv(String direction, int width, SafeHtml icon, SafeHtml cellContents);

        /**
         * The wrapper around the image vertically aligned to the bottom.
         */
        @Template("<div class='{2}' style=\"position:absolute;{0}:0px;bottom:0px;line-height:0px;\">{1}</div>")
        SafeHtml imageWrapperBottom(String direction, SafeHtml image, String dragHandlerClass);

        /**
         * The wrapper around the image vertically aligned to the middle.
         */
        @Template("<div class='{3}' class='draggable' style=\"position:absolute;{0}:0px;top:50%;line-height:0px;"
                + "margin-top:-{1}px;\">{2}</div>")
        SafeHtml imageWrapperMiddle(String direction, int halfHeight, SafeHtml image, String dragHandlerClass);

        /**
         * The wrapper around the image vertically aligned to the top.
         */
        @Template("<div class='{2}' style=\"position:absolute;{0}:0px;top:0px;line-height:0px;\">{1}</div>")
        SafeHtml imageWrapperTop(String direction, SafeHtml image, String dragHandlerClass);
    }

    /**
     * The default spacing between the icon and the text in pixels.
     */
    private static final int DEFAULT_SPACING = 6;

    private static Template template;

    private final Cell<C> cell;

    private final String direction = LocaleInfo.getCurrentLocale().isRTL() ? "right" : "left";

    private final SafeHtml iconHtml;

    private final int imageWidth;

    private final SafeHtml placeHolderHtml;

    private final DragSource dragSource;
    private final DragController dragController;

    /**
     * Construct a new {@link IconCellDecorator}. The icon and the content will be
     * middle aligned by default.
     *
     * @param dragSource     - drag source
     * @param dragController - drag controller
     * @param cell           the cell to decorate
     */
    public DraggableCellDecorator(DragSource dragSource, DragController dragController, Cell<C> cell) {
        this(dragSource, dragController, DNDResources.INSTANCE, cell, HasVerticalAlignment.ALIGN_MIDDLE,
                DEFAULT_SPACING);
    }

    /**
     * Construct a new {@link IconCellDecorator}.
     *
     * @param dragSource     - drag source
     * @param dragController - drag controller
     * @param dndResources   dndResources
     * @param cell           the cell to decorate
     * @param valign         the vertical alignment attribute of the contents
     * @param spacing        the pixel space between the icon and the cell
     */
    public DraggableCellDecorator(DragSource dragSource, DragController dragController, DNDResources dndResources,
            Cell<C> cell, HasVerticalAlignment.VerticalAlignmentConstant valign, int spacing) {
        this.dragSource = dragSource;
        this.dragController = dragController;
        if (template == null) {
            template = GWT.create(Template.class);
        }
        this.cell = cell;
        this.dragHandlerClass = dndResources.css().dragHandler();
        ImageResource icon = dndResources.images().dragHandle();
        this.iconHtml = getImageHtml(icon, valign, false);
        this.imageWidth = icon.getWidth() + spacing;
        this.placeHolderHtml = getImageHtml(icon, valign, true);
    }

    public boolean dependsOnSelection() {
        return cell.dependsOnSelection();
    }

    public Set<String> getConsumedEvents() {
        Set<String> consumedEvents = cell.getConsumedEvents();
        if (consumedEvents == null) {
            consumedEvents = new HashSet<String>();
        }
        if (!consumedEvents.contains(MOUSE_DOWN)) {
            consumedEvents.add(MOUSE_DOWN);
        }
        return consumedEvents;
    }

    public boolean handlesSelection() {
        return cell.handlesSelection();
    }

    public boolean isEditing(Context context, Element parent, T value) {
        return cell.isEditing(context, getCellParent(parent), getValue(value));
    }

    public void onBrowserEvent(Context context, Element parent, final T value, NativeEvent event,
            ValueUpdater<T> valueUpdater) {
        if (MOUSE_DOWN.equals(event.getType())) {
            EventTarget eventTarget = event.getEventTarget();
            if (Element.is(eventTarget)) {
                Element target = eventTarget.cast();
                Element wrapper = target.getParentElement();
                if (wrapper != null && dragHandlerClass.equals(wrapper.getClassName())) {
                    DNDContext dndContext = dragSource.startDragging(value);
                    dragController.dragStart(dndContext, parent);
                    event.stopPropagation();
                    event.preventDefault();
                    return;
                }
            }
        }
        final Element cellParent = getCellParent(parent);
        cell.onBrowserEvent(context, cellParent, getValue(value), event, getValueUpdater());
    }

    public void render(Context context, T value, SafeHtmlBuilder sb) {
        SafeHtmlBuilder cellBuilder = new SafeHtmlBuilder();
        cell.render(context, getValue(value), cellBuilder);

        sb.append(template.outerDiv(direction, imageWidth, isIconUsed(value) ? getIconHtml(value) : placeHolderHtml,
                cellBuilder.toSafeHtml()));
    }

    public boolean resetFocus(Context context, Element parent, T value) {
        return cell.resetFocus(context, getCellParent(parent), getValue(value));
    }

    public void setValue(Context context, Element parent, T value) {
        cell.setValue(context, getCellParent(parent), getValue(value));
    }

    /**
     * Get the safe HTML string that represents the icon. Override this method to
     * change the icon based on the value.
     *
     * @param value the value being rendered
     * @return the HTML string that represents the icon
     */
    @SuppressWarnings({ "UnusedDeclaration" })
    protected SafeHtml getIconHtml(T value) {
        return iconHtml;
    }

    /**
     * Check if the icon should be used for the value. If the icon should not be
     * used, a placeholder of the same size will be used instead. The default
     * implementations returns true.
     *
     * @param value the value being rendered
     * @return true to use the icon, false to use a placeholder
     */
    @SuppressWarnings({ "UnusedDeclaration" })
    protected boolean isIconUsed(T value) {
        return true;
    }

    /**
     * Get the HTML representation of an image. Visible for testing.
     *
     * @param res           the {@link ImageResource} to render as HTML
     * @param valign        the vertical alignment
     * @param isPlaceholder if true, do not include the background image
     * @return the rendered HTML
     */
    SafeHtml getImageHtml(ImageResource res, HasVerticalAlignment.VerticalAlignmentConstant valign,
            boolean isPlaceholder) {
        // Get the HTML for the image.
        SafeHtml image;
        if (isPlaceholder) {
            image = SafeHtmlUtils.fromTrustedString("<div></div>");
        } else {
            AbstractImagePrototype proto = AbstractImagePrototype.create(res);
            image = SafeHtmlUtils.fromTrustedString(proto.getHTML());
        }

        // Create the wrapper based on the vertical alignment.
        if (HasVerticalAlignment.ALIGN_TOP == valign) {
            return template.imageWrapperTop(direction, image, dragHandlerClass);
        } else if (HasVerticalAlignment.ALIGN_BOTTOM == valign) {
            return template.imageWrapperBottom(direction, image, dragHandlerClass);
        } else {
            int halfHeight = (int) Math.round(res.getHeight() / 2.0);
            return template.imageWrapperMiddle(direction, halfHeight, image, dragHandlerClass);
        }
    }

    /**
     * Get the parent element of the decorated cell.
     *
     * @param parent the parent of this cell
     * @return the decorated cell's parent
     */
    private Element getCellParent(Element parent) {
        final Element element = parent.getFirstChildElement().getFirstChildElement();
        return element.getChild(1).cast();
    }

    public abstract C getValue(T value);

    public ValueUpdater<C> getValueUpdater() {
        return null;
    }
}