123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- /*
- * Copyright 2000-2016 Vaadin Ltd.
- *
- * 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.vaadin.client.ui.combobox;
-
- import java.util.List;
-
- import com.vaadin.client.Profiler;
- import com.vaadin.client.annotations.OnStateChange;
- import com.vaadin.client.communication.StateChangeEvent;
- import com.vaadin.client.connectors.AbstractListingConnector;
- import com.vaadin.client.connectors.data.HasDataSource;
- import com.vaadin.client.data.DataSource;
- import com.vaadin.client.ui.HasErrorIndicator;
- import com.vaadin.client.ui.HasRequiredIndicator;
- import com.vaadin.client.ui.SimpleManagedLayout;
- import com.vaadin.client.ui.VComboBox;
- import com.vaadin.client.ui.VComboBox.ComboBoxSuggestion;
- import com.vaadin.client.ui.VComboBox.DataReceivedHandler;
- import com.vaadin.shared.EventId;
- import com.vaadin.shared.Registration;
- import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
- import com.vaadin.shared.data.DataCommunicatorConstants;
- import com.vaadin.shared.data.selection.SelectionServerRpc;
- import com.vaadin.shared.ui.Connect;
- import com.vaadin.shared.ui.combobox.ComboBoxConstants;
- import com.vaadin.shared.ui.combobox.ComboBoxServerRpc;
- import com.vaadin.shared.ui.combobox.ComboBoxState;
- import com.vaadin.ui.ComboBox;
-
- import elemental.json.JsonObject;
-
- @Connect(ComboBox.class)
- public class ComboBoxConnector extends AbstractListingConnector
- implements HasRequiredIndicator, HasDataSource, SimpleManagedLayout,
- HasErrorIndicator {
-
- private ComboBoxServerRpc rpc = getRpcProxy(ComboBoxServerRpc.class);
- private SelectionServerRpc selectionRpc = getRpcProxy(
- SelectionServerRpc.class);
-
- private FocusAndBlurServerRpc focusAndBlurRpc = getRpcProxy(
- FocusAndBlurServerRpc.class);
-
- private Registration dataChangeHandlerRegistration;
-
- @Override
- protected void init() {
- super.init();
- getWidget().connector = this;
- }
-
- @Override
- public void onStateChanged(StateChangeEvent stateChangeEvent) {
- super.onStateChanged(stateChangeEvent);
-
- Profiler.enter("ComboBoxConnector.onStateChanged update content");
-
- getWidget().readonly = isReadOnly();
- getWidget().updateReadOnly();
-
- // not a FocusWidget -> needs own tabindex handling
- getWidget().tb.setTabIndex(getState().tabIndex);
-
- getWidget().suggestionPopup.updateStyleNames(getState());
-
- getWidget().nullSelectionAllowed = getState().emptySelectionAllowed;
- // TODO having this true would mean that the empty selection item comes
- // from the data source so none needs to be added - currently
- // unsupported
- getWidget().nullSelectItem = false;
-
- // make sure the input prompt is updated
- getWidget().updatePlaceholder();
-
- getDataReceivedHandler().serverReplyHandled();
-
- Profiler.leave("ComboBoxConnector.onStateChanged update content");
- }
-
- @OnStateChange("emptySelectionCaption")
- private void onEmptySelectionCaptionChange() {
- List<ComboBoxSuggestion> suggestions = getWidget().currentSuggestions;
- if (!suggestions.isEmpty() && isFirstPage()) {
- suggestions.remove(0);
- addEmptySelectionItem();
- }
- }
-
- @OnStateChange({ "selectedItemKey", "selectedItemCaption" })
- private void onSelectionChange() {
- getDataReceivedHandler().updateSelectionFromServer(
- getState().selectedItemKey, getState().selectedItemCaption);
- }
-
- @Override
- public VComboBox getWidget() {
- return (VComboBox) super.getWidget();
- }
-
- private DataReceivedHandler getDataReceivedHandler() {
- return getWidget().getDataReceivedHandler();
- }
-
- @Override
- public ComboBoxState getState() {
- return (ComboBoxState) super.getState();
- }
-
- @Override
- public void layout() {
- VComboBox widget = getWidget();
- if (widget.initDone) {
- widget.updateRootWidth();
- }
- }
-
- @Override
- public void setWidgetEnabled(boolean widgetEnabled) {
- super.setWidgetEnabled(widgetEnabled);
- getWidget().enabled = widgetEnabled;
- getWidget().tb.setEnabled(widgetEnabled);
- }
-
- /*
- * These methods exist to move communications out of VComboBox, and may be
- * refactored/removed in the future
- */
-
- /**
- * Send a message about a newly created item to the server.
- *
- * This method is for internal use only and may be removed in future
- * versions.
- *
- * @since 8.0
- * @param itemValue
- * user entered string value for the new item
- */
- public void sendNewItem(String itemValue) {
- rpc.createNewItem(itemValue);
- getDataReceivedHandler().clearPendingNavigation();
- }
-
- /**
- * Send a message to the server set the current filter.
- *
- * This method is for internal use only and may be removed in future
- * versions.
- *
- * @since 8.0
- * @param filter
- * the current filter string
- */
- public void setFilter(String filter) {
- if (filter != getWidget().lastFilter) {
- getDataReceivedHandler().clearPendingNavigation();
- }
- rpc.setFilter(filter);
- }
-
- /**
- * Send a message to the server to request a page of items with the current
- * filter.
- *
- * This method is for internal use only and may be removed in future
- * versions.
- *
- * @since 8.0
- * @param page
- * the page number to get or -1 to let the server/connector
- * decide based on current selection (possibly loading more data
- * from the server)
- */
- public void requestPage(int page) {
- if (page < 0) {
- if (getState().scrollToSelectedItem) {
- getDataSource().ensureAvailability(0, 10000);
- return;
- } else {
- page = 0;
- }
- }
- int adjustment = getWidget().nullSelectionAllowed
- && "".equals(getWidget().lastFilter) ? 1 : 0;
- int startIndex = Math.max(0,
- page * getWidget().pageLength - adjustment);
- int pageLength = getWidget().pageLength > 0 ? getWidget().pageLength
- : 10000;
- getDataSource().ensureAvailability(startIndex, pageLength);
- }
-
- /**
- * Send a message to the server updating the current selection.
- *
- * This method is for internal use only and may be removed in future
- * versions.
- *
- * @since 8.0
- * @param selectionKey
- * the current selected item key
- */
- public void sendSelection(String selectionKey) {
- // map also the special empty string option key (from data change
- // handler below) to null
- selectionRpc.select("".equals(selectionKey) ? null : selectionKey);
- getDataReceivedHandler().clearPendingNavigation();
- }
-
- /**
- * Notify the server that the combo box received focus.
- *
- * For timing reasons, ConnectorFocusAndBlurHandler is not used at the
- * moment.
- *
- * This method is for internal use only and may be removed in future
- * versions.
- *
- * @since 8.0
- */
- public void sendFocusEvent() {
- boolean registeredListeners = hasEventListener(EventId.FOCUS);
- if (registeredListeners) {
- focusAndBlurRpc.focus();
- getDataReceivedHandler().clearPendingNavigation();
- }
- }
-
- /**
- * Notify the server that the combo box lost focus.
- *
- * For timing reasons, ConnectorFocusAndBlurHandler is not used at the
- * moment.
- *
- * This method is for internal use only and may be removed in future
- * versions.
- *
- * @since 8.0
- */
- public void sendBlurEvent() {
- boolean registeredListeners = hasEventListener(EventId.BLUR);
- if (registeredListeners) {
- focusAndBlurRpc.blur();
- getDataReceivedHandler().clearPendingNavigation();
- }
- }
-
- @Override
- public void setDataSource(DataSource<JsonObject> dataSource) {
- super.setDataSource(dataSource);
- dataChangeHandlerRegistration = dataSource
- .addDataChangeHandler(range -> refreshData());
- }
-
- @Override
- public void onUnregister() {
- super.onUnregister();
- dataChangeHandlerRegistration.remove();
- }
-
- @Override
- public boolean isRequiredIndicatorVisible() {
- return getState().required && !isReadOnly();
- }
-
- private void refreshData() {
- updateCurrentPage();
-
- getWidget().currentSuggestions.clear();
-
- int start = getWidget().currentPage * getWidget().pageLength;
- int end = getWidget().pageLength > 0 ? start + getWidget().pageLength
- : getDataSource().size();
-
- if (getWidget().nullSelectionAllowed
- && "".equals(getWidget().lastFilter)) {
- // add special null selection item...
- if (isFirstPage()) {
- addEmptySelectionItem();
- } else {
- // ...or leave space for it
- start = start - 1;
- }
- // in either case, the last item to show is
- // shifted by one
- end = end - 1;
- }
-
- updateSuggestions(start, end);
- getWidget().totalMatches = getDataSource().size()
- + (getState().emptySelectionAllowed ? 1 : 0);
-
- getDataReceivedHandler().dataReceived();
- }
-
- private void updateSuggestions(int start, int end) {
- for (int i = start; i < end; ++i) {
- JsonObject row = getDataSource().getRow(i);
-
- if (row != null) {
- String key = getRowKey(row);
- String caption = row.getString(DataCommunicatorConstants.NAME);
- String style = row.getString(ComboBoxConstants.STYLE);
- String untranslatedIconUri = row
- .getString(ComboBoxConstants.ICON);
- getWidget().currentSuggestions
- .add(getWidget().new ComboBoxSuggestion(key, caption,
- style, untranslatedIconUri));
- }
- }
- }
-
- private boolean isFirstPage() {
- return getWidget().currentPage == 0;
- }
-
- private void addEmptySelectionItem() {
- if (isFirstPage()) {
- getWidget().currentSuggestions.add(0,
- getWidget().new ComboBoxSuggestion("",
- getState().emptySelectionCaption, null, null));
- }
- }
-
- private void updateCurrentPage() {
- // try to find selected item if requested
- if (getState().scrollToSelectedItem && getState().pageLength > 0
- && getWidget().currentPage < 0
- && getWidget().selectedOptionKey != null) {
- // search for the item with the selected key
- getWidget().currentPage = 0;
- for (int i = 0; i < getDataSource().size(); ++i) {
- JsonObject row = getDataSource().getRow(i);
- if (row != null) {
- String key = getRowKey(row);
- if (getWidget().selectedOptionKey.equals(key)) {
- if (getWidget().nullSelectionAllowed) {
- getWidget().currentPage = (i + 1)
- / getState().pageLength;
- } else {
- getWidget().currentPage = i / getState().pageLength;
- }
- break;
- }
- }
- }
- } else if (getWidget().currentPage < 0) {
- getWidget().currentPage = 0;
- }
- }
- }
|