]> source.dussan.org Git - vaadin-framework.git/commitdiff
Revert ComboBox in compatibility packages to V7 version (#201).
authorDenis Anisimov <denis@vaadin.com>
Mon, 26 Sep 2016 10:01:26 +0000 (13:01 +0300)
committerDenis Anisimov <denis@vaadin.com>
Tue, 27 Sep 2016 07:50:27 +0000 (07:50 +0000)
Change-Id: Icbe1c6a5c0e2b10255424801cada8c11a71decb7

compatibility-client/src/main/java/com/vaadin/v7/client/ui/VFilterSelect.java
compatibility-client/src/main/java/com/vaadin/v7/client/ui/combobox/ComboBoxConnector.java
compatibility-server/src/main/java/com/vaadin/v7/ui/ComboBox.java
compatibility-server/src/test/java/com/vaadin/v7/tests/server/components/ComboBoxValueChangeTest.java
compatibility-server/src/test/java/com/vaadin/v7/ui/ComboBoxTest.java [new file with mode: 0644]
compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/combobox/ComboBoxServerRpc.java [deleted file]
compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/combobox/ComboBoxState.java

index 38b1bf785dc1d737393e2f244ccdab30592255fb..0b7724f3d0afdac33845854101c33e09f920faa3 100644 (file)
@@ -31,6 +31,7 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
 import com.google.gwt.dom.client.Style;
 import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Unit;
@@ -66,9 +67,12 @@ import com.google.gwt.user.client.ui.TextBox;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ApplicationConnection;
 import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComponentConnector;
 import com.vaadin.client.ComputedStyle;
+import com.vaadin.client.ConnectorMap;
 import com.vaadin.client.DeferredWorker;
 import com.vaadin.client.Focusable;
+import com.vaadin.client.UIDL;
 import com.vaadin.client.VConsole;
 import com.vaadin.client.WidgetUtil;
 import com.vaadin.client.ui.Field;
@@ -83,9 +87,9 @@ import com.vaadin.client.ui.aria.HandlesAriaRequired;
 import com.vaadin.client.ui.menubar.MenuBar;
 import com.vaadin.client.ui.menubar.MenuItem;
 import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.EventId;
 import com.vaadin.shared.ui.ComponentStateUtil;
 import com.vaadin.shared.util.SharedUtil;
-import com.vaadin.v7.client.ui.combobox.ComboBoxConnector;
 import com.vaadin.v7.shared.ui.combobox.FilteringMode;
 
 /**
@@ -112,22 +116,17 @@ public class VFilterSelect extends Composite
         /**
          * Constructor
          *
-         * @param key
-         *            item key, empty string for a special null item not in
-         *            container
-         * @param caption
-         *            item caption
-         * @param style
-         *            item style name, can be empty string
-         * @param untranslatedIconUri
-         *            icon URI or null
+         * @param uidl
+         *            The UIDL recieved from the server
          */
-        public FilterSelectSuggestion(String key, String caption, String style,
-                String untranslatedIconUri) {
-            this.key = key;
-            this.caption = caption;
-            this.style = style;
-            this.untranslatedIconUri = untranslatedIconUri;
+        public FilterSelectSuggestion(UIDL uidl) {
+            key = uidl.getStringAttribute("key");
+            caption = uidl.getStringAttribute("caption");
+            style = uidl.getStringAttribute("style");
+
+            if (uidl.hasAttribute("icon")) {
+                untranslatedIconUri = uidl.getStringAttribute("icon");
+            }
         }
 
         /**
@@ -139,7 +138,6 @@ public class VFilterSelect extends Composite
         @Override
         public String getDisplayString() {
             final StringBuffer sb = new StringBuffer();
-            ApplicationConnection client = connector.getConnection();
             final Icon icon = client
                     .getIcon(client.translateVaadinUri(untranslatedIconUri));
             if (icon != null) {
@@ -181,15 +179,13 @@ public class VFilterSelect extends Composite
          * @return
          */
         public String getIconUri() {
-            ApplicationConnection client = connector.getConnection();
             return client.translateVaadinUri(untranslatedIconUri);
         }
 
         /**
          * Gets the style set for this suggestion item. Styles are typically set
-         * by a server-side
-         * {@link com.vaadin.v7.ui.ComboBox.ItemStyleGenerator}. The returned
-         * style is prefixed by <code>v-filterselect-item-</code>.
+         * by a server-side {@link com.vaadin.ui.ComboBox.ItemStyleGenerator}.
+         * The returned style is prefixed by <code>v-filterselect-item-</code>.
          *
          * @since 7.5.6
          * @return the style name to use, or <code>null</code> to not apply any
@@ -247,12 +243,12 @@ public class VFilterSelect extends Composite
             return $entry(function(e) {
                 var deltaX = e.deltaX ? e.deltaX : -0.5*e.wheelDeltaX;
                 var deltaY = e.deltaY ? e.deltaY : -0.5*e.wheelDeltaY;
-
+        
                 // IE8 has only delta y
                 if (isNaN(deltaY)) {
                     deltaY = -0.5*e.wheelDelta;
                 }
-
+        
                 @com.vaadin.v7.client.ui.VFilterSelect.JsniUtil::moveScrollFromEvent(*)(widget, deltaX, deltaY, e, e.deltaMode);
             });
         }-*/;
@@ -306,8 +302,7 @@ public class VFilterSelect extends Composite
 
                     // "Scroll" if change exceeds item height
                     while (Math.abs(deltaSum) >= SCROLL_UNIT_PX) {
-                        if (!filterSelect.dataReceivedHandler
-                                .isWaitingForFilteringResponse()) {
+                        if (!filterSelect.waitingForFilteringResponse) {
                             // Move selection if page flip is not in progress
                             if (deltaSum < 0) {
                                 filterSelect.suggestionPopup.selectPrevItem();
@@ -474,6 +469,15 @@ public class VFilterSelect extends Composite
                             .clearWidth();
 
                     setPopupPositionAndShow(popup);
+                    // Fix for #14173
+                    // IE9 and IE10 have a bug, when resize an a element with
+                    // box-shadow.
+                    // IE9 and IE10 need explicit update to remove extra
+                    // box-shadows
+                    if (BrowserInfo.get().isIE9()
+                            || BrowserInfo.get().isIE10()) {
+                        forceReflow();
+                    }
                 }
             });
         }
@@ -604,7 +608,7 @@ public class VFilterSelect extends Composite
             public void run() {
                 debug("VFS.SP.LPS: run()");
                 if (pagesToScroll != 0) {
-                    if (!dataReceivedHandler.isWaitingForFilteringResponse()) {
+                    if (!waitingForFilteringResponse) {
                         /*
                          * Avoid scrolling while we are waiting for a response
                          * because otherwise the waiting flag will be reset in
@@ -853,6 +857,10 @@ public class VFilterSelect extends Composite
                     menu.setWidth(Window.getClientWidth() + "px");
 
                 }
+                if (BrowserInfo.get().isIE()
+                        && BrowserInfo.get().getBrowserMajorVersion() < 10) {
+                    setTdWidth(menu.getElement(), Window.getClientWidth() - 8);
+                }
             }
 
             setPopupPosition(left, top);
@@ -896,6 +904,44 @@ public class VFilterSelect extends Composite
                 width = WidgetUtil.escapeAttribute(suggestionPopupWidth);
             }
             menu.setWidth(width);
+
+            // IE8 or 9?
+            if (BrowserInfo.get().isIE()
+                    && BrowserInfo.get().getBrowserMajorVersion() < 10) {
+                // using legacy mode?
+                if (suggestionPopupWidth == null) {
+                    // set the TD widths manually as these browsers do not
+                    // respect display: block; width:100% rules
+                    setTdWidth(menu.getElement(), naturalMenuWidth);
+                } else {
+                    int compensation = WidgetUtil
+                            .measureHorizontalPaddingAndBorder(
+                                    menu.getElement(), 4);
+                    setTdWidth(menu.getElement(),
+                            menu.getOffsetWidth() - compensation);
+                }
+
+            }
+        }
+
+        /**
+         * Descends to child elements until finds TD elements and sets their
+         * width in pixels. Can be used to workaround IE8 & 9 TD element
+         * display: block issues
+         *
+         * @param parent
+         * @param width
+         */
+        private void setTdWidth(Node parent, int width) {
+            for (int i = 0; i < parent.getChildCount(); i++) {
+                Node child = parent.getChild(i);
+                if ("td".equals(child.getNodeName().toLowerCase())) {
+                    ((Element) child).getStyle().setWidth(width, Unit.PX);
+                } else {
+                    setTdWidth(child, width);
+                }
+
+            }
         }
 
         /**
@@ -952,10 +998,13 @@ public class VFilterSelect extends Composite
         /**
          * Updates style names in suggestion popup to help theme building.
          *
+         * @param uidl
+         *            UIDL for the whole combo box
          * @param componentState
          *            shared state of the combo box
          */
-        public void updateStyleNames(AbstractComponentState componentState) {
+        public void updateStyleNames(UIDL uidl,
+                AbstractComponentState componentState) {
             debug("VFS.SP: updateStyleNames()");
             setStyleName(
                     VFilterSelect.this.getStylePrimaryName() + "-suggestpopup");
@@ -1073,11 +1122,18 @@ public class VFilterSelect extends Composite
 
                 isFirstIteration = false;
             }
+            if (suggestionPopupWidth != null && BrowserInfo.get().isIE()
+                    && BrowserInfo.get().getBrowserMajorVersion() < 10) {
+                // set TD width to a low value so that they won't mandate the
+                // suggestion pop-up width
+                suggestionPopup.setTdWidth(suggestionPopup.menu.getElement(),
+                        1);
+            }
         }
 
         /**
          * Send the current selection to the server. Triggered when a selection
-         * is made with the ENTER key.
+         * is made or on a blur event.
          */
         public void doSelectedItemAction() {
             debug("VFS.SM: doSelectedItemAction()");
@@ -1092,23 +1148,32 @@ public class VFilterSelect extends Composite
                 }
                 // null is not visible on pages != 0, and not visible when
                 // filtering: handle separately
-                connector.requestFirstPage();
+                client.updateVariable(paintableId, "filter", "", false);
+                client.updateVariable(paintableId, "page", 0, false);
+                client.updateVariable(paintableId, "selected", new String[] {},
+                        immediate);
+                afterUpdateClientVariables();
 
                 suggestionPopup.hide();
                 return;
             }
 
-            dataReceivedHandler.doPostFilterWhenReady();
+            updateSelectionWhenReponseIsReceived = waitingForFilteringResponse;
+            if (!waitingForFilteringResponse) {
+                doPostFilterSelectedItemAction();
+            }
         }
 
         /**
-         * Triggered after a selection has been made.
+         * Triggered after a selection has been made
          */
         public void doPostFilterSelectedItemAction() {
             debug("VFS.SM: doPostFilterSelectedItemAction()");
             final MenuItem item = getSelectedItem();
             final String enteredItemValue = tb.getText();
 
+            updateSelectionWhenReponseIsReceived = false;
+
             // check for exact match in menu
             int p = getItems().size();
             if (p > 0) {
@@ -1136,7 +1201,9 @@ public class VFilterSelect extends Composite
                      * Store last sent new item string to avoid double sends
                      */
                     lastNewItemString = enteredItemValue;
-                    connector.sendNewItem(enteredItemValue);
+                    client.updateVariable(paintableId, "newitem",
+                            enteredItemValue, immediate);
+                    afterUpdateClientVariables();
                 }
             } else if (item != null && !"".equals(lastFilter)
                     && (filteringmode == FilteringMode.CONTAINS
@@ -1330,197 +1397,6 @@ public class VFilterSelect extends Composite
 
     }
 
-    /**
-     * Handler receiving notifications from the connector and updating the
-     * widget state accordingly.
-     *
-     * This class is still subject to change and should not be considered as
-     * public stable API.
-     *
-     * @since 8.0
-     */
-    public class DataReceivedHandler {
-
-        private Runnable navigationCallback = null;
-        /**
-         * Set true when popupopened has been clicked. Cleared on each
-         * UIDL-update. This handles the special case where are not filtering
-         * yet and the selected value has changed on the server-side. See #2119
-         * <p>
-         * For internal use only. May be removed or replaced in the future.
-         */
-        private boolean popupOpenerClicked = false;
-        private boolean performPostFilteringOnDataReceived = false;
-        /** For internal use only. May be removed or replaced in the future. */
-        private boolean waitingForFilteringResponse = false;
-
-        /**
-         * Called by the connector when new data for the last requested filter
-         * is received from the server.
-         */
-        public void dataReceived() {
-            suggestionPopup.showSuggestions(currentSuggestions, currentPage,
-                    totalMatches);
-
-            waitingForFilteringResponse = false;
-
-            if (!popupOpenerClicked) {
-                navigateItemAfterPageChange();
-            }
-
-            if (performPostFilteringOnDataReceived) {
-                performPostFilteringOnDataReceived = false;
-                suggestionPopup.menu.doPostFilterSelectedItemAction();
-            }
-
-            popupOpenerClicked = false;
-        }
-
-        /*
-         * This method navigates to the proper item in the combobox page. This
-         * should be executed after setSuggestions() method which is called from
-         * vFilterSelect.showSuggestions(). ShowSuggestions() method builds the
-         * page content. As far as setSuggestions() method is called as
-         * deferred, navigateItemAfterPageChange method should be also be called
-         * as deferred. #11333
-         */
-        private void navigateItemAfterPageChange() {
-            if (navigationCallback != null) {
-                // pageChangeCallback is not reset here but after any server
-                // request in case you are in between two requests both changing
-                // the page back and forth
-
-                // we're paging w/ arrows
-                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
-                    @Override
-                    public void execute() {
-                        if (navigationCallback != null) {
-                            navigationCallback.run();
-                        }
-                    }
-                });
-            }
-        }
-
-        /**
-         * Called by the connector when any request has been sent to the server,
-         * before waiting for a reply.
-         */
-        public void anyRequestSentToServer() {
-            navigationCallback = null;
-        }
-
-        /**
-         * Set a callback that is invoked when a page change occurs if there
-         * have not been intervening requests to the server. The callback is
-         * reset when any additional request is made to the server.
-         *
-         * @param callback
-         */
-        public void setNavigationCallback(Runnable callback) {
-            navigationCallback = callback;
-        }
-
-        /**
-         * Record that the popup opener has been clicked and the popup should be
-         * opened on the next request.
-         *
-         * This handles the special case where are not filtering yet and the
-         * selected value has changed on the server-side. See #2119. The flag is
-         * cleared on each UIDL reply.
-         */
-        public void popupOpenerClicked() {
-            popupOpenerClicked = true;
-        }
-
-        /**
-         * Cancel a pending request to perform post-filtering actions.
-         */
-        private void cancelPendingPostFiltering() {
-            performPostFilteringOnDataReceived = false;
-        }
-
-        /**
-         * Called by the connector when it has finished handling any reply from
-         * the server, regardless of what was updated.
-         */
-        public void serverReplyHandled() {
-            popupOpenerClicked = false;
-        }
-
-        /**
-         * For internal use only - this method will be removed in the future.
-         *
-         * @return true if the combo box is waiting for a reply from the server
-         *         with a new page of data, false otherwise
-         */
-        public boolean isWaitingForFilteringResponse() {
-            return waitingForFilteringResponse;
-        }
-
-        /**
-         * Set a flag that filtering of options is pending a response from the
-         * server.
-         */
-        private void startWaitingForFilteringResponse() {
-            waitingForFilteringResponse = true;
-        }
-
-        /**
-         * Perform the post-filter action either now (if not waiting for a
-         * server response) or when a response is received.
-         */
-        private void doPostFilterWhenReady() {
-            if (isWaitingForFilteringResponse()) {
-                performPostFilteringOnDataReceived = true;
-            } else {
-                performPostFilteringOnDataReceived = false;
-                suggestionPopup.menu.doPostFilterSelectedItemAction();
-            }
-        }
-
-        /**
-         * Perform selection (if appropriate) based on a reply from the server.
-         * When this method is called, the suggestions have been reset if new
-         * ones (different from the previous list) were received from the
-         * server.
-         *
-         * @param selectedKey
-         *            new selected key or null if none given by the server
-         * @param selectedCaption
-         *            new selected item caption if sent by the server or null -
-         *            this is used when the selected item is not on the current
-         *            page
-         * @param oldSuggestionTextMatchTheOldSelection
-         *            true if the old filtering text box contents matched
-         *            exactly the old selection
-         */
-        public void updateSelectionFromServer(String selectedKey,
-                String selectedCaption,
-                boolean oldSuggestionTextMatchTheOldSelection) {
-            // when filtering with empty filter, server sets the selected key
-            // to "", which we don't select here. Otherwise we won't be able to
-            // reset back to the item that was selected before filtering
-            // started.
-            if (selectedKey != null && !selectedKey.equals("")) {
-                performSelection(selectedKey,
-                        oldSuggestionTextMatchTheOldSelection,
-                        !isWaitingForFilteringResponse() || popupOpenerClicked);
-                setSelectedCaption(null);
-            } else if (!isWaitingForFilteringResponse()
-                    && selectedCaption != null) {
-                // scrolling to correct page is disabled, caption is passed as a
-                // special parameter
-                setSelectedCaption(selectedCaption);
-            } else {
-                if (!isWaitingForFilteringResponse() || popupOpenerClicked) {
-                    resetSelection(popupOpenerClicked);
-                }
-            }
-        }
-
-    }
-
     @Deprecated
     public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF;
     @Deprecated
@@ -1583,7 +1459,10 @@ public class VFilterSelect extends Composite
     private IconWidget selectedItemIcon;
 
     /** For internal use only. May be removed or replaced in the future. */
-    public ComboBoxConnector connector;
+    public ApplicationConnection client;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public String paintableId;
 
     /** For internal use only. May be removed or replaced in the future. */
     public int currentPage;
@@ -1594,17 +1473,34 @@ public class VFilterSelect extends Composite
      * <p>
      * For internal use only. May be removed or replaced in the future.
      */
-    public final List<FilterSelectSuggestion> currentSuggestions = new ArrayList<FilterSelectSuggestion>();
+    public final List<FilterSelectSuggestion> currentSuggestions = new ArrayList<>();
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean immediate;
 
     /** For internal use only. May be removed or replaced in the future. */
     public String selectedOptionKey;
 
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean waitingForFilteringResponse = false;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean updateSelectionWhenReponseIsReceived = false;
+
     /** For internal use only. May be removed or replaced in the future. */
     public boolean initDone = false;
 
     /** For internal use only. May be removed or replaced in the future. */
     public String lastFilter = "";
 
+    /** For internal use only. May be removed or replaced in the future. */
+    public enum Select {
+        NONE, FIRST, LAST
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public Select selectPopupItemWhenResponseIsReceived = Select.NONE;
+
     /**
      * The current suggestion selected from the dropdown. This is one of the
      * values in currentSuggestions except when filtering, in this case
@@ -1644,6 +1540,15 @@ public class VFilterSelect extends Composite
     /** For internal use only. May be removed or replaced in the future. */
     public boolean prompting = false;
 
+    /**
+     * Set true when popupopened has been clicked. Cleared on each UIDL-update.
+     * This handles the special case where are not filtering yet and the
+     * selected value has changed on the server-side. See #2119
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public boolean popupOpenerClicked;
+
     /** For internal use only. May be removed or replaced in the future. */
     public int suggestionPopupMinWidth = 0;
 
@@ -1667,8 +1572,6 @@ public class VFilterSelect extends Composite
      */
     private boolean textInputEnabled = true;
 
-    private final DataReceivedHandler dataReceivedHandler = new DataReceivedHandler();
-
     /**
      * Default constructor.
      */
@@ -1808,7 +1711,22 @@ public class VFilterSelect extends Composite
      *            The filter to apply to the components
      */
     public void filterOptions(int page, String filter) {
-        debug("VFS: filterOptions(" + page + ", " + filter + ")");
+        filterOptions(page, filter, true);
+    }
+
+    /**
+     * Filters the options at certain page using the given filter
+     *
+     * @param page
+     *            The page to filter
+     * @param filter
+     *            The filter to apply to the options
+     * @param immediate
+     *            Whether to send the options request immediately
+     */
+    private void filterOptions(int page, String filter, boolean immediate) {
+        debug("VFS: filterOptions(" + page + ", " + filter + ", " + immediate
+                + ")");
 
         if (filter.equals(lastFilter) && currentPage == page) {
             if (!suggestionPopup.isAttached()) {
@@ -1829,8 +1747,10 @@ public class VFilterSelect extends Composite
             }
         }
 
-        dataReceivedHandler.startWaitingForFilteringResponse();
-        connector.requestPage(filter, page);
+        waitingForFilteringResponse = true;
+        client.updateVariable(paintableId, "filter", filter, false);
+        client.updateVariable(paintableId, "page", page, immediate);
+        afterUpdateClientVariables();
 
         lastFilter = filter;
         currentPage = page;
@@ -1928,7 +1848,7 @@ public class VFilterSelect extends Composite
             debug("VFS: onSuggestionSelected(" + suggestion.caption + ": "
                     + suggestion.key + ")");
         }
-        dataReceivedHandler.cancelPendingPostFiltering();
+        updateSelectionWhenReponseIsReceived = false;
 
         currentSuggestion = suggestion;
         String newKey;
@@ -1951,7 +1871,9 @@ public class VFilterSelect extends Composite
         if (!(newKey.equals(selectedOptionKey)
                 || ("".equals(newKey) && selectedOptionKey == null))) {
             selectedOptionKey = newKey;
-            connector.sendSelection(selectedOptionKey);
+            client.updateVariable(paintableId, "selected",
+                    new String[] { selectedOptionKey }, immediate);
+            afterUpdateClientVariables();
 
             // currentPage = -1; // forget the page
         }
@@ -1962,7 +1884,9 @@ public class VFilterSelect extends Composite
             // hard to interpret that new clause added here :-(
             selectedOptionKey = newKey;
             explicitSelectedCaption = null;
-            connector.sendSelection(selectedOptionKey);
+            client.updateVariable(paintableId, "selected",
+                    new String[] { selectedOptionKey }, immediate);
+            afterUpdateClientVariables();
         }
 
         suggestionPopup.hide();
@@ -1987,8 +1911,7 @@ public class VFilterSelect extends Composite
             if (selectedItemIcon != null) {
                 panel.remove(selectedItemIcon);
             }
-            selectedItemIcon = new IconWidget(
-                    connector.getConnection().getIcon(iconUri));
+            selectedItemIcon = new IconWidget(client.getIcon(iconUri));
             // Older IE versions don't scale icon correctly if DOM
             // contains height and width attributes.
             selectedItemIcon.getElement().removeAttribute("height");
@@ -2005,7 +1928,7 @@ public class VFilterSelect extends Composite
     }
 
     private void afterSelectedItemIconChange() {
-        if (BrowserInfo.get().isWebkit()) {
+        if (BrowserInfo.get().isWebkit() || BrowserInfo.get().isIE8()) {
             // Some browsers need a nudge to reposition the text field
             forceReflow();
         }
@@ -2015,88 +1938,6 @@ public class VFilterSelect extends Composite
         }
     }
 
-    /**
-     * Perform selection based on a message from the server.
-     *
-     * This method is called when the server gave a non-empty selected item key,
-     * whereas null selection is handled by {@link #resetSelection()} and the
-     * special case where the selected item is not on the current page is
-     * handled separately by the caller.
-     *
-     * @param selectedKey
-     *            non-empty selected item key
-     * @param oldSuggestionTextMatchTheOldSelection
-     *            true if the suggestion box text matched the previous selection
-     *            before the message from the server updating the selection
-     * @param updatePromptAndSelectionIfMatchFound
-     */
-    private void performSelection(String selectedKey,
-            boolean oldSuggestionTextMatchTheOldSelection,
-            boolean updatePromptAndSelectionIfMatchFound) {
-        // some item selected
-        for (FilterSelectSuggestion suggestion : currentSuggestions) {
-            String suggestionKey = suggestion.getOptionKey();
-            if (!suggestionKey.equals(selectedKey)) {
-                continue;
-            }
-            // at this point, suggestion key matches the new selection key
-            if (updatePromptAndSelectionIfMatchFound) {
-                if (!suggestionKey.equals(selectedOptionKey)
-                        || suggestion.getReplacementString()
-                                .equals(tb.getText())
-                        || oldSuggestionTextMatchTheOldSelection) {
-                    // Update text field if we've got a new
-                    // selection
-                    // Also update if we've got the same text to
-                    // retain old text selection behavior
-                    // OR if selected item caption is changed.
-                    setPromptingOff(suggestion.getReplacementString());
-                    selectedOptionKey = suggestionKey;
-                }
-            }
-            currentSuggestion = suggestion;
-            setSelectedItemIcon(suggestion.getIconUri());
-            // only a single item can be selected
-            break;
-        }
-    }
-
-    /**
-     * Reset the selection of the combo box to an empty string if focused, the
-     * input prompt if not.
-     *
-     * This method also clears the current suggestion and the selected option
-     * key.
-     */
-    private void resetSelection(boolean useSelectedCaption) {
-        // select nulled
-        if (!focused) {
-            // TODO it is unclear whether this is really needed anymore -
-            // client.updateComponent used to overwrite all styles so we had to
-            // set them again
-            setPromptingOff("");
-            if (enabled && !readonly) {
-                setPromptingOn();
-            }
-        } else {
-            // we have focus in field, prompting can't be set on,
-            // instead just clear the input if the value has changed from
-            // something else to null
-            if (selectedOptionKey != null
-                    || (allowNewItem && !tb.getValue().isEmpty())) {
-                if (useSelectedCaption && getSelectedCaption() != null) {
-                    tb.setValue(getSelectedCaption());
-                    tb.selectAll();
-                } else {
-                    tb.setValue("");
-                }
-            }
-        }
-        currentSuggestion = null; // #13217
-        setSelectedItemIcon(null);
-        selectedOptionKey = null;
-    }
-
     private void forceReflow() {
         WidgetUtil.setStyleTemporarily(tb.getElement(), "zoom", "1");
     }
@@ -2116,7 +1957,7 @@ public class VFilterSelect extends Composite
                 Unit.PX);
     }
 
