Package com.sencha.gxt.cell.core.client.form

Source Code of com.sencha.gxt.cell.core.client.form.TriggerFieldCell$MouseDownHandler

/**
* Sencha GXT 3.1.0-beta - Sencha for GWT
* Copyright(c) 2007-2014, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.cell.core.client.form;

import java.text.ParseException;
import java.util.logging.Logger;

import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.GXTLogConfiguration;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.DelayedTask;
import com.sencha.gxt.widget.core.client.event.ParseErrorEvent;
import com.sencha.gxt.widget.core.client.event.TriggerClickEvent;

/**
* A base cell for an input field and a clickable trigger. The purpose of the trigger is defined by the
* derived class (e.g. displaying a drop down or modifying the value of the input field).
*
* @param <T> the cell model type
*/
public class TriggerFieldCell<T> extends ValueBaseInputCell<T> {

  public interface TriggerFieldAppearance extends ValueBaseFieldAppearance {
    void onResize(XElement parent, int width, int height, boolean hideTrigger);

    void onTriggerClick(XElement parent, boolean click);

    void onTriggerOver(XElement parent, boolean over);

    void render(SafeHtmlBuilder sb, String value, FieldAppearanceOptions options);

    void setEditable(XElement parent, boolean editable);

    boolean triggerIsOrHasChild(XElement parent, Element target);

  }

  private class MouseDownHandler extends DelayedTask implements NativePreviewHandler {

    private final Context context;
    private final XElement parent;
    private final ValueUpdater<T> updater;
    private final T value;
    private HandlerRegistration reg;

    public MouseDownHandler(Context context, XElement parent, T value, ValueUpdater<T> updater) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("add event preview");
      }

