Package gwtquery.plugins.draggable.client

Source Code of gwtquery.plugins.draggable.client.Draggable$StartCaller

/*
* 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.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HasHandlers;
import com.google.gwt.event.shared.UmbrellaException;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.query.client.plugins.MousePlugin;
import com.google.gwt.query.client.plugins.Plugin;
import com.google.gwt.query.client.plugins.events.GqEvent;
import gwtquery.plugins.draggable.client.DraggableOptions.DragFunction;
import gwtquery.plugins.draggable.client.DraggableOptions.HelperType;
import gwtquery.plugins.draggable.client.DraggableOptions.RevertOption;
import gwtquery.plugins.draggable.client.DraggableOptions.SelectFunction;
import gwtquery.plugins.draggable.client.events.BeforeDragStartEvent;
import gwtquery.plugins.draggable.client.events.DragContext;
import gwtquery.plugins.draggable.client.events.DragEvent;
import gwtquery.plugins.draggable.client.events.DragStartEvent;
import gwtquery.plugins.draggable.client.events.DragStopEvent;
import gwtquery.plugins.draggable.client.events.DraggableSelectedEvent;
import gwtquery.plugins.draggable.client.events.DraggableUnselectedEvent;
import gwtquery.plugins.draggable.client.plugins.CursorPlugin;
import gwtquery.plugins.draggable.client.plugins.DraggablePlugin;
import gwtquery.plugins.draggable.client.plugins.GroupSelectedPlugin;
import gwtquery.plugins.draggable.client.plugins.OpacityPlugin;
import gwtquery.plugins.draggable.client.plugins.ScrollPlugin;
import gwtquery.plugins.draggable.client.plugins.SnapPlugin;
import gwtquery.plugins.draggable.client.plugins.StackPlugin;
import gwtquery.plugins.draggable.client.plugins.ZIndexPlugin;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Draggable plugin for GwtQuery
*/
public class Draggable extends MousePlugin {

  /**
   * Interface containing all css classes used in this plug-in
   */
  public static interface CssClassNames {
    String GWT_DRAGGABLE = "gwtQuery-draggable";
    String GWT_DRAGGABLE_DISABLED = "gwtQuery-draggable-disabled";
    String GWT_DRAGGABLE_DRAGGING = "gwtQuery-draggable-dragging";
  }

  private class DragCaller extends StartCaller {

    public DragCaller(DragContext ctx, DraggableHandler dragHandler, GqEvent e) {
      super(ctx, dragHandler, e);
    }

    public void call(DraggablePlugin plugin) {
      plugin.onDrag(dragHandler, ctx, e);
    }
  }

  private static interface PluginCaller {
    void call(DraggablePlugin plugin);
  }

  private class StartCaller implements PluginCaller {
    protected DragContext ctx;
    protected DraggableHandler dragHandler;
    protected GqEvent e;

    public StartCaller(DragContext ctx, DraggableHandler dragHandler, GqEvent e) {
      this.ctx = ctx;
      this.dragHandler = dragHandler;
      this.e = e;
    }

    public void call(DraggablePlugin plugin) {
      plugin.onStart(dragHandler, ctx, e);
    }
  }

  private class StopCaller extends StartCaller {

    public StopCaller(DragContext ctx, DraggableHandler dragHandler, GqEvent e) {
      super(ctx, dragHandler, e);
    }

    public void call(DraggablePlugin plugin) {
      plugin.onStop(dragHandler, ctx, e);
    }
  }

  public static final Class<Draggable> Draggable = Draggable.class;

  public static final String DRAGGABLE_HANDLER_KEY = "draggableHandler";

  static List<Element> selectedDraggables;

  private static Map<String, DraggablePlugin> draggablePlugins;

  // Register the plugin in GQuery
  static {
    GQuery.registerPlugin(Draggable.class, new Plugin<Draggable>() {
      public Draggable init(GQuery gq) {
        return new Draggable(gq);
      }
    });

    // register the different draggable plugins
    registerDraggablePlugin(new OpacityPlugin());
    registerDraggablePlugin(new ScrollPlugin());
    registerDraggablePlugin(new CursorPlugin());
    registerDraggablePlugin(new ZIndexPlugin());
    registerDraggablePlugin(new StackPlugin());
    registerDraggablePlugin(new SnapPlugin());
    registerDraggablePlugin(new GroupSelectedPlugin());

    selectedDraggables = new ArrayList<Element>();
  }

