You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Select.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*
  2. * Copyright 2011 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.HashSet;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import com.vaadin.data.Container;
  26. import com.vaadin.data.util.filter.SimpleStringFilter;
  27. import com.vaadin.event.FieldEvents;
  28. import com.vaadin.event.FieldEvents.BlurEvent;
  29. import com.vaadin.event.FieldEvents.BlurListener;
  30. import com.vaadin.event.FieldEvents.FocusEvent;
  31. import com.vaadin.event.FieldEvents.FocusListener;
  32. import com.vaadin.terminal.PaintException;
  33. import com.vaadin.terminal.PaintTarget;
  34. import com.vaadin.terminal.Resource;
  35. /**
  36. * <p>
  37. * A class representing a selection of items the user has selected in a UI. The
  38. * set of choices is presented as a set of {@link com.vaadin.data.Item}s in a
  39. * {@link com.vaadin.data.Container}.
  40. * </p>
  41. *
  42. * <p>
  43. * A <code>Select</code> component may be in single- or multiselect mode.
  44. * Multiselect mode means that more than one item can be selected
  45. * simultaneously.
  46. * </p>
  47. *
  48. * @author Vaadin Ltd.
  49. * @since 3.0
  50. */
  51. @SuppressWarnings("serial")
  52. public class Select extends AbstractSelect implements AbstractSelect.Filtering,
  53. FieldEvents.BlurNotifier, FieldEvents.FocusNotifier {
  54. /**
  55. * Holds value of property pageLength. 0 disables paging.
  56. */
  57. protected int pageLength = 10;
  58. private int columns = 0;
  59. // Current page when the user is 'paging' trough options
  60. private int currentPage = -1;
  61. private int filteringMode = FILTERINGMODE_STARTSWITH;
  62. private String filterstring;
  63. private String prevfilterstring;
  64. /**
  65. * Number of options that pass the filter, excluding the null item if any.
  66. */
  67. private int filteredSize;
  68. /**
  69. * Cache of filtered options, used only by the in-memory filtering system.
  70. */
  71. private List<Object> filteredOptions;
  72. /**
  73. * Flag to indicate that request repaint is called by filter request only
  74. */
  75. private boolean optionRequest;
  76. /**
  77. * True if the container is being filtered temporarily and item set change
  78. * notifications should be suppressed.
  79. */
  80. private boolean filteringContainer;
  81. /**
  82. * Flag to indicate whether to scroll the selected item visible (select the
  83. * page on which it is) when opening the popup or not. Only applies to
  84. * single select mode.
  85. *
  86. * This requires finding the index of the item, which can be expensive in
  87. * many large lazy loading containers.
  88. */
  89. private boolean scrollToSelectedItem = true;
  90. /* Constructors */
  91. /* Component methods */
  92. public Select() {
  93. super();
  94. }
  95. public Select(String caption, Collection<?> options) {
  96. super(caption, options);
  97. }
  98. public Select(String caption, Container dataSource) {
  99. super(caption, dataSource);
  100. }
  101. public Select(String caption) {
  102. super(caption);
  103. }
  104. /**
  105. * Paints the content of this component.
  106. *
  107. * @param target
  108. * the Paint Event.
  109. * @throws PaintException
  110. * if the paint operation failed.
  111. */
  112. @Override
  113. public void paintContent(PaintTarget target) throws PaintException {
  114. if (isMultiSelect()) {
  115. // background compatibility hack. This object shouldn't be used for
  116. // multiselect lists anymore (ListSelect instead). This fallbacks to
  117. // a simpler paint method in super class.
  118. super.paintContent(target);
  119. // Fix for #4553
  120. target.addAttribute("type", "legacy-multi");
  121. return;
  122. }
  123. // clear caption change listeners
  124. getCaptionChangeListener().clear();
  125. // The tab ordering number
  126. if (getTabIndex() != 0) {
  127. target.addAttribute("tabindex", getTabIndex());
  128. }
  129. // If the field is modified, but not committed, set modified attribute
  130. if (isModified()) {
  131. target.addAttribute("modified", true);
  132. }
  133. if (isNewItemsAllowed()) {
  134. target.addAttribute("allownewitem", true);
  135. }
  136. boolean needNullSelectOption = false;
  137. if (isNullSelectionAllowed()) {
  138. target.addAttribute("nullselect", true);
  139. needNullSelectOption = (getNullSelectionItemId() == null);
  140. if (!needNullSelectOption) {
  141. target.addAttribute("nullselectitem", true);
  142. }
  143. }
  144. // Constructs selected keys array
  145. String[] selectedKeys;
  146. if (isMultiSelect()) {
  147. selectedKeys = new String[((Set<?>) getValue()).size()];
  148. } else {
  149. selectedKeys = new String[(getValue() == null
  150. && getNullSelectionItemId() == null ? 0 : 1)];
  151. }
  152. target.addAttribute("pagelength", pageLength);
  153. target.addAttribute("filteringmode", getFilteringMode());
  154. // Paints the options and create array of selected id keys
  155. int keyIndex = 0;
  156. target.startTag("options");
  157. if (currentPage < 0) {
  158. optionRequest = false;
  159. currentPage = 0;
  160. filterstring = "";
  161. }
  162. boolean nullFilteredOut = filterstring != null
  163. && !"".equals(filterstring)
  164. && filteringMode != FILTERINGMODE_OFF;
  165. // null option is needed and not filtered out, even if not on current
  166. // page
  167. boolean nullOptionVisible = needNullSelectOption && !nullFilteredOut;
  168. // first try if using container filters is possible
  169. List<?> options = getOptionsWithFilter(nullOptionVisible);
  170. if (null == options) {
  171. // not able to use container filters, perform explicit in-memory
  172. // filtering
  173. options = getFilteredOptions();
  174. filteredSize = options.size();
  175. options = sanitetizeList(options, nullOptionVisible);
  176. }
  177. final boolean paintNullSelection = needNullSelectOption
  178. && currentPage == 0 && !nullFilteredOut;
  179. if (paintNullSelection) {
  180. target.startTag("so");
  181. target.addAttribute("caption", "");
  182. target.addAttribute("key", "");
  183. target.endTag("so");
  184. }
  185. final Iterator<?> i = options.iterator();
  186. // Paints the available selection options from data source
  187. while (i.hasNext()) {
  188. final Object id = i.next();
  189. if (!isNullSelectionAllowed() && id != null
  190. && id.equals(getNullSelectionItemId()) && !isSelected(id)) {
  191. continue;
  192. }
  193. // Gets the option attribute values
  194. final String key = itemIdMapper.key(id);
  195. final String caption = getItemCaption(id);
  196. final Resource icon = getItemIcon(id);
  197. getCaptionChangeListener().addNotifierForItem(id);
  198. // Paints the option
  199. target.startTag("so");
  200. if (icon != null) {
  201. target.addAttribute("icon", icon);
  202. }
  203. target.addAttribute("caption", caption);
  204. if (id != null && id.equals(getNullSelectionItemId())) {
  205. target.addAttribute("nullselection", true);
  206. }
  207. target.addAttribute("key", key);
  208. if (isSelected(id) && keyIndex < selectedKeys.length) {
  209. target.addAttribute("selected", true);
  210. selectedKeys[keyIndex++] = key;
  211. }
  212. target.endTag("so");
  213. }
  214. target.endTag("options");
  215. target.addAttribute("totalitems", size()
  216. + (needNullSelectOption ? 1 : 0));
  217. if (filteredSize > 0 || nullOptionVisible) {
  218. target.addAttribute("totalMatches", filteredSize
  219. + (nullOptionVisible ? 1 : 0));
  220. }
  221. // Paint variables
  222. target.addVariable(this, "selected", selectedKeys);
  223. if (isNewItemsAllowed()) {
  224. target.addVariable(this, "newitem", "");
  225. }
  226. target.addVariable(this, "filter", filterstring);
  227. target.addVariable(this, "page", currentPage);
  228. currentPage = -1; // current page is always set by client
  229. optionRequest = true;
  230. }
  231. /**
  232. * Returns the filtered options for the current page using a container
  233. * filter.
  234. *
  235. * As a size effect, {@link #filteredSize} is set to the total number of
  236. * items passing the filter.
  237. *
  238. * The current container must be {@link Filterable} and {@link Indexed}, and
  239. * the filtering mode must be suitable for container filtering (tested with
  240. * {@link #canUseContainerFilter()}).
  241. *
  242. * Use {@link #getFilteredOptions()} and
  243. * {@link #sanitetizeList(List, boolean)} if this is not the case.
  244. *
  245. * @param needNullSelectOption
  246. * @return filtered list of options (may be empty) or null if cannot use
  247. * container filters
  248. */
  249. protected List<?> getOptionsWithFilter(boolean needNullSelectOption) {
  250. Container container = getContainerDataSource();
  251. if (pageLength == 0) {
  252. // no paging: return all items
  253. filteredSize = container.size();
  254. return new ArrayList<Object>(container.getItemIds());
  255. }
  256. if (!(container instanceof Filterable)
  257. || !(container instanceof Indexed)
  258. || getItemCaptionMode() != ITEM_CAPTION_MODE_PROPERTY) {
  259. return null;
  260. }
  261. Filterable filterable = (Filterable) container;
  262. Filter filter = buildFilter(filterstring, filteringMode);
  263. // adding and removing filters leads to extraneous item set
  264. // change events from the underlying container, but the ComboBox does
  265. // not process or propagate them based on the flag filteringContainer
  266. if (filter != null) {
  267. filteringContainer = true;
  268. filterable.addContainerFilter(filter);
  269. }
  270. Indexed indexed = (Indexed) container;
  271. int indexToEnsureInView = -1;
  272. // if not an option request (item list when user changes page), go
  273. // to page with the selected item after filtering if accepted by
  274. // filter
  275. Object selection = getValue();
  276. if (isScrollToSelectedItem() && !optionRequest && !isMultiSelect()
  277. && selection != null) {
  278. // ensure proper page
  279. indexToEnsureInView = indexed.indexOfId(selection);
  280. }
  281. filteredSize = container.size();
  282. currentPage = adjustCurrentPage(currentPage, needNullSelectOption,
  283. indexToEnsureInView, filteredSize);
  284. int first = getFirstItemIndexOnCurrentPage(needNullSelectOption,
  285. filteredSize);
  286. int last = getLastItemIndexOnCurrentPage(needNullSelectOption,
  287. filteredSize, first);
  288. List<Object> options = new ArrayList<Object>();
  289. for (int i = first; i <= last && i < filteredSize; ++i) {
  290. options.add(indexed.getIdByIndex(i));
  291. }
  292. // to the outside, filtering should not be visible
  293. if (filter != null) {
  294. filterable.removeContainerFilter(filter);
  295. filteringContainer = false;
  296. }
  297. return options;
  298. }
  299. /**
  300. * Constructs a filter instance to use when using a Filterable container in
  301. * the <code>ITEM_CAPTION_MODE_PROPERTY</code> mode.
  302. *
  303. * Note that the client side implementation expects the filter string to
  304. * apply to the item caption string it sees, so changing the behavior of
  305. * this method can cause problems.
  306. *
  307. * @param filterString
  308. * @param filteringMode
  309. * @return
  310. */
  311. protected Filter buildFilter(String filterString, int filteringMode) {
  312. Filter filter = null;
  313. if (null != filterString && !"".equals(filterString)) {
  314. switch (filteringMode) {
  315. case FILTERINGMODE_OFF:
  316. break;
  317. case FILTERINGMODE_STARTSWITH:
  318. filter = new SimpleStringFilter(getItemCaptionPropertyId(),
  319. filterString, true, true);
  320. break;
  321. case FILTERINGMODE_CONTAINS:
  322. filter = new SimpleStringFilter(getItemCaptionPropertyId(),
  323. filterString, true, false);
  324. break;
  325. }
  326. }
  327. return filter;
  328. }
  329. @Override
  330. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  331. if (!filteringContainer) {
  332. super.containerItemSetChange(event);
  333. }
  334. }
  335. /**
  336. * Makes correct sublist of given list of options.
  337. *
  338. * If paint is not an option request (affected by page or filter change),
  339. * page will be the one where possible selection exists.
  340. *
  341. * Detects proper first and last item in list to return right page of
  342. * options. Also, if the current page is beyond the end of the list, it will
  343. * be adjusted.
  344. *
  345. * @param options
  346. * @param needNullSelectOption
  347. * flag to indicate if nullselect option needs to be taken into
  348. * consideration
  349. */
  350. private List<?> sanitetizeList(List<?> options, boolean needNullSelectOption) {
  351. if (pageLength != 0 && options.size() > pageLength) {
  352. int indexToEnsureInView = -1;
  353. // if not an option request (item list when user changes page), go
  354. // to page with the selected item after filtering if accepted by
  355. // filter
  356. Object selection = getValue();
  357. if (isScrollToSelectedItem() && !optionRequest && !isMultiSelect()
  358. && selection != null) {
  359. // ensure proper page
  360. indexToEnsureInView = options.indexOf(selection);
  361. }
  362. int size = options.size();
  363. currentPage = adjustCurrentPage(currentPage, needNullSelectOption,
  364. indexToEnsureInView, size);
  365. int first = getFirstItemIndexOnCurrentPage(needNullSelectOption,
  366. size);
  367. int last = getLastItemIndexOnCurrentPage(needNullSelectOption,
  368. size, first);
  369. return options.subList(first, last + 1);
  370. } else {
  371. return options;
  372. }
  373. }
  374. /**
  375. * Returns the index of the first item on the current page. The index is to
  376. * the underlying (possibly filtered) contents. The null item, if any, does
  377. * not have an index but takes up a slot on the first page.
  378. *
  379. * @param needNullSelectOption
  380. * true if a null option should be shown before any other options
  381. * (takes up the first slot on the first page, not counted in
  382. * index)
  383. * @param size
  384. * number of items after filtering (not including the null item,
  385. * if any)
  386. * @return first item to show on the UI (index to the filtered list of
  387. * options, not taking the null item into consideration if any)
  388. */
  389. private int getFirstItemIndexOnCurrentPage(boolean needNullSelectOption,
  390. int size) {
  391. // Not all options are visible, find out which ones are on the
  392. // current "page".
  393. int first = currentPage * pageLength;
  394. if (needNullSelectOption && currentPage > 0) {
  395. first--;
  396. }
  397. return first;
  398. }
  399. /**
  400. * Returns the index of the last item on the current page. The index is to
  401. * the underlying (possibly filtered) contents. If needNullSelectOption is
  402. * true, the null item takes up the first slot on the first page,
  403. * effectively reducing the first page size by one.
  404. *
  405. * @param needNullSelectOption
  406. * true if a null option should be shown before any other options
  407. * (takes up the first slot on the first page, not counted in
  408. * index)
  409. * @param size
  410. * number of items after filtering (not including the null item,
  411. * if any)
  412. * @param first
  413. * index in the filtered view of the first item of the page
  414. * @return index in the filtered view of the last item on the page
  415. */
  416. private int getLastItemIndexOnCurrentPage(boolean needNullSelectOption,
  417. int size, int first) {
  418. // page length usable for non-null items
  419. int effectivePageLength = pageLength
  420. - (needNullSelectOption && (currentPage == 0) ? 1 : 0);
  421. return Math.min(size - 1, first + effectivePageLength - 1);
  422. }
  423. /**
  424. * Adjusts the index of the current page if necessary: make sure the current
  425. * page is not after the end of the contents, and optionally go to the page
  426. * containg a specific item. There are no side effects but the adjusted page
  427. * index is returned.
  428. *
  429. * @param page
  430. * page number to use as the starting point
  431. * @param needNullSelectOption
  432. * true if a null option should be shown before any other options
  433. * (takes up the first slot on the first page, not counted in
  434. * index)
  435. * @param indexToEnsureInView
  436. * index of an item that should be included on the page (in the
  437. * data set, not counting the null item if any), -1 for none
  438. * @param size
  439. * number of items after filtering (not including the null item,
  440. * if any)
  441. */
  442. private int adjustCurrentPage(int page, boolean needNullSelectOption,
  443. int indexToEnsureInView, int size) {
  444. if (indexToEnsureInView != -1) {
  445. int newPage = (indexToEnsureInView + (needNullSelectOption ? 1 : 0))
  446. / pageLength;
  447. page = newPage;
  448. }
  449. // adjust the current page if beyond the end of the list
  450. if (page * pageLength > size) {
  451. page = (size + (needNullSelectOption ? 1 : 0)) / pageLength;
  452. }
  453. return page;
  454. }
  455. /**
  456. * Filters the options in memory and returns the full filtered list.
  457. *
  458. * This can be less efficient than using container filters, so use
  459. * {@link #getOptionsWithFilter(boolean)} if possible (filterable container
  460. * and suitable item caption mode etc.).
  461. *
  462. * @return
  463. */
  464. protected List<?> getFilteredOptions() {
  465. if (null == filterstring || "".equals(filterstring)
  466. || FILTERINGMODE_OFF == filteringMode) {
  467. prevfilterstring = null;
  468. filteredOptions = new LinkedList<Object>(getItemIds());
  469. return filteredOptions;
  470. }
  471. if (filterstring.equals(prevfilterstring)) {
  472. return filteredOptions;
  473. }
  474. Collection<?> items;
  475. if (prevfilterstring != null
  476. && filterstring.startsWith(prevfilterstring)) {
  477. items = filteredOptions;
  478. } else {
  479. items = getItemIds();
  480. }
  481. prevfilterstring = filterstring;
  482. filteredOptions = new LinkedList<Object>();
  483. for (final Iterator<?> it = items.iterator(); it.hasNext();) {
  484. final Object itemId = it.next();
  485. String caption = getItemCaption(itemId);
  486. if (caption == null || caption.equals("")) {
  487. continue;
  488. } else {
  489. caption = caption.toLowerCase();
  490. }
  491. switch (filteringMode) {
  492. case FILTERINGMODE_CONTAINS:
  493. if (caption.indexOf(filterstring) > -1) {
  494. filteredOptions.add(itemId);
  495. }
  496. break;
  497. case FILTERINGMODE_STARTSWITH:
  498. default:
  499. if (caption.startsWith(filterstring)) {
  500. filteredOptions.add(itemId);
  501. }
  502. break;
  503. }
  504. }
  505. return filteredOptions;
  506. }
  507. /**
  508. * Invoked when the value of a variable has changed.
  509. *
  510. * @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
  511. * java.util.Map)
  512. */
  513. @Override
  514. public void changeVariables(Object source, Map<String, Object> variables) {
  515. // Not calling super.changeVariables due the history of select
  516. // component hierarchy
  517. // Selection change
  518. if (variables.containsKey("selected")) {
  519. final String[] ka = (String[]) variables.get("selected");
  520. if (isMultiSelect()) {
  521. // Multiselect mode
  522. // TODO Optimize by adding repaintNotNeeded whan applicaple
  523. // Converts the key-array to id-set
  524. final LinkedList<Object> s = new LinkedList<Object>();
  525. for (int i = 0; i < ka.length; i++) {
  526. final Object id = itemIdMapper.get(ka[i]);
  527. if (id != null && containsId(id)) {
  528. s.add(id);
  529. }
  530. }
  531. // Limits the deselection to the set of visible items
  532. // (non-visible items can not be deselected)
  533. final Collection<?> visible = getVisibleItemIds();
  534. if (visible != null) {
  535. @SuppressWarnings("unchecked")
  536. Set<Object> newsel = (Set<Object>) getValue();
  537. if (newsel == null) {
  538. newsel = new HashSet<Object>();
  539. } else {
  540. newsel = new HashSet<Object>(newsel);
  541. }
  542. newsel.removeAll(visible);
  543. newsel.addAll(s);
  544. setValue(newsel, true);
  545. }
  546. } else {
  547. // Single select mode
  548. if (ka.length == 0) {
  549. // Allows deselection only if the deselected item is visible
  550. final Object current = getValue();
  551. final Collection<?> visible = getVisibleItemIds();
  552. if (visible != null && visible.contains(current)) {
  553. setValue(null, true);
  554. }
  555. } else {
  556. final Object id = itemIdMapper.get(ka[0]);
  557. if (id != null && id.equals(getNullSelectionItemId())) {
  558. setValue(null, true);
  559. } else {
  560. setValue(id, true);
  561. }
  562. }
  563. }
  564. }
  565. String newFilter;
  566. if ((newFilter = (String) variables.get("filter")) != null) {
  567. // this is a filter request
  568. currentPage = ((Integer) variables.get("page")).intValue();
  569. filterstring = newFilter;
  570. if (filterstring != null) {
  571. filterstring = filterstring.toLowerCase();
  572. }
  573. optionRepaint();
  574. } else if (isNewItemsAllowed()) {
  575. // New option entered (and it is allowed)
  576. final String newitem = (String) variables.get("newitem");
  577. if (newitem != null && newitem.length() > 0) {
  578. getNewItemHandler().addNewItem(newitem);
  579. // rebuild list
  580. filterstring = null;
  581. prevfilterstring = null;
  582. }
  583. }
  584. if (variables.containsKey(FocusEvent.EVENT_ID)) {
  585. fireEvent(new FocusEvent(this));
  586. }
  587. if (variables.containsKey(BlurEvent.EVENT_ID)) {
  588. fireEvent(new BlurEvent(this));
  589. }
  590. }
  591. @Override
  592. public void requestRepaint() {
  593. super.requestRepaint();
  594. optionRequest = false;
  595. prevfilterstring = filterstring;
  596. filterstring = null;
  597. }
  598. private void optionRepaint() {
  599. super.requestRepaint();
  600. }
  601. @Override
  602. public void setFilteringMode(int filteringMode) {
  603. this.filteringMode = filteringMode;
  604. }
  605. @Override
  606. public int getFilteringMode() {
  607. return filteringMode;
  608. }
  609. @Override
  610. public void addListener(BlurListener listener) {
  611. addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener,
  612. BlurListener.blurMethod);
  613. }
  614. @Override
  615. public void removeListener(BlurListener listener) {
  616. removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener);
  617. }
  618. @Override
  619. public void addListener(FocusListener listener) {
  620. addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener,
  621. FocusListener.focusMethod);
  622. }
  623. @Override
  624. public void removeListener(FocusListener listener) {
  625. removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener);
  626. }
  627. /**
  628. * @deprecated use {@link ListSelect}, {@link OptionGroup} or
  629. * {@link TwinColSelect} instead
  630. * @see com.vaadin.ui.AbstractSelect#setMultiSelect(boolean)
  631. * @throws UnsupportedOperationException
  632. * if trying to activate multiselect mode
  633. */
  634. @Deprecated
  635. @Override
  636. public void setMultiSelect(boolean multiSelect) {
  637. if (multiSelect) {
  638. throw new UnsupportedOperationException("Multiselect not supported");
  639. }
  640. }
  641. /**
  642. * @deprecated use {@link ListSelect}, {@link OptionGroup} or
  643. * {@link TwinColSelect} instead
  644. *
  645. * @see com.vaadin.ui.AbstractSelect#isMultiSelect()
  646. */
  647. @Deprecated
  648. @Override
  649. public boolean isMultiSelect() {
  650. return super.isMultiSelect();
  651. }
  652. /**
  653. * Sets whether to scroll the selected item visible (directly open the page
  654. * on which it is) when opening the combo box popup or not. Only applies to
  655. * single select mode.
  656. *
  657. * This requires finding the index of the item, which can be expensive in
  658. * many large lazy loading containers.
  659. *
  660. * @param scrollToSelectedItem
  661. * true to find the page with the selected item when opening the
  662. * selection popup
  663. */
  664. public void setScrollToSelectedItem(boolean scrollToSelectedItem) {
  665. this.scrollToSelectedItem = scrollToSelectedItem;
  666. }
  667. /**
  668. * Returns true if the select should find the page with the selected item
  669. * when opening the popup (single select combo box only).
  670. *
  671. * @see #setScrollToSelectedItem(boolean)
  672. *
  673. * @return true if the page with the selected item will be shown when
  674. * opening the popup
  675. */
  676. public boolean isScrollToSelectedItem() {
  677. return scrollToSelectedItem;
  678. }
  679. }