Package com.google.gwt.user.client.ui

Source Code of com.google.gwt.user.client.ui.SuggestBox

/*
* Copyright 2007 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 com.google.gwt.user.client.ui;

import com.google.gwt.user.client.ui.SuggestOracle.Callback;
import com.google.gwt.user.client.ui.SuggestOracle.Request;
import com.google.gwt.user.client.ui.SuggestOracle.Response;
import com.google.gwt.user.client.ui.impl.ItemPickerDropDownImpl;
import com.google.gwt.user.client.ui.impl.SuggestPickerImpl;

import java.util.Collection;

/**
* A {@link SuggestBox} is a text box or text area which displays a
* pre-configured set of selections that match the user's input.
*
* Each {@link SuggestBox} is associated with a single {@link SuggestOracle}.
* The {@link SuggestOracle} is used to provide a set of selections given a
* specific query string.
*
* <p>
* By default, the {@link SuggestBox} uses a {@link MultiWordSuggestOracle} as
* its oracle. Below we show how a {@link MultiWordSuggestOracle} can be
* configured:
* </p>
*
* <pre>
*   MultiWordSuggestOracle oracle = new MultiWordSuggestOracle(); 
*   oracle.add("Cat");
*   oracle.add("Dog");
*   oracle.add("Horse");
*   oracle.add("Canary");
*  
*   SuggestBox box = new SuggestBox(oracle);
* </pre>
*
* Using the example above, if the user types "C" into the text widget, the
* oracle will configure the suggestions with the "Cat" and "Canary"
* suggestions. Specifically, whenever the user types a key into the text
* widget, the value is submitted to the <code>MultiWordSuggestOracle</code>.
*
* <p>
* <img class='gallery' src='SuggestBox.png'/>
* </p>
*
* <h3>CSS Style Rules</h3>
* <ul class='css'>
* <li>.gwt-SuggestBox { the suggest box itself }</li>
* <li>.gwt-SuggestBoxPopup { the suggestion popup }</li>
* <li>.gwt-SuggestBoxPopup .item { an unselected suggestion }</li>
* <li>.gwt-SuggestBoxPopup .item-selected { a selected suggestion }</li>
* </ul>
*
* @see SuggestOracle, MultiWordSuggestOracle, TextBoxBase
*/
public final class SuggestBox extends Composite implements HasText, HasFocus,
    SourcesClickEvents, SourcesFocusEvents, SourcesChangeEvents,
    SourcesKeyboardEvents {

  private static final String STYLENAME_DEFAULT = "gwt-SuggestBox";

  private int limit = 20;
  private int selectStart;
  private int selectEnd;
  private SuggestOracle oracle;
  private char[] separators;
  private String currentValue;
  private final PopupPanel popup;
  private final SuggestPickerImpl picker;
  private final TextBoxBase box;
  private DelegatingClickListenerCollection clickListeners;
  private DelegatingChangeListenerCollection changeListeners;
  private DelegatingFocusListenerCollection focusListeners;
  private DelegatingKeyboardListenerCollection keyboardListeners;
  private String separatorPadding = "";

  private final Callback callBack = new Callback() {
    public void onSuggestionsReady(Request request, Response response) {
      showSuggestions(response.getSuggestions());
    }
  };

  /**
   * Constructor for {@link SuggestBox}. Creates a
   * {@link MultiWordSuggestOracle} and {@link TextBox} to use with this
   * {@link SuggestBox}.
   */
  public SuggestBox() {
    this(new MultiWordSuggestOracle());
  }

  /**
   * Constructor for {@link SuggestBox}. Creates a {@link TextBox} to use with
   * this {@link SuggestBox}.
   *
   * @param oracle the oracle for this <code>SuggestBox</code>
   */
  public SuggestBox(SuggestOracle oracle) {
    this(oracle, new TextBox());
  }

  /**
   * Constructor for {@link SuggestBox}. The text box will be removed from it's
   * current location and wrapped by the {@link SuggestBox}.
   *
   * @param oracle supplies suggestions based upon the current contents of the
   *          text widget
   * @param box the text widget
   */
  public SuggestBox(SuggestOracle oracle, TextBoxBase box) {
    this.box = box;
    initWidget(box);
    this.picker = new SuggestPickerImpl(oracle.isDisplayStringHTML());
    this.popup = new ItemPickerDropDownImpl(this, picker);
    addPopupChangeListener();
    addKeyboardSupport();
    setOracle(oracle);
    setStyleName(STYLENAME_DEFAULT);
  }

  public final void addChangeListener(ChangeListener listener) {
    if (changeListeners == null) {
      changeListeners = new DelegatingChangeListenerCollection(this, box);
    }
    changeListeners.add(listener);
  }

  public final void addClickListener(ClickListener listener) {
    if (clickListeners == null) {
      clickListeners = new DelegatingClickListenerCollection(this, box);
    }
    clickListeners.add(listener);
  }

  public final void addFocusListener(FocusListener listener) {
    if (focusListeners == null) {
      focusListeners = new DelegatingFocusListenerCollection(this, box);
    }
    focusListeners.add(listener);
  }

  public final void addKeyboardListener(KeyboardListener listener) {
    if (keyboardListeners == null) {
      keyboardListeners = new DelegatingKeyboardListenerCollection(this, box);
    }
    keyboardListeners.add(listener);
  }

  /**
   * Gets the limit for the number of suggestions that should be displayed for
   * this box. It is up to the current {@link SuggestOracle} to enforce this
   * limit.
   *
   * @return the limit for the number of suggestions
   */
  public final int getLimit() {
    return limit;
  }

  /**
   * Gets the suggest box's {@link com.google.gwt.user.client.ui.SuggestOracle}.
   *
   * @return the {@link SuggestOracle}
   */
  public final SuggestOracle getSuggestOracle() {
    return oracle;
  }

  public final int getTabIndex() {
    return box.getTabIndex();
  }

  public final String getText() {
    return box.getText();
  }

  public final void removeChangeListener(ChangeListener listener) {
    if (clickListeners != null) {
      clickListeners.remove(listener);
    }
  }

  public final void removeClickListener(ClickListener listener) {
    if (clickListeners != null) {
      clickListeners.remove(listener);
    }
  }

  public final void removeFocusListener(FocusListener listener) {
    if (focusListeners != null) {
      focusListeners.remove(listener);
    }
  }

  public final void removeKeyboardListener(KeyboardListener listener) {
    if (keyboardListeners != null) {
      keyboardListeners.remove(listener);
    }
  }

  public final void setAccessKey(char key) {
    box.setAccessKey(key);
  }

  public final void setFocus(boolean focused) {
    box.setFocus(focused);
  }

  /**
   * Sets the limit to the number of suggestions the oracle should provide. It
   * is up to the oracle to enforce this limit.
   *
   * @param limit the limit to the number of suggestions provided
   */
  public final void setLimit(int limit) {
    this.limit = limit;
  }

  public final void setTabIndex(int index) {
    box.setTabIndex(index);
  }

  public final void setText(String text) {
    box.setText(text);
  }

  /**
   * Show the given collection of suggestions.
   *
   * @param suggestions suggestions to show
   */
  private void showSuggestions(Collection suggestions) {
    if (suggestions.size() > 0) {
      picker.setItems(suggestions);
      popup.show();
    } else {
      popup.hide();
    }
  }

  private void addKeyboardSupport() {
    box.addKeyboardListener(new KeyboardListenerAdapter() {
      private boolean pendingCancel;

      public void onKeyDown(Widget sender, char keyCode, int modifiers) {
        pendingCancel = picker.delegateKeyDown(keyCode);
      }

      public void onKeyPress(Widget sender, char keyCode, int modifiers) {
        if (pendingCancel) {
          // IE does not allow cancel key on key down, so we have delayed the
          // cancellation of the key until the associated key press.
          box.cancelKey();
          pendingCancel = false;
        } else if (popup.isAttached()) {
          if (separators != null && isSeparator(keyCode)) {
            // onKeyDown/onKeyUps's keyCode for ',' comes back '1/4', so unlike
            // navigation, we use key press events to determine when the user
            // wants to simulate clicking on the popup.
            picker.commitSelection();

            // The separator will be added after the popup is activated, so the
            // popup will have already added a new separator. Therefore, the
            // original separator should not be added as well.
            box.cancelKey();
          }
        }
      }

      public void onKeyUp(Widget sender, char keyCode, int modifiers) {
        // After every user key input, refresh the popup's suggestions.
        refreshSuggestions();
      }

      /**
       * In the presence of separators, returns the active search selection.
       */
      private String getActiveSelection(String text) {
        selectEnd = box.getCursorPos();

        // Find the last instance of a separator.
        selectStart = -1;
        for (int i = 0; i < separators.length; i++) {
          selectStart = Math.max(
              text.lastIndexOf(separators[i], selectEnd - 1), selectStart);
        }
        ++selectStart;

        return text.substring(selectStart, selectEnd).trim();
      }

      private void refreshSuggestions() {
        // Get the raw text.
        String text = box.getText();
        if (text.equals(currentValue)) {
          return;
        } else {
          currentValue = text;
        }

        // Find selection to replace.
        String selection;
        if (separators == null) {
          selection = text;
        } else {
          selection = getActiveSelection(text);
        }
        // If we have no text, let's not show the suggestions.
        if (selection.length() == 0) {
          popup.hide();
        } else {
          showSuggestions(selection);
        }
      }
    });
  }

  /**
   * Adds a standard popup listener to the suggest box's popup.
   */
  private void addPopupChangeListener() {
    picker.addChangeListener(new ChangeListener() {
      public void onChange(Widget sender) {
        if (separators != null) {
          onChangeWithSeparators();
        } else {
          currentValue = picker.getSelectedValue().toString();
          box.setText(currentValue);
        }
        if (changeListeners != null) {
          changeListeners.fireChange(SuggestBox.this);
        }
      }

      private void onChangeWithSeparators() {
        String newValue = (String) picker.getSelectedValue();

        StringBuffer accum = new StringBuffer();
        String text = box.getText();

        // Add all text up to the selection start.
        accum.append(text.substring(0, selectStart));

        // Add one space if not at start.
        if (selectStart > 0) {
          accum.append(separatorPadding);
        }
        // Add the new value.
        accum.append(newValue);

        // Find correct cursor position.
        int savedCursorPos = accum.length();

        // Add all text after the selection end
        String ender = text.substring(selectEnd).trim();
        if (ender.length() == 0 || !isSeparator(ender.charAt(0))) {
          // Add a separator if the first char of the ender is not already a
          // separator.
          accum.append(separators[0]).append(separatorPadding);
          savedCursorPos = accum.length();
        }
        accum.append(ender);

        // Set the text and cursor pos to correct location.
        String replacement = accum.toString();
        currentValue = replacement.trim();
        box.setText(replacement);
        box.setCursorPos(savedCursorPos);
      }
    });
  }

  /**
   * Convenience method for identifying if a character is a separator.
   */
  private boolean isSeparator(char candidate) {
    // An int map would be very handy right here...
    for (int i = 0; i < separators.length; i++) {
      if (candidate == separators[i]) {
        return true;
      }
    }
    return false;
  }

  /**
   * Sets the suggestion oracle used to create suggestions.
   *
   * @param oracle the oracle
   */
  private void setOracle(SuggestOracle oracle) {
    this.oracle = oracle;
  }

  private void showSuggestions(String query) {
    oracle.requestSuggestions(new Request(query, limit), callBack);
  }
}
TOP

Related Classes of com.google.gwt.user.client.ui.SuggestBox

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.