  /**
   * Register a draggable plugin that will be called during the drag operation
   *
   * @param plugin
   */
  public static void registerDraggablePlugin(DraggablePlugin plugin) {
    if (draggablePlugins == null) {
      draggablePlugins = new HashMap<String, DraggablePlugin>();
    }
    draggablePlugins.put(plugin.getName(), plugin);
  }

  private static void trigger(GwtEvent<?> e, DragFunction callback,
                              DragContext dragContext, HasHandlers handlerManager) {
    if (handlerManager != null && e != null) {
      handlerManager.fireEvent(e);
    }
    if (callback != null) {
      callback.f(dragContext);
    }
  }

  private boolean dragStart = false;


  /**
   * Constructor
   *
   * @param gq
   */
  protected Draggable(GQuery gq) {
    super(gq);
  }

  /**
   * Remove the draggable behavior to the selected elements. This method
   * releases resources used by the plugin and should be called when an element
   * is removed of the DOM.
   *
   * @return
   */
  public Draggable destroy() {

    for (Element e : elements()) {
      selectedDraggables.remove(e);

      $(e).removeData(DRAGGABLE_HANDLER_KEY).removeClass(
          CssClassNames.GWT_DRAGGABLE, CssClassNames.GWT_DRAGGABLE_DISABLED,
          CssClassNames.GWT_DRAGGABLE_DRAGGING);
    }
    destroyMouseHandler();
    return this;
  }

  /**
   * Make the selected elements draggable with default options
   *
   * @return
   */
  public Draggable draggable() {
    return draggable(new DraggableOptions(), null);
  }

  /**
   * Make the selected elements draggable by using the <code>options</code>
   *
   * @param options options to use during the drag operation
   * @return
   */
  public Draggable draggable(DraggableOptions options) {
    return draggable(options, null);
  }

  /**
   * Make the selected elements draggable by using the <code>options</code>. All
   * drag events will be fired on the <code>eventBus</code>
   *
   * @param options  options to use during the drag operation
   * @param eventBus The eventBus to use to fire events.
   * @return
   */
  public Draggable draggable(DraggableOptions options, HasHandlers eventBus) {

    this.eventBus = eventBus;

    initMouseHandler(options);

    for (Element e : elements()) {
      if (options.getHelperType() == HelperType.ORIGINAL
          && !positionIsFixedAbsoluteOrRelative(e.getStyle().getPosition())) {
        e.getStyle().setPosition(Position.RELATIVE);
      }
      e.addClassName(CssClassNames.GWT_DRAGGABLE);

      if (options.isDisabled()) {
        e.addClassName(CssClassNames.GWT_DRAGGABLE_DISABLED);
      }
      DraggableHandler handler = new DraggableHandler(options);
      $(e).data(DRAGGABLE_HANDLER_KEY, handler);
    }

    return this;
  }

  /**
   * Make the selected elements draggable with default options. All drag events
   * will be fired on the <code>eventBus</code>
   *
   * @param eventBus The eventBus to use to fire events.
   * @return
   */
  public Draggable draggable(HasHandlers eventBus) {
    return draggable(new DraggableOptions(), eventBus);
  }

  /**
   * Get the {@link DraggableOptions} for the first element.
   *
   * @return
   */
  public DraggableOptions options() {

    DraggableHandler handler = data(DRAGGABLE_HANDLER_KEY,
        DraggableHandler.class);
    if (handler != null) {
      return handler.getOptions();
    }

    return null;
  }

  /**
   * Set the DraggableOptions on each element.
   *
   * @param options
   * @return
   */
  public Draggable options(DraggableOptions options) {

    for (Element e : elements()) {
      DraggableHandler handler = $(e).data(DRAGGABLE_HANDLER_KEY,
          DraggableHandler.class);
      if (handler != null) {
        handler.setOptions(options);
      }
    }
    return this;
  }

  @Override
  protected String getPluginName() {
    return "draggable";
  }