-    private static Set<Integer> navigationKeyCodes = new HashSet<Integer>();
+    private static Set<Integer> navigationKeyCodes = new HashSet<>();
     static {
         navigationKeyCodes.add(KeyCodes.KEY_DOWN);
         navigationKeyCodes.add(KeyCodes.KEY_UP);
@@ -2141,7 +1982,7 @@ public class VFilterSelect extends Composite
             if (enableDebug) {
                 debug("VFS: key down: " + keyCode);
             }
-            if (dataReceivedHandler.isWaitingForFilteringResponse()
+            if (waitingForFilteringResponse
                     && navigationKeyCodes.contains(keyCode)) {
                 /*
                  * Keyboard navigation events should not be handled while we are
@@ -2290,12 +2131,7 @@ public class VFilterSelect extends Composite
     private void selectPrevPage() {
         if (currentPage > 0) {
             filterOptions(currentPage - 1, lastFilter);
-            dataReceivedHandler.setNavigationCallback(new Runnable() {
-                @Override
-                public void run() {
-                    suggestionPopup.selectLastItem();
-                }
-            });
+            selectPopupItemWhenResponseIsReceived = Select.LAST;
         }
     }
 
@@ -2305,12 +2141,7 @@ public class VFilterSelect extends Composite
     private void selectNextPage() {
         if (hasNextPage()) {
             filterOptions(currentPage + 1, lastFilter);
-            dataReceivedHandler.setNavigationCallback(new Runnable() {
-                @Override
-                public void run() {
-                    suggestionPopup.selectFirstItem();
-                }
-            });
+            selectPopupItemWhenResponseIsReceived = Select.FIRST;
         }
     }
 
@@ -2394,8 +2225,13 @@ public class VFilterSelect extends Composite
             // ask suggestionPopup if it was just closed, we are using GWT
             // Popup's auto close feature
             if (!suggestionPopup.isJustClosed()) {
-                filterOptions(-1, "");
-                dataReceivedHandler.popupOpenerClicked();
+                // If a focus event is not going to be sent, send the options
+                // request immediately; otherwise queue in the same burst as the
+                // focus event. Fixes #8321.
+                boolean immediate = focused
+                        || !client.hasEventListeners(this, EventId.FOCUS);
+                filterOptions(-1, "", immediate);
+                popupOpenerClicked = true;
                 lastFilter = "";
             }
             DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
@@ -2488,9 +2324,14 @@ public class VFilterSelect extends Composite
         }
         addStyleDependentName("focus");
 
-        connector.sendFocusEvent();
+        if (client.hasEventListeners(this, EventId.FOCUS)) {
+            client.updateVariable(paintableId, EventId.FOCUS, "", true);
+            afterUpdateClientVariables();
+        }
 
-        connector.getConnection().getVTooltip()
+        ComponentConnector connector = ConnectorMap.get(client)
+                .getConnector(this);
+        client.getVTooltip()
                 .showAssistive(connector.getTooltipInfo(getElement()));
     }
 
@@ -2552,7 +2393,10 @@ public class VFilterSelect extends Composite
         }
         removeStyleDependentName("focus");
 
-        connector.sendBlurEvent();
+        if (client.hasEventListeners(this, EventId.BLUR)) {
+            client.updateVariable(paintableId, EventId.BLUR, "", true);
+            afterUpdateClientVariables();
+        }
     }
 
     /*
@@ -2578,7 +2422,10 @@ public class VFilterSelect extends Composite
      * For internal use only. May be removed or replaced in the future.
      */
     public void updateRootWidth() {
-        if (connector.isUndefinedWidth()) {
+        ComponentConnector paintable = ConnectorMap.get(client)
+                .getConnector(this);
+
+        if (paintable.isUndefinedWidth()) {
 
             /*
              * When the select has a undefined with we need to check that we are
@@ -2734,9 +2581,19 @@ public class VFilterSelect extends Composite
         AriaHelper.bindCaption(tb, captionElement);
     }
 
+    /*
+     * Anything that should be set after the client updates the server.
+     */
+    private void afterUpdateClientVariables() {
+        // We need this here to be consistent with the all the calls.
+        // Then set your specific selection type only after
+        // client.updateVariable() method call.
+        selectPopupItemWhenResponseIsReceived = Select.NONE;
+    }
+
     @Override
     public boolean isWorkPending() {
-        return dataReceivedHandler.isWaitingForFilteringResponse()
+        return waitingForFilteringResponse
                 || suggestionPopup.lazyPageScroller.isRunning();
     }
 
@@ -2765,14 +2622,4 @@ public class VFilterSelect extends Composite
         return explicitSelectedCaption;
     }
 
-    /**
-     * Returns a handler receiving notifications from the connector about
-     * communications.
-     *
-     * @return the dataReceivedHandler
-     */
-    public DataReceivedHandler getDataReceivedHandler() {
-        return dataReceivedHandler;
-    }
-
 }
index 6f851ec792b0db64357ad4ca8c3b8776e8985cab..212a1aa8d1af9d3e8260ed2024a70067a1557fb7 100644 (file)
@@ -19,65 +19,28 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.vaadin.client.ApplicationConnection;
 import com.vaadin.client.Paintable;
-import com.vaadin.client.Profiler;
 import com.vaadin.client.UIDL;
-import com.vaadin.client.communication.RpcProxy;
-import com.vaadin.client.communication.StateChangeEvent;
 import com.vaadin.client.ui.SimpleManagedLayout;
-import com.vaadin.shared.EventId;
-import com.vaadin.shared.communication.FieldRpc.FocusAndBlurServerRpc;
 import com.vaadin.shared.ui.Connect;
 import com.vaadin.v7.client.ui.AbstractFieldConnector;
 import com.vaadin.v7.client.ui.VFilterSelect;
-import com.vaadin.v7.client.ui.VFilterSelect.DataReceivedHandler;
 import com.vaadin.v7.client.ui.VFilterSelect.FilterSelectSuggestion;
-import com.vaadin.v7.shared.ui.combobox.ComboBoxServerRpc;
+import com.vaadin.v7.shared.ui.combobox.ComboBoxConstants;
 import com.vaadin.v7.shared.ui.combobox.ComboBoxState;
+import com.vaadin.v7.shared.ui.combobox.FilteringMode;
 import com.vaadin.v7.ui.ComboBox;
 
 @Connect(ComboBox.class)
 public class ComboBoxConnector extends AbstractFieldConnector
         implements Paintable, SimpleManagedLayout {
 
-    protected ComboBoxServerRpc rpc = RpcProxy.create(ComboBoxServerRpc.class,
-            this);
-
-    protected FocusAndBlurServerRpc focusAndBlurRpc = RpcProxy
-            .create(FocusAndBlurServerRpc.class, this);
-
-    @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();
-
-        getWidget().setTextInputEnabled(getState().textInputAllowed);
-
-        if (getState().inputPrompt != null) {
-            getWidget().inputPrompt = getState().inputPrompt;
-        } else {
-            getWidget().inputPrompt = "";
-        }
-
-        getWidget().pageLength = getState().pageLength;
-
-        getWidget().filteringmode = getState().filteringMode;
-
-        getWidget().suggestionPopupWidth = getState().suggestionPopupWidth;
-
-        Profiler.leave("ComboBoxConnector.onStateChanged update content");
-    }
+    // oldSuggestionTextMatchTheOldSelection is used to detect when it's safe to
+    // update textbox text by a changed item caption.
+    private boolean oldSuggestionTextMatchTheOldSelection;
 
     /*
      * (non-Javadoc)
@@ -87,13 +50,35 @@ public class ComboBoxConnector extends AbstractFieldConnector
      */
     @Override
     public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+        // Save details
+        getWidget().client = client;
+        getWidget().paintableId = uidl.getId();
+
+        getWidget().readonly = isReadOnly();
+        getWidget().updateReadOnly();
+
         if (!isRealUpdate(uidl)) {
             return;
         }
 
+        // Inverse logic here to make the default case (text input enabled)
+        // work without additional UIDL messages
+        boolean noTextInput = uidl
+                .hasAttribute(ComboBoxConstants.ATTR_NO_TEXT_INPUT)
+                && uidl.getBooleanAttribute(
+                        ComboBoxConstants.ATTR_NO_TEXT_INPUT);
+        getWidget().setTextInputEnabled(!noTextInput);
+
         // not a FocusWidget -> needs own tabindex handling
         getWidget().tb.setTabIndex(getState().tabIndex);
 
+        if (uidl.hasAttribute("filteringmode")) {
+            getWidget().filteringmode = FilteringMode
+                    .valueOf(uidl.getStringAttribute("filteringmode"));
+        }
+
+        getWidget().immediate = getState().immediate;
+
         getWidget().nullSelectionAllowed = uidl.hasAttribute("nullselect");
 
         getWidget().nullSelectItem = uidl.hasAttribute("nullselectitem")
@@ -101,7 +86,33 @@ public class ComboBoxConnector extends AbstractFieldConnector
 
         getWidget().currentPage = uidl.getIntVariable("page");
 
-        getWidget().suggestionPopup.updateStyleNames(getState());
+        if (uidl.hasAttribute("pagelength")) {
+            getWidget().pageLength = uidl.getIntAttribute("pagelength");
+        }
+
+        if (uidl.hasAttribute(ComboBoxConstants.ATTR_INPUTPROMPT)) {
+            // input prompt changed from server
+            getWidget().inputPrompt = uidl
+                    .getStringAttribute(ComboBoxConstants.ATTR_INPUTPROMPT);
+        } else {
+            getWidget().inputPrompt = "";
+        }
+
+        if (uidl.hasAttribute("suggestionPopupWidth")) {
+            getWidget().suggestionPopupWidth = uidl
+                    .getStringAttribute("suggestionPopupWidth");
+        } else {
+            getWidget().suggestionPopupWidth = null;
+        }
+
+        if (uidl.hasAttribute("suggestionPopupWidth")) {
+            getWidget().suggestionPopupWidth = uidl
+                    .getStringAttribute("suggestionPopupWidth");
+        } else {
+            getWidget().suggestionPopupWidth = null;
+        }
+
+        getWidget().suggestionPopup.updateStyleNames(uidl, getState());
 
         getWidget().allowNewItem = uidl.hasAttribute("allownewitem");
         getWidget().lastNewItemString = null;
@@ -113,21 +124,12 @@ public class ComboBoxConnector extends AbstractFieldConnector
             getWidget().totalMatches = 0;
         }
 
-        List<FilterSelectSuggestion> newSuggestions = new ArrayList<FilterSelectSuggestion>();
+        List<FilterSelectSuggestion> newSuggestions = new ArrayList<>();
 
         for (final Iterator<?> i = options.getChildIterator(); i.hasNext();) {
             final UIDL optionUidl = (UIDL) i.next();
-            String key = optionUidl.getStringAttribute("key");
-            String caption = optionUidl.getStringAttribute("caption");
-            String style = optionUidl.getStringAttribute("style");
-
-            String untranslatedIconUri = null;
-            if (optionUidl.hasAttribute("icon")) {
-                untranslatedIconUri = optionUidl.getStringAttribute("icon");
-            }
-
             final FilterSelectSuggestion suggestion = getWidget().new FilterSelectSuggestion(
-                    key, caption, style, untranslatedIconUri);
+                    optionUidl);
             newSuggestions.add(suggestion);
         }
 
@@ -139,15 +141,13 @@ public class ComboBoxConnector extends AbstractFieldConnector
         // popup. Popup needs to be repopulated with suggestions from UIDL.
         boolean popupOpenAndCleared = false;
 
-        // oldSuggestionTextMatchTheOldSelection is used to detect when it's
-        // safe to update textbox text by a changed item caption.
-        boolean oldSuggestionTextMatchesTheOldSelection = false;
+        oldSuggestionTextMatchTheOldSelection = false;
 
         if (suggestionsChanged) {
-            oldSuggestionTextMatchesTheOldSelection = isWidgetsCurrentSelectionTextInTextBox();
+            oldSuggestionTextMatchTheOldSelection = isWidgetsCurrentSelectionTextInTextBox();
             getWidget().currentSuggestions.clear();
 
-            if (!getDataReceivedHandler().isWaitingForFilteringResponse()) {
+            if (!getWidget().waitingForFilteringResponse) {
                 /*
                  * Clear the current suggestions as the server response always
                  * includes the new ones. Exception is when filtering, then we
@@ -180,34 +180,62 @@ public class ComboBoxConnector extends AbstractFieldConnector
         //
         ) {
 
-            // single selected key (can be empty string) or empty array for null
-            // selection
             String[] selectedKeys = uidl.getStringArrayVariable("selected");
-            String selectedKey = null;
-            if (selectedKeys.length == 1) {
-                selectedKey = selectedKeys[0];
-            }
-            // selected item caption in case it is not on the current page
-            String selectedCaption = null;
-            if (uidl.hasAttribute("selectedCaption")) {
-                selectedCaption = uidl.getStringAttribute("selectedCaption");
-            }
 
-            getDataReceivedHandler().updateSelectionFromServer(selectedKey,
-                    selectedCaption, oldSuggestionTextMatchesTheOldSelection);
+            // when filtering with empty filter, server sets the selected key
+            // to "", which we don't select here. Otherwise we won't be able to
+            // reset back to the item that was selected before filtering
+            // started.
+            if (selectedKeys.length > 0 && !selectedKeys[0].equals("")) {
+                performSelection(selectedKeys[0]);
+                // if selected key is available, assume caption is know based on
+                // it as well and clear selected caption
+                getWidget().setSelectedCaption(null);
+
+            } else if (!getWidget().waitingForFilteringResponse
+                    && uidl.hasAttribute("selectedCaption")) {
+                // scrolling to correct page is disabled, caption is passed as a
+                // special parameter
+                getWidget().setSelectedCaption(
+                        uidl.getStringAttribute("selectedCaption"));
+            } else {
+                resetSelection();
+            }
         }
 
-        // TODO even this condition should probably be moved to the handler
-        if ((getDataReceivedHandler().isWaitingForFilteringResponse()
-                && getWidget().lastFilter.toLowerCase()
-                        .equals(uidl.getStringVariable("filter")))
+        if ((getWidget().waitingForFilteringResponse && getWidget().lastFilter
+                .toLowerCase().equals(uidl.getStringVariable("filter")))
                 || popupOpenAndCleared) {
-            getDataReceivedHandler().dataReceived();
+
+            getWidget().suggestionPopup.showSuggestions(
+                    getWidget().currentSuggestions, getWidget().currentPage,
+                    getWidget().totalMatches);
+
+            getWidget().waitingForFilteringResponse = false;
+
+            if (!getWidget().popupOpenerClicked
+                    && getWidget().selectPopupItemWhenResponseIsReceived != VFilterSelect.Select.NONE) {
+
+                // we're paging w/ arrows
+                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+                    @Override
+                    public void execute() {
+                        navigateItemAfterPageChange();
+                    }
+                });
+            }
+
+            if (getWidget().updateSelectionWhenReponseIsReceived) {
+                getWidget().suggestionPopup.menu
+                        .doPostFilterSelectedItemAction();
+            }
         }
 
         // Calculate minimum textarea width
         getWidget().updateSuggestionPopupMinWidth();
 
+        getWidget().popupOpenerClicked = false;
+
         /*
          * if this is our first time we need to recalculate the root width.
          */
@@ -223,9 +251,58 @@ public class ComboBoxConnector extends AbstractFieldConnector
         }
 
         getWidget().initDone = true;
+    }
+
+    /*
+     * This method navigates to the proper item in the combobox page. This
+     * should be executed after setSuggestions() method which is called from
+     * vFilterSelect.showSuggestions(). ShowSuggestions() method builds the page
+     * content. As far as setSuggestions() method is called as deferred,
+     * navigateItemAfterPageChange method should be also be called as deferred.
+     * #11333
+     */
+    private void navigateItemAfterPageChange() {
+        if (getWidget().selectPopupItemWhenResponseIsReceived == VFilterSelect.Select.LAST) {
+            getWidget().suggestionPopup.selectLastItem();
+        } else {
+            getWidget().suggestionPopup.selectFirstItem();
+        }
+
+        // If you're in between 2 requests both changing the page back and
+        // forth, you don't want this here, instead you need it before any
+        // other request.
+        // getWidget().selectPopupItemWhenResponseIsReceived =
+        // VFilterSelect.Select.NONE; // reset
+    }
 
-        // TODO this should perhaps be moved to be a part of dataReceived()
-        getDataReceivedHandler().serverReplyHandled();
+    private void performSelection(String selectedKey) {
+        // some item selected
+        for (FilterSelectSuggestion suggestion : getWidget().currentSuggestions) {
+            String suggestionKey = suggestion.getOptionKey();
+            if (!suggestionKey.equals(selectedKey)) {
+                continue;
+            }
+            if (!getWidget().waitingForFilteringResponse
+                    || getWidget().popupOpenerClicked) {
+                if (!suggestionKey.equals(getWidget().selectedOptionKey)
+                        || suggestion.getReplacementString()
+                                .equals(getWidget().tb.getText())
+                        || oldSuggestionTextMatchTheOldSelection) {
+                    // Update text field if we've got a new
+                    // selection
+                    // Also update if we've got the same text to
+                    // retain old text selection behavior
+                    // OR if selected item caption is changed.
+                    getWidget()
+                            .setPromptingOff(suggestion.getReplacementString());
+                    getWidget().selectedOptionKey = suggestionKey;
+                }
+            }
+            getWidget().currentSuggestion = suggestion;
+            getWidget().setSelectedItemIcon(suggestion.getIconUri());
+            // only a single item can be selected
+            break;
+        }
     }
 
     private boolean isWidgetsCurrentSelectionTextInTextBox() {
@@ -234,15 +311,50 @@ public class ComboBoxConnector extends AbstractFieldConnector
                         .equals(getWidget().tb.getText());
     }
 
+    private void resetSelection() {
+        if (!getWidget().waitingForFilteringResponse
+                || getWidget().popupOpenerClicked) {
+            // select nulled
+            if (!getWidget().focused) {
+                /*
+                 * client.updateComponent overwrites all styles so we must
+                 * ALWAYS set the prompting style at this point, even though we
+                 * think it has been set already...
+                 */
+                getWidget().setPromptingOff("");
+                if (getWidget().enabled && !getWidget().readonly) {
+                    getWidget().setPromptingOn();
+                }
+            } else {
+                // we have focus in field, prompting can't be set on, instead
+                // just clear the input if the value has changed from something
+                // else to null
+                if (getWidget().selectedOptionKey != null
+                        || (getWidget().allowNewItem
+                                && !getWidget().tb.getValue().isEmpty())) {
+
+                    boolean openedPopupWithNonScrollingMode = (getWidget().popupOpenerClicked
+                            && getWidget().getSelectedCaption() != null);
+                    if (!openedPopupWithNonScrollingMode) {
+                        getWidget().tb.setValue("");
+                    } else {
+                        getWidget().tb
+                                .setValue(getWidget().getSelectedCaption());
+                        getWidget().tb.selectAll();
+                    }
+                }
+            }
+            getWidget().currentSuggestion = null; // #13217
+            getWidget().setSelectedItemIcon(null);
+            getWidget().selectedOptionKey = null;
+        }
+    }
+
     @Override
     public VFilterSelect getWidget() {
         return (VFilterSelect) super.getWidget();
     }
 
-    private DataReceivedHandler getDataReceivedHandler() {
-        return getWidget().getDataReceivedHandler();
-    }
-
     @Override
     public ComboBoxState getState() {
         return (ComboBoxState) super.getState();
@@ -263,116 +375,4 @@ public class ComboBoxConnector extends AbstractFieldConnector
         getWidget().tb.setEnabled(widgetEnabled);
     }
 
-    /*
-     * These methods exist to move communications out of VFilterSelect, 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);
-        afterSendRequestToServer();
-    }
-
-    /**
-     * Send a message to the server to request the first page of items without
-     * filtering or selection.
-     *
-     * This method is for internal use only and may be removed in future
-     * versions.
-     *
-     * @since 8.0
-     */
-    public void requestFirstPage() {
-        sendSelection(null);
-        requestPage("", 0);
-    }
-
-    /**
-     * Send a message to the server to request a page of items with a given
-     * filter.
-     *
-     * This method is for internal use only and may be removed in future
-     * versions.
-     *
-     * @since 8.0
-     * @param filter
-     *            the current filter string
-     * @param page
-     *            the page number to get
-     */
-    public void requestPage(String filter, int page) {
-        rpc.requestPage(filter, page);
-        afterSendRequestToServer();
-    }
-
-    /**
-     * 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 selection
-     *            the current selection
-     */
-    public void sendSelection(String selection) {
-        rpc.setSelectedItem(selection);
-        afterSendRequestToServer();
-    }
-
-    /**
-     * 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();
-            afterSendRequestToServer();
-        }
-    }
-
-    /**
-     * 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();
-            afterSendRequestToServer();
-        }
-    }
-
-    /*
-     * Called after any request to server.
-     */
-    private void afterSendRequestToServer() {
-        getDataReceivedHandler().anyRequestSentToServer();
-    }
-
 }
index a0af67359e05daa37ff6af9739125b938326beb6..37d427b503791a42498a91df32884dc1cd17a239 100644 (file)
@@ -27,16 +27,14 @@ import java.util.Map;
 import com.vaadin.event.FieldEvents;
 import com.vaadin.event.FieldEvents.BlurEvent;
 import com.vaadin.event.FieldEvents.BlurListener;
-import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcImpl;
 import com.vaadin.event.FieldEvents.FocusEvent;
 import com.vaadin.event.FieldEvents.FocusListener;
 import com.vaadin.server.PaintException;
 import com.vaadin.server.PaintTarget;
 import com.vaadin.server.Resource;
-import com.vaadin.ui.Component;
 import com.vaadin.v7.data.Container;
 import com.vaadin.v7.data.util.filter.SimpleStringFilter;
-import com.vaadin.v7.shared.ui.combobox.ComboBoxServerRpc;
+import com.vaadin.v7.shared.ui.combobox.ComboBoxConstants;
 import com.vaadin.v7.shared.ui.combobox.ComboBoxState;
 import com.vaadin.v7.shared.ui.combobox.FilteringMode;
 
@@ -79,59 +77,18 @@ public class ComboBox extends AbstractSelect
         public String getStyle(ComboBox source, Object itemId);
     }
 
-    private ComboBoxServerRpc rpc = new ComboBoxServerRpc() {
-        @Override
-        public void createNewItem(String itemValue) {
-            if (isNewItemsAllowed()) {
-                // New option entered (and it is allowed)
-                if (itemValue != null && itemValue.length() > 0) {
-                    getNewItemHandler().addNewItem(itemValue);
-                    // rebuild list
-                    filterstring = null;
-                    prevfilterstring = null;
-                }
-            }
-        }
-
-        @Override
-        public void setSelectedItem(String item) {
-            if (item == null) {
-                setValue(null, true);
-            } else {
-                final Object id = itemIdMapper.get(item);
-                if (id != null && id.equals(getNullSelectionItemId())) {
-                    setValue(null, true);
-                } else {
-                    setValue(id, true);
-                }
-            }
-        }
+    private String inputPrompt = null;
 
-        @Override
-        public void requestPage(String filter, int page) {
-            filterstring = filter;
-            if (filterstring != null) {
-                filterstring = filterstring.toLowerCase(getLocale());
-            }
-            currentPage = page;
-
-            // TODO this should trigger a data-only update instead of a full
-            // repaint
-            requestRepaint();
-        }
-    };
-
-    FocusAndBlurServerRpcImpl focusBlurRpc = new FocusAndBlurServerRpcImpl(
-            this) {
-        @Override
-        protected void fireEvent(Component.Event event) {
-            ComboBox.this.fireEvent(event);
-        }
-    };
+    /**
+     * Holds value of property pageLength. 0 disables paging.
+     */
+    protected int pageLength = 10;
 
     // Current page when the user is 'paging' trough options
     private int currentPage = -1;
 
+    private FilteringMode filteringMode = FilteringMode.STARTSWITH;
+
     private String filterstring;
     private String prevfilterstring;
 
@@ -166,35 +123,40 @@ public class ComboBox extends AbstractSelect
      */
     private boolean scrollToSelectedItem = true;
 
+    private String suggestionPopupWidth = null;
+
+    /**
+     * If text input is not allowed, the ComboBox behaves like a pretty
+     * NativeSelect - the user can not enter any text and clicking the text
+     * field opens the drop down with options
+     */
+    private boolean textInputAllowed = true;
+
     private ItemStyleGenerator itemStyleGenerator = null;
 
     public ComboBox() {
-        init();
+        initDefaults();
     }
 
     public ComboBox(String caption, Collection<?> options) {
         super(caption, options);
-        init();
+        initDefaults();
     }
 
     public ComboBox(String caption, Container dataSource) {
         super(caption, dataSource);
-        init();
+        initDefaults();
     }
 
     public ComboBox(String caption) {
         super(caption);
-        init();
+        initDefaults();
     }
 
     /**
-     * Initialize the ComboBox with default settings and register client to
-     * server RPC implementation.
+     * Initialize the ComboBox with default settings
      */
-    private void init() {
-        registerRpc(rpc);
-        registerRpc(focusBlurRpc);
-
+    private void initDefaults() {
         setNewItemsAllowed(false);
         setImmediate(true);
     }
@@ -206,7 +168,7 @@ public class ComboBox extends AbstractSelect
      * @return the current input prompt, or null if not enabled
      */
     public String getInputPrompt() {
-        return getState(false).inputPrompt;
+        return inputPrompt;
     }
 
     /**
@@ -217,46 +179,42 @@ public class ComboBox extends AbstractSelect
      *            the desired input prompt, or null to disable
      */
     public void setInputPrompt(String inputPrompt) {
-        getState().inputPrompt = inputPrompt;
+        this.inputPrompt = inputPrompt;
+        markAsDirty();
     }
 
     private boolean isFilteringNeeded() {
         return filterstring != null && filterstring.length() > 0
-                && getFilteringMode() != FilteringMode.OFF;
-    }
-
-    /**
-     * A class representing an item in a ComboBox for server to client
-     * communication. This class is for internal use only and subject to change.
-     *
-     * @since 8.0
-     */
-    private static class ComboBoxItem implements Serializable {
-        String key = "";
-        String caption = "";
-        String style = null;
-        Resource icon = null;
-
-        // constructor for a null item
-        public ComboBoxItem() {
-        }
-
-        public ComboBoxItem(String key, String caption, String style,
-                Resource icon) {
-            this.key = key;
-            this.caption = caption;
-            this.style = style;
-            this.icon = icon;
-        }
+                && filteringMode != FilteringMode.OFF;
     }
 
     @Override
     public void paintContent(PaintTarget target) throws PaintException {
         isPainting = true;
         try {
+            if (inputPrompt != null) {
+                target.addAttribute(ComboBoxConstants.ATTR_INPUTPROMPT,
+                        inputPrompt);
+            }
+
+            if (!textInputAllowed) {
+                target.addAttribute(ComboBoxConstants.ATTR_NO_TEXT_INPUT, true);
+            }
+
             // clear caption change listeners
             getCaptionChangeListener().clear();
 
+            // The tab ordering number
+            if (getTabIndex() != 0) {
+                target.addAttribute("tabindex", getTabIndex());
+            }
+
+            // If the field is modified, but not committed, set modified
+            // attribute
+            if (isModified()) {
+                target.addAttribute("modified", true);
+            }
+
             if (isNewItemsAllowed()) {
                 target.addAttribute("allownewitem", true);
             }
@@ -274,9 +232,20 @@ public class ComboBox extends AbstractSelect
             String[] selectedKeys = new String[(getValue() == null
                     && getNullSelectionItemId() == null ? 0 : 1)];
 
+            target.addAttribute("pagelength", pageLength);
+
+            if (suggestionPopupWidth != null) {
+                target.addAttribute("suggestionPopupWidth",
+                        suggestionPopupWidth);
+            }
+
+            target.addAttribute("filteringmode", getFilteringMode().toString());
+
             // Paints the options and create array of selected id keys
             int keyIndex = 0;
 
+            target.startTag("options");
+
             if (currentPage < 0) {
                 optionRequest = false;
                 currentPage = 0;
@@ -285,7 +254,8 @@ public class ComboBox extends AbstractSelect
 
             boolean nullFilteredOut = isFilteringNeeded();
             // null option is needed and not filtered out, even if not on
-            // current page
+            // current
+            // page
             boolean nullOptionVisible = needNullSelectOption
                     && !nullFilteredOut;
 
@@ -296,18 +266,20 @@ public class ComboBox extends AbstractSelect
                 // filtering
                 options = getFilteredOptions();
                 filteredSize = options.size();
-                options = sanitetizeList(options, nullOptionVisible);
+                options = sanitizeList(options, nullOptionVisible);
             }
 
             final boolean paintNullSelection = needNullSelectOption
                     && currentPage == 0 && !nullFilteredOut;
 
-            List<ComboBoxItem> items = new ArrayList<>();
-
             if (paintNullSelection) {
-                ComboBoxItem item = new ComboBoxItem();
-                item.style = getItemStyle(null);
-                items.add(item);
+                target.startTag("so");
+                target.addAttribute("caption", "");
+                target.addAttribute("key", "");
+
+                paintItemStyle(target, null);
+
+                target.endTag("so");
             }
 
             final Iterator<?> i = options.iterator();
@@ -327,31 +299,24 @@ public class ComboBox extends AbstractSelect
                 final String key = itemIdMapper.key(id);
                 final String caption = getItemCaption(id);
                 final Resource icon = getItemIcon(id);
-
                 getCaptionChangeListener().addNotifierForItem(id);
 
-                // Prepare to paint the option
-                ComboBoxItem item = new ComboBoxItem(key, caption,
-                        getItemStyle(id), icon);
-                items.add(item);
+                // Paints the option
+                target.startTag("so");
+                if (icon != null) {
+                    target.addAttribute("icon", icon);
+                }
+                target.addAttribute("caption", caption);
+                if (id != null && id.equals(getNullSelectionItemId())) {
+                    target.addAttribute("nullselection", true);
+                }
+                target.addAttribute("key", key);
                 if (keyIndex < selectedKeys.length && isSelected(id)) {
                     // at most one item can be selected at a time
                     selectedKeys[keyIndex++] = key;
                 }
-            }
 
-            // paint the items
-            target.startTag("options");
-            for (ComboBoxItem item : items) {
-                target.startTag("so");
-                if (item.icon != null) {
-                    target.addAttribute("icon", item.icon);
-                }
-                target.addAttribute("caption", item.caption);
-                target.addAttribute("key", item.key);
-                if (item.style != null) {
-                    target.addAttribute("style", item.style);
-                }
+                paintItemStyle(target, id);
 
                 target.endTag("so");
             }
@@ -389,11 +354,14 @@ public class ComboBox extends AbstractSelect
 
     }
 
-    private String getItemStyle(Object itemId) throws PaintException {
+    private void paintItemStyle(PaintTarget target, Object itemId)
+            throws PaintException {
         if (itemStyleGenerator != null) {
-            return itemStyleGenerator.getStyle(this, itemId);
+            String style = itemStyleGenerator.getStyle(this, itemId);
+            if (style != null && !style.isEmpty()) {
+                target.addAttribute("style", style);
+            }
         }
-        return null;
     }
 
     /**
@@ -409,7 +377,8 @@ public class ComboBox extends AbstractSelect
      *            selection
      */
     public void setTextInputAllowed(boolean textInputAllowed) {
-        getState().textInputAllowed = textInputAllowed;
+        this.textInputAllowed = textInputAllowed;
+        markAsDirty();
     }
 
     /**
@@ -421,7 +390,7 @@ public class ComboBox extends AbstractSelect
      * @return
      */
     public boolean isTextInputAllowed() {
-        return getState(false).textInputAllowed;
+        return textInputAllowed;
     }
 
     @Override
@@ -429,11 +398,6 @@ public class ComboBox extends AbstractSelect
         return (ComboBoxState) super.getState();
     }
 
-    @Override
-    protected ComboBoxState getState(boolean markAsDirty) {
-        return (ComboBoxState) super.getState(markAsDirty);
-    }
-
     /**
      * Returns the filtered options for the current page using a container
      * filter.
@@ -446,7 +410,7 @@ public class ComboBox extends AbstractSelect
      * {@link #canUseContainerFilter()}).
      *
      * Use {@link #getFilteredOptions()} and
-     * {@link #sanitetizeList(List, boolean)} if this is not the case.
+     * {@link #sanitizeList(List, boolean)} if this is not the case.
      *
      * @param needNullSelectOption
      * @return filtered list of options (may be empty) or null if cannot use
@@ -455,7 +419,7 @@ public class ComboBox extends AbstractSelect
     protected List<?> getOptionsWithFilter(boolean needNullSelectOption) {
         Container container = getContainerDataSource();
 
-        if (getPageLength() == 0 && !isFilteringNeeded()) {
+        if (pageLength == 0 && !isFilteringNeeded()) {
             // no paging or filtering: return all items
             filteredSize = container.size();
             assert filteredSize >= 0;
@@ -470,7 +434,7 @@ public class ComboBox extends AbstractSelect
 
         Filterable filterable = (Filterable) container;
 
-        Filter filter = buildFilter(filterstring, getFilteringMode());
+        Filter filter = buildFilter(filterstring, filteringMode);
 
         // adding and removing filters leads to extraneous item set
         // change events from the underlying container, but the ComboBox does
@@ -563,23 +527,25 @@ public class ComboBox extends AbstractSelect
 
     /**
      * Makes correct sublist of given list of options.
-     *
+     * <p>
      * If paint is not an option request (affected by page or filter change),
      * page will be the one where possible selection exists.
-     *
+     * <p>
      * Detects proper first and last item in list to return right page of
      * options. Also, if the current page is beyond the end of the list, it will
      * be adjusted.
+     * <p>
+     * Package private only for testing purposes.
      *
      * @param options
      * @param needNullSelectOption
      *            flag to indicate if nullselect option needs to be taken into
      *            consideration
      */
-    private List<?> sanitetizeList(List<?> options,
-            boolean needNullSelectOption) {
-
-        if (getPageLength() != 0 && options.size() > getPageLength()) {
+    List<?> sanitizeList(List<?> options, boolean needNullSelectOption) {
+        int totalRows = options.size() + (needNullSelectOption ? 1 : 0);
+        if (pageLength != 0 && totalRows > pageLength) {
+            // options will not fit on one page
 
             int indexToEnsureInView = -1;
 
@@ -625,7 +591,7 @@ public class ComboBox extends AbstractSelect
             int size) {
         // Not all options are visible, find out which ones are on the
         // current "page".
-        int first = currentPage * getPageLength();
+        int first = currentPage * pageLength;
         if (needNullSelectOption && currentPage > 0) {
             first--;
         }
@@ -652,7 +618,7 @@ public class ComboBox extends AbstractSelect
     private int getLastItemIndexOnCurrentPage(boolean needNullSelectOption,
             int size, int first) {
         // page length usable for non-null items
-        int effectivePageLength = getPageLength()
+        int effectivePageLength = pageLength
                 - (needNullSelectOption && (currentPage == 0) ? 1 : 0);
         return Math.min(size - 1, first + effectivePageLength - 1);
     }
@@ -680,12 +646,12 @@ public class ComboBox extends AbstractSelect
             int indexToEnsureInView, int size) {
         if (indexToEnsureInView != -1) {
             int newPage = (indexToEnsureInView + (needNullSelectOption ? 1 : 0))
-                    / getPageLength();
+                    / pageLength;
             page = newPage;
         }
         // adjust the current page if beyond the end of the list
-        if (page * getPageLength() > size) {
-            page = (size + (needNullSelectOption ? 1 : 0)) / getPageLength();
+        if (page * pageLength > size) {
+            page = (size + (needNullSelectOption ? 1 : 0)) / pageLength;
         }
         return page;
     }
@@ -728,7 +694,7 @@ public class ComboBox extends AbstractSelect
             } else {
                 caption = caption.toLowerCase(getLocale());
             }
-            switch (getFilteringMode()) {
+            switch (filteringMode) {
             case CONTAINS:
                 if (caption.indexOf(filterstring) > -1) {
                     filteredOptions.add(itemId);
@@ -757,17 +723,66 @@ public class ComboBox extends AbstractSelect
         // Not calling super.changeVariables due the history of select
         // component hierarchy
 
-        // all the client to server requests are now handled by RPC
+        // Selection change
+        if (variables.containsKey("selected")) {
+            final String[] ka = (String[]) variables.get("selected");
+
+            // Single select mode
+            if (ka.length == 0) {
+
+                // Allows deselection only if the deselected item is visible
+                final Object current = getValue();
+                final Collection<?> visible = getVisibleItemIds();
+                if (visible != null && visible.contains(current)) {
+                    setValue(null, true);
+                }
+            } else {
+                final Object id = itemIdMapper.get(ka[0]);
+                if (id != null && id.equals(getNullSelectionItemId())) {
+                    setValue(null, true);
+                } else {
+                    setValue(id, true);
+                }
+            }
+        }
+
+        String newFilter;
+        if ((newFilter = (String) variables.get("filter")) != null) {
+            // this is a filter request
+            currentPage = ((Integer) variables.get("page")).intValue();
+            filterstring = newFilter;
+            if (filterstring != null) {
+                filterstring = filterstring.toLowerCase(getLocale());
+            }
+            requestRepaint();
+        } else if (isNewItemsAllowed()) {
+            // New option entered (and it is allowed)
+            final String newitem = (String) variables.get("newitem");
+            if (newitem != null && newitem.length() > 0) {
+                getNewItemHandler().addNewItem(newitem);
+                // rebuild list
+                filterstring = null;
+                prevfilterstring = null;
+            }
+        }
+
+        if (variables.containsKey(FocusEvent.EVENT_ID)) {
+            fireEvent(new FocusEvent(this));
+        }
+        if (variables.containsKey(BlurEvent.EVENT_ID)) {
+            fireEvent(new BlurEvent(this));
+        }
+
     }
 
     @Override
     public void setFilteringMode(FilteringMode filteringMode) {
-        getState().filteringMode = filteringMode;
+        this.filteringMode = filteringMode;
     }
 
     @Override
     public FilteringMode getFilteringMode() {
-        return getState(false).filteringMode;
+        return filteringMode;
     }
 
     @Override
@@ -797,7 +812,7 @@ public class ComboBox extends AbstractSelect
      *
      * @deprecated As of 7.0, use {@link ListSelect}, {@link OptionGroup} or
      *             {@link TwinColSelect} instead
-     * @see com.vaadin.v7.ui.AbstractSelect#setMultiSelect(boolean)
+     * @see com.vaadin.ui.AbstractSelect#setMultiSelect(boolean)
      * @throws UnsupportedOperationException
      *             if trying to activate multiselect mode
      */
@@ -816,7 +831,7 @@ public class ComboBox extends AbstractSelect
      * @deprecated As of 7.0, use {@link ListSelect}, {@link OptionGroup} or
      *             {@link TwinColSelect} instead
      *
-     * @see com.vaadin.v7.ui.AbstractSelect#isMultiSelect()
+     * @see com.vaadin.ui.AbstractSelect#isMultiSelect()
      *
      * @return false
      */
@@ -832,7 +847,7 @@ public class ComboBox extends AbstractSelect
      * @return the pageLength
      */
     public int getPageLength() {
-        return getState(false).pageLength;
+        return pageLength;
     }
 
     /**
@@ -842,7 +857,7 @@ public class ComboBox extends AbstractSelect
      * @since 7.7
      */
     public String getPopupWidth() {
-        return getState(false).suggestionPopupWidth;
+        return suggestionPopupWidth;
     }
 
     /**
@@ -853,7 +868,8 @@ public class ComboBox extends AbstractSelect
      *            the pageLength to set
      */
     public void setPageLength(int pageLength) {
-        getState().pageLength = pageLength;
+        this.pageLength = pageLength;
+        markAsDirty();
     }
 
     /**
@@ -867,7 +883,8 @@ public class ComboBox extends AbstractSelect
      *            the width
      */
     public void setPopupWidth(String width) {
-        getState().suggestionPopupWidth = width;
+        suggestionPopupWidth = width;
+        markAsDirty();
     }
 
     /**
index ffb46c519aee065c1ca8d70cf181e849fb630be5..e62358f272ee1fdb80fe74d9d0b514b90d57db19 100644 (file)
@@ -1,10 +1,10 @@
 package com.vaadin.v7.tests.server.components;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.junit.Before;
 
-import com.vaadin.server.ServerRpcManager;
-import com.vaadin.server.ServerRpcMethodInvocation;
-import com.vaadin.v7.shared.ui.combobox.ComboBoxServerRpc;
 import com.vaadin.v7.tests.server.component.abstractfield.AbstractFieldValueChangeTestBase;
 import com.vaadin.v7.ui.AbstractField;
 import com.vaadin.v7.ui.ComboBox;
@@ -20,28 +20,16 @@ public class ComboBoxValueChangeTest
 
     @Before
     public void setUp() {
-        ComboBox combo = new ComboBox() {
-            @Override
-            public String getConnectorId() {
-                return "id";
-            }
-        };
+        ComboBox combo = new ComboBox();
         combo.addItem("myvalue");
         super.setUp(combo);
     }
 
     @Override
     protected void setValue(AbstractField<Object> field) {
-        ComboBox combo = (ComboBox) field;
-        ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
-                combo.getConnectorId(), ComboBoxServerRpc.class,
-                "setSelectedItem", 1);
-        invocation.setParameters(new Object[] { "myvalue" });
-        try {
-            ServerRpcManager.applyInvocation(combo, invocation);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
+        Map<String, Object> variables = new HashMap<>();
+        variables.put("selected", new String[] { "myvalue" });
+        ((ComboBox) field).changeVariables(field, variables);
     }
 
 }
diff --git a/compatibility-server/src/test/java/com/vaadin/v7/ui/ComboBoxTest.java b/compatibility-server/src/test/java/com/vaadin/v7/ui/ComboBoxTest.java
new file mode 100644 (file)
index 0000000..62a0c0b
--- /dev/null
@@ -0,0 +1,158 @@
+package com.vaadin.v7.ui;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.v7.shared.ui.combobox.FilteringMode;
+
+public class ComboBoxTest {
+
+    private ComboBox comboBox;
+
+    @Before
+    public void setup() {
+        comboBox = new ComboBox();
+        comboBox.setLocale(Locale.ENGLISH);
+    }
+
+    @Test
+    public void options_noFilter() {
+        ComboBox comboBox = new ComboBox();
+        for (int i = 0; i < 10; i++) {
+            comboBox.addItem("" + i);
+        }
+
+        List<?> options = comboBox.getFilteredOptions();
+        Assert.assertEquals(10, options.size());
+        for (int i = 0; i < 10; i++) {
+            Assert.assertEquals("" + i, options.get(i));
+        }
+
+    }
+
+    @Test
+    public void options_inMemoryFilteringStartsWith() {
+        for (int i = 0; i < 21; i++) {
+            comboBox.addItem("" + i);
+        }
+
+        setFilterAndCurrentPage(comboBox, "1", 0);
+
+        List<?> options = comboBox.getFilteredOptions();
+        Assert.assertEquals(11, options.size());
+
+    }
+
+    @Test
+    public void options_inMemoryFilteringContains() {
+        comboBox.setFilteringMode(FilteringMode.CONTAINS);
+        for (int i = 0; i < 21; i++) {
+            comboBox.addItem("" + i);
+        }
+
+        setFilterAndCurrentPage(comboBox, "2", 0);
+        List<?> options = comboBox.getFilteredOptions();
+        Assert.assertEquals(3, options.size());
+
+    }
+
+    private static void setFilterAndCurrentPage(ComboBox comboBox,
+            String filterString, int currentPage) {
+        Map<String, Object> variables = new HashMap<>();
+        variables.put("filter", filterString);
+        variables.put("page", currentPage);
+        comboBox.changeVariables(null, variables);
+
+    }
+
+    @Test
+    public void getOptions_moreThanOnePage_noNullItem() {
+        int nrOptions = comboBox.getPageLength() * 2;
+        for (int i = 0; i < nrOptions; i++) {
+            comboBox.addItem("" + i);
+        }
+        setFilterAndCurrentPage(comboBox, "", 0);
+
+        List<?> goingToClient = comboBox
+                .sanitizeList(comboBox.getFilteredOptions(), false);
+        Assert.assertEquals(comboBox.getPageLength(), goingToClient.size());
+
+    }
+
+    @Test
+    public void getOptions_moreThanOnePage_nullItem() {
+        int nrOptions = comboBox.getPageLength() * 2;
+        for (int i = 0; i < nrOptions; i++) {
+            comboBox.addItem("" + i);
+        }
+
+        setFilterAndCurrentPage(comboBox, "", 0);
+        List<?> goingToClient = comboBox
+                .sanitizeList(comboBox.getFilteredOptions(), true);
+        // Null item is shown on first page
+        Assert.assertEquals(comboBox.getPageLength() - 1, goingToClient.size());
+
+        setFilterAndCurrentPage(comboBox, "", 1);
+        goingToClient = comboBox.sanitizeList(comboBox.getFilteredOptions(),
+                true);
+        // Null item is not shown on the second page
+        Assert.assertEquals(comboBox.getPageLength(), goingToClient.size());
+
+    }
+
+    @Test
+    public void getOptions_lessThanOnePage_noNullItem() {
+        int nrOptions = comboBox.getPageLength() / 2;
+        for (int i = 0; i < nrOptions; i++) {
+            comboBox.addItem("" + i);
+        }
+        setFilterAndCurrentPage(comboBox, "", 0);
+
+        List<?> goingToClient = comboBox
+                .sanitizeList(comboBox.getFilteredOptions(), false);
+        Assert.assertEquals(nrOptions, goingToClient.size());
+
+    }
+
+    @Test
+    public void getOptions_lessThanOnePage_withNullItem() {
+        int nrOptions = comboBox.getPageLength() / 2;
+        for (int i = 0; i < nrOptions; i++) {
+            comboBox.addItem("" + i);
+        }
+        setFilterAndCurrentPage(comboBox, "", 0);
+
+        List<?> goingToClient = comboBox
+                .sanitizeList(comboBox.getFilteredOptions(), true);
+        // All items + null still fit on one page
+        Assert.assertEquals(nrOptions, goingToClient.size());
+
+    }
+
+    @Test
+    public void getOptions_exactlyOnePage_withNullItem() {
+        int nrOptions = comboBox.getPageLength();
+        for (int i = 0; i < nrOptions; i++) {
+            comboBox.addItem("" + i);
+        }
+        setFilterAndCurrentPage(comboBox, "", 0);
+
+        List<?> goingToClient = comboBox
+                .sanitizeList(comboBox.getFilteredOptions(), true);
+        // Null item on first page
+        Assert.assertEquals(nrOptions - 1, goingToClient.size());
+
+        setFilterAndCurrentPage(comboBox, "", 1);
+        goingToClient = comboBox.sanitizeList(comboBox.getFilteredOptions(),
+                true);
+        // All but one was on the first page
+        Assert.assertEquals(1, goingToClient.size());
+
+    }
+}
diff --git a/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/combobox/ComboBoxServerRpc.java b/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/combobox/ComboBoxServerRpc.java
deleted file mode 100644 (file)
index 69d360b..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.v7.shared.ui.combobox;
-
-import com.vaadin.shared.communication.ServerRpc;
-
-/**
- * Client to server RPC interface for ComboBox.
- *
- * @since 8.0
- */
-public interface ComboBoxServerRpc extends ServerRpc {
-    /**
-     * Create a new item in the combo box. This method can only be used when the
-     * ComboBox is configured to allow the creation of new items by the user.
-     *
-     * @param itemValue
-     *            user entered string value for the new item
-     */
-    public void createNewItem(String itemValue);
-
-    /**
-     * Set the current selection.
-     *
-     * @param item
-     *            the id of a single item or null to deselect the current value
-     */
-    public void setSelectedItem(String item);
-
-    /**
-     * Request the server to send a page of the item list.
-     *
-     * @param filter
-     *            filter string interpreted according to the current filtering
-     *            mode
-     * @param page
-     *            zero based page number
-     */
-    public void requestPage(String filter, int page);
-}
index b1b9a7252e11c42a400ae33541e58347a5ccb701..d76effc404e41751bd222659441c685fce5106c7 100644 (file)
@@ -26,40 +26,4 @@ public class ComboBoxState extends AbstractSelectState {
     {
         primaryStyleName = "v-filterselect";
     }
-
-    /**
-     * If text input is not allowed, the ComboBox behaves like a pretty
-     * NativeSelect - the user can not enter any text and clicking the text
-     * field opens the drop down with options.
-     *
-     * @since 8.0
-     */
-    public boolean textInputAllowed = true;
-
-    /**
-     * A textual prompt that is displayed when the select would otherwise be
-     * empty, to prompt the user for input.
-     *
-     * @since 8.0
-     */
-    public String inputPrompt = null;
-
-    /**
-     * Number of items to show per page or 0 to disable paging.
-     */
-    public int pageLength = 10;
-
-    /**
-     * Current filtering mode (look for match of the user typed string in the
-     * beginning of the item caption or anywhere in the item caption).
-     */
-    public FilteringMode filteringMode = FilteringMode.STARTSWITH;
-
-    /**
-     * Suggestion pop-up's width as a CSS string. By using relative units (e.g.
-     * "50%") it's possible to set the popup's width relative to the ComboBox
-     * itself.
-     */
-    public String suggestionPopupWidth = null;
-
 }