      this.context = context;
      this.parent = parent;
      this.value = value;
      this.updater = updater;
      reg = Event.addNativePreviewHandler(this);
      delay(100);
    }

    @Override
    public void onExecute() {
      Element activeElement = XDOM.getActiveElement();
      if (parent.getOffsetParent() == null ||
              (activeElement != null &&
                      !isFocusedWithTarget(parent, activeElement))) {
        //either we are invisible, or someone else has focus, drop the appearance of focus and trigger the blur
        triggerBlur(context, parent, value, updater);
        onBlur(context, parent, value, null, updater);
      } else {
        delay(100);
      }
    }

    @Override
    public void onPreviewNativeEvent(NativePreviewEvent event) {
      NativeEvent e = event.getNativeEvent();
      XElement target = event.getNativeEvent().getEventTarget().cast();
      if ("mousedown".equals(e.getType())) {
        if (GXTLogConfiguration.loggingIsEnabled()) {
          logger.finest("preview mouse down");
        }

        if (!isFocusedWithTarget(parent.<XElement>cast(), target)) {
          if (GXTLogConfiguration.loggingIsEnabled()) {
            logger.finest("preview mouse down not a focus click, remove preview");
          }

          remove();
          lastValueUpdater = updater;
          lastContext = context;

          // rather than calling triggerBlur, we just set the mimic flag and
          // wait
          // for onBlur to be called, then we finish the edit
          mimicking = false;
        }
      }
    }
    public void remove() {
      cancel();
      reg.removeHandler();
      reg = null;
      focusManagerRegistration = null;
    }
  }

  protected static TriggerFieldCell<?> focusedCell;
  protected boolean finishEditOnEnter = true;
 
  private static void ensureBlur() {
    if (focusedCell != null) {
      focusedCell.doTriggerBlur();
    }
  }

  protected boolean mimicking;

  private boolean editable = true;
  private boolean hideTrigger;
  private boolean monitorTab = true;
  private MouseDownHandler focusManagerRegistration;
  private static Logger logger = Logger.getLogger(TriggerFieldCell.class.getName());

  /**
   * Creates a new trigger cell instance.
   */
  public TriggerFieldCell() {
    this(GWT.<TriggerFieldAppearance> create(TriggerFieldAppearance.class));
  }

  /**
   * Creates a new trigger cell instance.
   *
   * @param appearance the appearance
   */
  public TriggerFieldCell(TriggerFieldAppearance appearance) {
    super(appearance);
    finishEditOnBlur = false;
  }

  @Override
  public void finishEditing(final Element parent, T value, Object key, ValueUpdater<T> valueUpdater) {
    if (GXTLogConfiguration.loggingIsEnabled()) {
      logger.finest("finishEditing");
    }

    if (focusManagerRegistration != null) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("TriggerFieldCell finishEditing remove event preview");
      }

      focusManagerRegistration.remove();
    }

    String newValue = getText(XElement.as(parent));

    // Get the view data.
    FieldViewData vd = getViewData(key);
    if (vd == null) {
      vd = new FieldViewData(value == null ? "" : getPropertyEditor().render(value));
      setViewData(key, vd);
    }
    vd.setCurrentValue(newValue);

    // Fire the value updater if the value has changed.
    if (valueUpdater != null && !vd.getCurrentValue().equals(vd.getLastValue())) {
      vd.setLastValue(newValue);
      try {
        if (GXTLogConfiguration.loggingIsEnabled()) {
          logger.finest("finishEditing saving value: " + newValue);
        }

        if ("".equals(newValue)) {
          value = null;

          valueUpdater.update(null);
        } else {
          value = getPropertyEditor().parse(newValue);

          valueUpdater.update(value);

          // parsing may have changed value
          getInputElement(parent).setValue(value == null ? "" : getPropertyEditor().render(value));
        }

      } catch (ParseException e) {
        if (GXTLogConfiguration.loggingIsEnabled()) {
          logger.finest("finishEditing parseError: " + e.getMessage());
        }

        if (isClearValueOnParseError()) {
          vd.setCurrentValue("");
          valueUpdater.update(null);
          setText(parent.<XElement> cast(), "");
        }

        fireEvent(new ParseErrorEvent(newValue, e));
      }
    } else {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("finishEditing value not changed: " + newValue + " old: " + vd.getLastValue());
      }

    }

    clearViewData(key);
    clearFocusKey();
    focusedCell = null;

    // not calling super as GWT code does input.blur() which breaks navigation between fields
  }

  @Override
  public TriggerFieldAppearance getAppearance() {
    return (TriggerFieldAppearance) super.getAppearance();
  }

  /**
   * Returns true if the field is editable.
   *
   * @return true if editable
   */
  public boolean isEditable() {
    return editable;
  }

  /**
   * Returns the finish on enter key state.
   *
   * @return {@code true} if editing finished on enter key
   */
  public boolean isFinishEditOnEnter() {
    return finishEditOnEnter;
  }

  /**
   * Returns {@code true} if the trigger is hidden.
   *
   * @return {@code true} if hidden
   */
  public boolean isHideTrigger() {
    return hideTrigger;
  }

  /**
   * Returns true if tab key events are being monitored.
   *
   * @return true if monitoring
   */
  public boolean isMonitorTab() {
    return monitorTab;
  }

  public void onBrowserEvent(Context context, Element parent, T value, NativeEvent event, ValueUpdater<T> valueUpdater) {
    Element target = event.getEventTarget().cast();
    if (!parent.isOrHasChild(target)) {
      return;
    }
    if (isReadOnly() || isDisabled()) {
      return;
    }
    super.onBrowserEvent(context, parent, value, event, valueUpdater);
    String eventType = event.getType();
    Object key = context.getKey();
    if ("change".equals(eventType)) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("onBrowserEvent change event fired mimmicking = " + mimicking);
      }
      if (!mimicking) {
        finishEditing(parent, value, key, valueUpdater);
      }
    } else if ("click".equals(eventType)) {
      onClick(context, parent.<XElement> cast(), event, value, valueUpdater);
    } else if ("keyup".equals(eventType)) {
      // Record keys as they are typed.
      FieldViewData vd = getViewData(key);
      if (vd == null) {
        vd = new FieldViewData(value == null ? "" : getPropertyEditor().render(value));
        setViewData(key, vd);
      }
      vd.setCurrentValue(getText(XElement.as(parent)));
    }
  }

  @Override
  public void render(Context context, T value, SafeHtmlBuilder sb) {
    String v = "";
    if (value != null) {
      v = getPropertyEditor().render(value);
    }

    FieldViewData viewData = checkViewData(context, v);

    FieldAppearanceOptions options = new FieldAppearanceOptions(width, height, isReadOnly());
    options.setEmptyText(getEmptyText());
    options.setHideTrigger(isHideTrigger());
    options.setEditable(editable);
    options.setName(name);
    options.setDisabled(isDisabled());

    String s = (viewData != null) ? viewData.getCurrentValue() : v;
    getAppearance().render(sb, s == null ? "" : s, options);

  }

  public void setEditable(XElement parent, boolean editable) {
    this.editable = editable;

    getAppearance().setEditable(parent, editable);

    InputElement inputElem = getAppearance().getInputElement(parent).cast();
    if (!isReadOnly()) {
      inputElem.setReadOnly(!editable);
    }
  }

  /**
   * Determines if the current edit should be completed when the enter key is pressed (defaults to true). When enabled,
   * the cell will be blurred causing {@link #finishEditing(Element, Object, Object, ValueUpdater)} to be called.
   *
   * @param finishEditOnEnter {@code true} to call {@link #finishEditing(Element, Object, Object, ValueUpdater)} when
   *          enter key is pressed
   */
  public void setFinishEditOnEnter(boolean finishEditOnEnter) {
    this.finishEditOnEnter = finishEditOnEnter;
  }

  /**
   * Controls the visibility of the cells trigger.
   *
   * @param hideTrigger {@code true} to hide
   */
  public void setHideTrigger(boolean hideTrigger) {
    this.hideTrigger = hideTrigger;
  }

  /**
   * True to monitor tab key events to force the bluring of the field (defaults to true).
   *
   * @param monitorTab true to monitor tab key events
   */
  public void setMonitorTab(boolean monitorTab) {
    this.monitorTab = monitorTab;
  }

  @Override
  public void setSize(XElement parent, int width, int height) {
    super.setSize(parent, width, height);
    getAppearance().onResize(parent, width, height, isHideTrigger());
  }

  @Override
  protected void clearContext() {
    super.clearContext();
    focusedCell = null;
  }

  /**
   * Checks if an element can be focused within the current parent. Allows subclasses to let external elements
   * take the focus and still consider this field to be focused.
   *
   * @param parent the parent element of the active cell
   * @param target the element which may have or receive focus
   * @return true of this cell can still have logical focus if the target has dom focus
   */
  protected boolean isFocusedWithTarget(Element parent, Element target) {
    return parent.isOrHasChild(target);
  }

  @Override
  protected void onBlur(Context context, XElement parent, T value, NativeEvent event, ValueUpdater<T> valueUpdater) {
    // do nothing
    if (GXTLogConfiguration.loggingIsEnabled()) {
      logger.finest("TriggerFieldCell onBlur - mimicking = " + mimicking);
    }

    if (!mimicking) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("TriggerFieldCell onBlur no mimicking so calling super");
      }

      finishEditing(parent, value, context.getKey(), valueUpdater);
      super.onBlur(context, parent, value, event, valueUpdater);
    }
  }

  protected void onClick(Context context, XElement parent, NativeEvent event, T value, ValueUpdater<T> updater) {
    Element target = event.getEventTarget().cast();

    if (!isReadOnly() && (!isEditable() && getInputElement(parent).isOrHasChild(target))
        || getAppearance().triggerIsOrHasChild(parent.<XElement> cast(), target)) {
      onTriggerClick(context, parent, event, value, updater);
    }

    if (!editable) {
      event.preventDefault();
      event.stopPropagation();
    }
  }

  protected void onEnterKeyDown(Context context, Element parent, T value, NativeEvent event,
      ValueUpdater<T> valueUpdater) {
    Element target = event.getEventTarget().cast();
    if (!finishEditOnEnter) {
      event.preventDefault();
      event.stopPropagation();
      return;
    }
    if (getInputElement(parent).isOrHasChild(target)) {
      mimicking = false;
      getInputElement(parent).blur();
    } else {
      super.onEnterKeyDown(context, parent, value, event, valueUpdater);
    }
  }

  @Override
  protected void onFocus(Context context, XElement parent, T value, NativeEvent event, ValueUpdater<T> valueUpdater) {
    if (GXTLogConfiguration.loggingIsEnabled()) {
      logger.finest("TriggerFieldCell onFocus " + parent.getId());
    }

    if (mimicking) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("TriggerFieldCell onFocus mimic = true so exiting " + parent.getId());
      }

      return;
    }

    if (!mimicking) {
      // EXTGWT-2183 IE8 is not firing blur when switching focus to another
      // application then clicking another trigger cell to regain focus
      // me manually finish the edit. we can't call onBlur has the attempt the
      // blur the field throws an exception
      if ((GXT.isIE8() || GXT.isWebKit()) && focusedCell != null) {
        if (GXTLogConfiguration.loggingIsEnabled()) {
          logger.finest("TriggerFieldCell onFocus manually finishing edit as blur not fired");
        }

        finishEditing(parent, value, context.getKey(), valueUpdater);

        return;
      }

      super.onFocus(context, parent, value, event, valueUpdater);
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("onFocus setting mimicking to true " + parent.getId());
      }

      saveContext(context, parent, event, valueUpdater, value);
      mimicking = true;
      getAppearance().onFocus(parent, true);
      focusManagerRegistration = new MouseDownHandler(context, parent, value, valueUpdater);
    }
  }

  protected void onKeyDown(final Context context, final Element parent, final T value, NativeEvent event,
      final ValueUpdater<T> valueUpdater) {
    super.onKeyDown(context, parent, value, event, valueUpdater);

    int key = event.getKeyCode();

    // IE8 backspace causes navigation away from page when input is read only
    if (!isEditable() && key == KeyCodes.KEY_BACKSPACE) {
      event.preventDefault();
      event.stopPropagation();
    }

    if (monitorTab && event.getKeyCode() == KeyCodes.KEY_TAB) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("onKeyDown Tab " + parent.getId());
      }

      mimicking = false;
    }
  }

  @Override
  protected void onMouseDown(XElement parent, NativeEvent event) {
    super.onMouseDown(parent, event);

    Element target = event.getEventTarget().cast();
    if (!isReadOnly() && (!isEditable() && getInputElement(parent).isOrHasChild(target))
            || getAppearance().triggerIsOrHasChild(parent, target)) {
      getAppearance().onTriggerClick(parent, true);
      event.preventDefault();
    }
  }

  @Override
  protected void onMouseOut(XElement parent, NativeEvent event) {
    super.onMouseOut(parent, event);
    XElement target = event.getEventTarget().cast();
    if (getAppearance().triggerIsOrHasChild(parent.<XElement> cast(), target)) {
      getAppearance().onTriggerOver(parent.<XElement> cast(), false);
    }
  }

  @Override
  protected void onMouseOver(XElement parent, NativeEvent event) {
    super.onMouseOver(parent, event);
    XElement target = event.getEventTarget().cast();
    if (getAppearance().triggerIsOrHasChild(parent.<XElement> cast(), target)) {
      getAppearance().onTriggerOver(parent.<XElement> cast(), true);
    }
  }

  protected void onTriggerClick(Context context, XElement parent, NativeEvent event, T value, ValueUpdater<T> updater) {
    fireEvent(context, new TriggerClickEvent());
    getAppearance().onTriggerClick(parent, false);
  }

  protected void saveContext(Context context, Element parent, NativeEvent event, ValueUpdater<T> valueUpdater, T value) {
    super.saveContext(context, parent, event, valueUpdater, value);
    focusedCell = this;
  }

  protected void triggerBlur(Context context, final XElement parent, T value, ValueUpdater<T> valueUpdater) {
    if (GXTLogConfiguration.loggingIsEnabled()) {
      logger.finest("TriggerFieldCell triggerBlur " + parent.getId());
      logger.finest("TriggerFieldCell triggerBlur mimicking = false");
    }

    if (focusManagerRegistration != null) {
      if (GXTLogConfiguration.loggingIsEnabled()) {
        logger.finest("TriggerFieldCell triggerBlur remove event preview");
      }

      focusManagerRegistration.remove();
    }

    mimicking = false;

    finishEditing(parent, value, context.getKey(), valueUpdater);
  }

  protected boolean validateBlur(Element target) {
    return true;
  }

  void doTriggerBlur() {
    triggerBlur(lastContext, lastParent, lastValue, lastValueUpdater);
    onBlur(lastContext, lastParent, lastValue, null, lastValueUpdater);
  }

  private native void clearFocusKey() /*-{
    this.@com.google.gwt.cell.client.AbstractInputCell::focusedKey = null;
  }-*/;

TOP

Related Classes of com.sencha.gxt.cell.core.client.form.TriggerFieldCell$MouseDownHandler

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.