  @Override
  protected boolean mouseCapture(Element draggable, GqEvent event) {

    DraggableHandler handler = $(draggable).data(DRAGGABLE_HANDLER_KEY,
        DraggableHandler.class);
    return handler != null && handler.getHelper() == null
        && !handler.getOptions().isDisabled()
        && isHandleClicked(draggable, event);
  }

  @Override
  protected boolean mouseClick(Element element, GqEvent event) {
    // react on click event only if no metakey is pressed, if no drag occurs and
    // if more than one element are selected

    if (!event.isMetaKeyPressed() && !dragStart
        && selectedDraggables.size() > 1) {
      DraggableHandler dragHandler = DraggableHandler.getInstance(element);
      DraggableOptions options = dragHandler.getOptions();
      unselectAll();
      select(element, options.getSelectedClassName());
    }

    dragStart = false;

    return !event.isMetaKeyPressed();
  }

  @Override
  protected boolean mouseDown(Element draggable, GqEvent event) {

    DraggableHandler dragHandler = DraggableHandler.getInstance(draggable);
    DraggableOptions options = dragHandler.getOptions();

    if (!options.isMultipleSelection()) {
      // ensure all previously selected element are unselected
      unselectAll();

    } else {

      if (event.isMetaKeyPressed()) {

        if (selectedDraggables.contains(draggable)) {

          unselect(draggable);

        } else if (canBeSelected(draggable, dragHandler)) {
          select(draggable, options.getSelectedClassName());
        }
      } else if (!selectedDraggables.contains(draggable)) {
        // if no meta key pressed and if the draggable is not selected ,
        // deselect all and select the draggable.
        unselectAll();
        select(draggable, options.getSelectedClassName());

      }
    }
    return super.mouseDown(draggable, event) && !event.isMetaKeyPressed();
  }

  @Override
  protected boolean mouseDrag(Element currentDraggable, GqEvent event) {
    dragStart = true;

    boolean result = false;

    DragContext ctx = new DragContext(currentDraggable, currentDraggable,
        selectedDraggables);
    result |= mouseDragImpl(ctx,
        DraggableHandler.getInstance(currentDraggable), event, false);

    for (Element draggable : selectedDraggables) {
      if (draggable != currentDraggable) {
        ctx = new DragContext(draggable, currentDraggable, selectedDraggables);
        result |= mouseDragImpl(ctx, DraggableHandler.getInstance(draggable),
            event, false);
      }

    }

    return result;
  }

  @Override
  protected boolean mouseStart(Element currentDraggable, GqEvent event) {

    boolean result = false;

    DraggableHandler dragHandler = getHandler(currentDraggable);
    DraggableOptions options = dragHandler.getOptions();
    // if the currentDraggable have not the same scope has the other selected
    // draggable or doesn't accept multi selection, unselect all
    if (!canBeSelected(currentDraggable, dragHandler)
        || !options.isMultipleSelection()) {
      unselectAll();
    }

    // if the currentDraggable is not yet selected and can be selected,
    // select it
    if (!selectedDraggables.contains(currentDraggable)
        && canBeSelected(currentDraggable, dragHandler)
        && options.isMultipleSelection()) {
      GWT.log("select element");
      select(currentDraggable, options.getSelectedClassName());
    }

    // select other draggable elements if select options is set
    SelectFunction selectFunction = options.getSelect();
    if (selectFunction != null) {
      GQuery followers = selectFunction.selectElements();
      followers.each(new Function() {
        @Override
        public void f(Element e) {
          DraggableHandler handler = DraggableHandler.getInstance(e);
          if (handler != null) {
            GWT.log("Select automatic selected element " + e.getId());
            select(e, handler.getOptions().getSelectedClassName());
          }
        }
      });
    }

    // first call mouseStart for the initial draggable
    DragContext ctx = new DragContext(currentDraggable, currentDraggable,
        selectedDraggables);
    result |= mouseStartImpl(ctx, event);

    // call mouseStartImpl for the others
    for (Element draggable : selectedDraggables) {
      if (draggable != currentDraggable) {
        ctx = new DragContext(draggable, currentDraggable, selectedDraggables);
        result |= mouseStartImpl(ctx, event);
      }
    }

    return result;
  }

  @Override
  protected boolean mouseStop(Element initialDraggable, final GqEvent event) {
    boolean result = false;

    DragContext ctx = new DragContext(initialDraggable, initialDraggable,
        selectedDraggables);
    result |= mouseStopImpl(ctx, event);

    for (Element draggable : selectedDraggables) {
      if (draggable != initialDraggable) {
        ctx = new DragContext(draggable, initialDraggable, selectedDraggables);
        result |= mouseStopImpl(ctx, event);
      }

    }

    DraggableOptions options = getOptions(initialDraggable);

    // deselect automatic selected elements
    // select other draggable elements if select options is set
    SelectFunction selectFunction = options.getSelect();
    if (selectFunction != null) {
      GQuery followers = selectFunction.selectElements();
      for (Element e : followers.elements()) {
        unselect(e);
      }
    }

    return result;
  }

  private void callPlugins(PluginCaller caller, DraggableOptions options) {
    for (DraggablePlugin plugin : draggablePlugins.values()) {
      if (plugin.hasToBeExecuted(options)) {
        caller.call(plugin);
      }
    }
  }

  private boolean canBeSelected(Element draggable, DraggableHandler handler) {
    if (selectedDraggables.isEmpty()) {

      return true;
    }

    String selectedScope = DraggableHandler.getInstance(
        selectedDraggables.get(0)).getOptions().getScope();
    String currentScope = handler.getOptions().getScope();

    return currentScope.equals(selectedScope);

  }

  private DragAndDropManager getDragAndDropManager() {
    return DragAndDropManager.getInstance();
  }

  private DraggableHandler getHandler(Element draggable) {

    return DraggableHandler.getInstance(draggable);
  }

  private DraggableOptions getOptions(Element draggable) {
    DraggableHandler handler = getHandler(draggable);
    return handler != null ? handler.getOptions() : null;
  }

  private boolean isHandleClicked(Element draggable, final GqEvent event) {
    DraggableOptions options = getOptions(draggable);
    // if no handle or if specified handle is not inside the draggable element,
    // continue
    if (options.getHandle() == null
        || $(options.getHandle(), draggable).length() == 0) {
      return true;
    }

    // OK, we have a valid handle, check if we are clicking on the handle object
    // or one of its descendants
    GQuery handleAndDescendant = $(options.getHandle(), draggable).find("*").andSelf();
    for (Element e : handleAndDescendant.elements()) {
      if (e == event.getEventTarget().cast()) {
        return true;
      }
    }
    return false;
  }

  /**
   * implementation of mouse drag
   */
  private boolean mouseDragImpl(DragContext ctx, DraggableHandler dragHandler,
                                GqEvent event, boolean noPropagation) {
    Element draggable = ctx.getDraggable();

    dragHandler.regeneratePositions(event);

    if (!noPropagation) {

      callPlugins(new DragCaller(ctx, dragHandler, event),
          dragHandler.getOptions());

      try {
        trigger(new DragEvent(ctx), dragHandler.getOptions().getOnDrag(), ctx);
      } catch (UmbrellaException e) {
        for (Throwable t : e.getCauses()) {
          if (t instanceof StopDragException) {
            mouseStop(draggable, event);
            return false;
          }
        }

      }
    }

    dragHandler.moveHelper(noPropagation);

    if (getDragAndDropManager().isHandleDroppable(ctx)) {
      getDragAndDropManager().drag(ctx, event);
    }

    return false;
  }

  private boolean mouseStartImpl(final DragContext ctx, final GqEvent event) {

    Element draggable = ctx.getDraggable();
    final DraggableHandler dragHandler = DraggableHandler.getInstance(draggable);
    DraggableOptions options = dragHandler.getOptions();

    try {
      trigger(new BeforeDragStartEvent(ctx), options.getOnBeforeDragStart(),
          ctx);
    } catch (UmbrellaException e) {
      for (Throwable t : e.getCauses()) {
        if (t instanceof StopDragException) {
          return false;
        }
      }

    }

    dragHandler.createHelper(draggable, event);
    dragHandler.cacheHelperSize();

    dragHandler.initialize(draggable, event);
    callPlugins(new StartCaller(ctx, dragHandler, event), options);

    try {
      trigger(new DragStartEvent(ctx), options.getOnDragStart(), ctx);
    } catch (UmbrellaException e) {
      for (Throwable t : e.getCauses()) {
        if (t instanceof StopDragException) {
          mouseStop(draggable, event);
          return false;
        }
      }

    }

    dragHandler.cacheHelperSize();

    if (getDragAndDropManager().isHandleDroppable(ctx)) {
      getDragAndDropManager().initialize(ctx, event);
    }

    dragHandler.getHelper().addClass(CssClassNames.GWT_DRAGGABLE_DRAGGING);
    // defer the mouseDragImpl to be sure that all selected draggable are
    // initialized
    Scheduler.get().scheduleDeferred(new ScheduledCommand() {

      public void execute() {
        mouseDragImpl(ctx, dragHandler, event, true);

      }
    });

    return true;
  }

  private boolean mouseStopImpl(final DragContext ctx, final GqEvent event) {
    final Element draggable = ctx.getDraggable();
    final DraggableHandler handler = getHandler(draggable);
    final DraggableOptions options = handler.getOptions();

    boolean dropped = isDropped(ctx, event);

    if (draggable == null) {
      return false;
    }

    RevertOption revertOption = options.getRevert();
    if (revertOption.doRevert(dropped)) {
      handler.revertToOriginalPosition(new Function() {
        @Override
        public void f(Element e) {
          callPlugins(new StopCaller(ctx, handler, event), options);
          triggerDragStop(ctx, options);

          handler.clear(draggable);
        }
      });
      return false;
    }

    callPlugins(new StopCaller(ctx, handler, event), options);
    triggerDragStop(ctx, options);

    handler.clear(draggable);

    return false;
  }

  private boolean isDropped(DragContext ctx, GqEvent event) {

    boolean dropped = false;

    if (ctx.getDraggable() == ctx.getInitialDraggable()) {

      if (getDragAndDropManager().isHandleDroppable(ctx)) {
        dropped = getDragAndDropManager().drop(ctx, event);
      }

      $(ctx.getInitialDraggable()).data("_is_dropped", dropped);

    } else {
      dropped = $(ctx.getInitialDraggable()).data("_is_dropped", Boolean.class);

    }
    return dropped;
  }

  private native boolean positionIsFixedAbsoluteOrRelative(String position) /*-{
      return (/^(?:r|a|f)/).test(position);
  }-*/;

  private void select(Element draggable, String selectedCssClass) {
    if (selectedDraggables.contains(draggable)) {
      return;
    }
    selectedDraggables.add(draggable);

    if (selectedCssClass != null) {
      draggable.addClassName(selectedCssClass);
    }
    GWT.log("trigger DraggableSelectedEvent");
    trigger(new DraggableSelectedEvent(draggable),
        getOptions(draggable).getOnSelected(), draggable);
  }

  private void unselect(Element draggable) {

    DraggableHandler handler = DraggableHandler.getInstance(draggable);
    DraggableOptions options = handler.getOptions();

    if (options.getSelectedClassName() != null) {
      draggable.removeClassName(options.getSelectedClassName());
    }
    GWT.log("trigger DraggableUnselectedEvent");
    selectedDraggables.remove(draggable);
    trigger(new DraggableUnselectedEvent(draggable), options.getOnUnselected(),
        draggable);
  }

  private void trigger(GwtEvent<?> e, DragFunction callback,
                       DragContext dragContext) {
    trigger(e, callback, dragContext, eventBus);
  }

  /**
   * Use a deferred command to be sure that this event is trigger after the
   * possible drop event.
   *
   * @param draggable
   * @param options
   */
  private void triggerDragStop(final DragContext ctx,
                               final DraggableOptions options) {
    Scheduler.get().scheduleDeferred(new ScheduledCommand() {

      public void execute() {
        trigger(new DragStopEvent(ctx), options.getOnDragStop(), ctx);

      }
    });
  }

  private void unselectAll() {
    // TODO concurent modification list !
    while (selectedDraggables.size() != 0) {
      unselect(selectedDraggables.get(0));
    }

  }

}
TOP

Related Classes of gwtquery.plugins.draggable.client.Draggable$StartCaller

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.