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.

AbstractSelect.java 76KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364
  1. /*
  2. * Copyright 2000-2016 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.v7.ui;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.EventObject;
  22. import java.util.HashMap;
  23. import java.util.HashSet;
  24. import java.util.Iterator;
  25. import java.util.LinkedHashSet;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import com.vaadin.ui.AbstractListing;
  31. import org.jsoup.nodes.Element;
  32. import com.vaadin.event.Transferable;
  33. import com.vaadin.event.dd.DragAndDropEvent;
  34. import com.vaadin.event.dd.DropTarget;
  35. import com.vaadin.event.dd.TargetDetailsImpl;
  36. import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
  37. import com.vaadin.event.dd.acceptcriteria.ContainsDataFlavor;
  38. import com.vaadin.event.dd.acceptcriteria.TargetDetailIs;
  39. import com.vaadin.server.KeyMapper;
  40. import com.vaadin.server.PaintException;
  41. import com.vaadin.server.PaintTarget;
  42. import com.vaadin.server.Resource;
  43. import com.vaadin.server.VaadinSession;
  44. import com.vaadin.shared.ui.dd.VerticalDropLocation;
  45. import com.vaadin.ui.AbstractComponent;
  46. import com.vaadin.ui.Component;
  47. import com.vaadin.ui.LegacyComponent;
  48. import com.vaadin.ui.declarative.DesignAttributeHandler;
  49. import com.vaadin.ui.declarative.DesignContext;
  50. import com.vaadin.ui.declarative.DesignException;
  51. import com.vaadin.ui.declarative.DesignFormatter;
  52. import com.vaadin.v7.data.Container;
  53. import com.vaadin.v7.data.Item;
  54. import com.vaadin.v7.data.Property;
  55. import com.vaadin.v7.data.Validator.InvalidValueException;
  56. import com.vaadin.v7.data.util.IndexedContainer;
  57. import com.vaadin.v7.data.util.converter.Converter;
  58. import com.vaadin.v7.data.util.converter.Converter.ConversionException;
  59. import com.vaadin.v7.data.util.converter.ConverterUtil;
  60. import com.vaadin.v7.event.DataBoundTransferable;
  61. import com.vaadin.v7.shared.ui.combobox.FilteringMode;
  62. import com.vaadin.v7.shared.ui.select.AbstractSelectState;
  63. /**
  64. * <p>
  65. * A class representing a selection of items the user has selected in a UI. The
  66. * set of choices is presented as a set of {@link Item}s in a {@link Container}.
  67. * </p>
  68. *
  69. * <p>
  70. * A <code>Select</code> component may be in single- or multiselect mode.
  71. * Multiselect mode means that more than one item can be selected
  72. * simultaneously.
  73. * </p>
  74. *
  75. * @author Vaadin Ltd.
  76. * @since 5.0
  77. * @deprecated As of 8.0, replaced by {@link AbstractListing}
  78. */
  79. @SuppressWarnings("serial")
  80. @Deprecated
  81. // TODO currently cannot specify type more precisely in case of multi-select
  82. public abstract class AbstractSelect extends AbstractField<Object> implements
  83. Container, Container.Viewer, Container.PropertySetChangeListener,
  84. Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier,
  85. Container.ItemSetChangeListener, LegacyComponent {
  86. @Deprecated
  87. public enum ItemCaptionMode {
  88. /**
  89. * Item caption mode: Item's ID converted to a String using
  90. * {@link VaadinSession#getConverterFactory()} is used as caption.
  91. */
  92. ID,
  93. /**
  94. * Item caption mode: Item's ID's <code>String</code> representation is
  95. * used as caption.
  96. *
  97. * @since 7.5.6
  98. */
  99. ID_TOSTRING,
  100. /**
  101. * Item caption mode: Item's <code>String</code> representation is used
  102. * as caption.
  103. */
  104. ITEM,
  105. /**
  106. * Item caption mode: Index of the item is used as caption. The index
  107. * mode can only be used with the containers implementing the
  108. * {@link Container.Indexed} interface.
  109. */
  110. INDEX,
  111. /**
  112. * Item caption mode: If an Item has a caption it's used, if not, Item's
  113. * ID converted to a String using
  114. * {@link VaadinSession#getConverterFactory()} is used as caption.
  115. * <b>This is the default</b>.
  116. */
  117. EXPLICIT_DEFAULTS_ID,
  118. /**
  119. * Item caption mode: Captions must be explicitly specified.
  120. */
  121. EXPLICIT,
  122. /**
  123. * Item caption mode: Only icons are shown, captions are hidden.
  124. */
  125. ICON_ONLY,
  126. /**
  127. * Item caption mode: Item captions are read from property specified
  128. * with <code>setItemCaptionPropertyId</code>.
  129. */
  130. PROPERTY;
  131. }
  132. /**
  133. * @deprecated As of 7.0, use {@link ItemCaptionMode#ID} instead
  134. */
  135. @Deprecated
  136. public static final ItemCaptionMode ITEM_CAPTION_MODE_ID = ItemCaptionMode.ID;
  137. /**
  138. * @deprecated As of 7.0, use {@link ItemCaptionMode#ITEM} instead
  139. */
  140. @Deprecated
  141. public static final ItemCaptionMode ITEM_CAPTION_MODE_ITEM = ItemCaptionMode.ITEM;
  142. /**
  143. * @deprecated As of 7.0, use {@link ItemCaptionMode#INDEX} instead
  144. */
  145. @Deprecated
  146. public static final ItemCaptionMode ITEM_CAPTION_MODE_INDEX = ItemCaptionMode.INDEX;
  147. /**
  148. * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID}
  149. * instead
  150. */
  151. @Deprecated
  152. public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = ItemCaptionMode.EXPLICIT_DEFAULTS_ID;
  153. /**
  154. * @deprecated As of 7.0, use {@link ItemCaptionMode#EXPLICIT} instead
  155. */
  156. @Deprecated
  157. public static final ItemCaptionMode ITEM_CAPTION_MODE_EXPLICIT = ItemCaptionMode.EXPLICIT;
  158. /**
  159. * @deprecated As of 7.0, use {@link ItemCaptionMode#ICON_ONLY} instead
  160. */
  161. @Deprecated
  162. public static final ItemCaptionMode ITEM_CAPTION_MODE_ICON_ONLY = ItemCaptionMode.ICON_ONLY;
  163. /**
  164. * @deprecated As of 7.0, use {@link ItemCaptionMode#PROPERTY} instead
  165. */
  166. @Deprecated
  167. public static final ItemCaptionMode ITEM_CAPTION_MODE_PROPERTY = ItemCaptionMode.PROPERTY;
  168. /**
  169. * Interface for option filtering, used to filter options based on user
  170. * entered value. The value is matched to the item caption.
  171. * <code>FilteringMode.OFF</code> (0) turns the filtering off.
  172. * <code>FilteringMode.STARTSWITH</code> (1) matches from the start of the
  173. * caption. <code>FilteringMode.CONTAINS</code> (1) matches anywhere in the
  174. * caption.
  175. */
  176. @Deprecated
  177. public interface Filtering extends Serializable {
  178. /**
  179. * @deprecated As of 7.0, use {@link FilteringMode#OFF} instead
  180. */
  181. @Deprecated
  182. public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF;
  183. /**
  184. * @deprecated As of 7.0, use {@link FilteringMode#STARTSWITH} instead
  185. */
  186. @Deprecated
  187. public static final FilteringMode FILTERINGMODE_STARTSWITH = FilteringMode.STARTSWITH;
  188. /**
  189. * @deprecated As of 7.0, use {@link FilteringMode#CONTAINS} instead
  190. */
  191. @Deprecated
  192. public static final FilteringMode FILTERINGMODE_CONTAINS = FilteringMode.CONTAINS;
  193. /**
  194. * Sets the option filtering mode.
  195. *
  196. * @param filteringMode
  197. * the filtering mode to use
  198. */
  199. public void setFilteringMode(FilteringMode filteringMode);
  200. /**
  201. * Gets the current filtering mode.
  202. *
  203. * @return the filtering mode in use
  204. */
  205. public FilteringMode getFilteringMode();
  206. }
  207. /**
  208. * Is the select in multiselect mode?
  209. */
  210. private boolean multiSelect = false;
  211. /**
  212. * Select options.
  213. */
  214. protected Container items;
  215. /**
  216. * Is the user allowed to add new options?
  217. */
  218. private boolean allowNewOptions;
  219. /**
  220. * Keymapper used to map key values.
  221. */
  222. protected KeyMapper<Object> itemIdMapper = new KeyMapper<Object>();
  223. /**
  224. * Item icons.
  225. */
  226. private final HashMap<Object, Resource> itemIcons = new HashMap<Object, Resource>();
  227. /**
  228. * Item captions.
  229. */
  230. private final HashMap<Object, String> itemCaptions = new HashMap<Object, String>();
  231. /**
  232. * Item caption mode.
  233. */
  234. private ItemCaptionMode itemCaptionMode = ItemCaptionMode.EXPLICIT_DEFAULTS_ID;
  235. /**
  236. * Item caption source property id.
  237. */
  238. private Object itemCaptionPropertyId = null;
  239. /**
  240. * Item icon source property id.
  241. */
  242. private Object itemIconPropertyId = null;
  243. /**
  244. * List of property set change event listeners.
  245. */
  246. private Set<Container.PropertySetChangeListener> propertySetEventListeners = null;
  247. /**
  248. * List of item set change event listeners.
  249. */
  250. private Set<Container.ItemSetChangeListener> itemSetEventListeners = null;
  251. /**
  252. * Item id that represents null selection of this select.
  253. *
  254. * <p>
  255. * Data interface does not support nulls as item ids. Selecting the item
  256. * identified by this id is the same as selecting no items at all. This
  257. * setting only affects the single select mode.
  258. * </p>
  259. */
  260. private Object nullSelectionItemId = null;
  261. // Null (empty) selection is enabled by default
  262. private boolean nullSelectionAllowed = true;
  263. private NewItemHandler newItemHandler;
  264. // Caption (Item / Property) change listeners
  265. CaptionChangeListener captionChangeListener;
  266. /* Constructors */
  267. /**
  268. * Creates an empty Select. The caption is not used.
  269. */
  270. public AbstractSelect() {
  271. setContainerDataSource(new IndexedContainer());
  272. }
  273. /**
  274. * Creates an empty Select with caption.
  275. */
  276. public AbstractSelect(String caption) {
  277. setContainerDataSource(new IndexedContainer());
  278. setCaption(caption);
  279. }
  280. /**
  281. * Creates a new select that is connected to a data-source.
  282. *
  283. * @param caption
  284. * the Caption of the component.
  285. * @param dataSource
  286. * the Container datasource to be selected from by this select.
  287. */
  288. public AbstractSelect(String caption, Container dataSource) {
  289. setCaption(caption);
  290. setContainerDataSource(dataSource);
  291. }
  292. /**
  293. * Creates a new select that is filled from a collection of option values.
  294. *
  295. * @param caption
  296. * the Caption of this field.
  297. * @param options
  298. * the Collection containing the options.
  299. */
  300. public AbstractSelect(String caption, Collection<?> options) {
  301. // Creates the options container and add given options to it
  302. final Container c = new IndexedContainer();
  303. if (options != null) {
  304. for (final Object item : options) {
  305. c.addItem(item);
  306. }
  307. }
  308. setCaption(caption);
  309. setContainerDataSource(c);
  310. }
  311. /* Component methods */
  312. /**
  313. * Paints the content of this component.
  314. *
  315. * @param target
  316. * the Paint Event.
  317. * @throws PaintException
  318. * if the paint operation failed.
  319. */
  320. @Override
  321. public void paintContent(PaintTarget target) throws PaintException {
  322. // Paints select attributes
  323. if (isMultiSelect()) {
  324. target.addAttribute("selectmode", "multi");
  325. }
  326. if (isNewItemsAllowed()) {
  327. target.addAttribute("allownewitem", true);
  328. }
  329. if (isNullSelectionAllowed()) {
  330. target.addAttribute("nullselect", true);
  331. if (getNullSelectionItemId() != null) {
  332. target.addAttribute("nullselectitem", true);
  333. }
  334. }
  335. // Constructs selected keys array
  336. String[] selectedKeys;
  337. if (isMultiSelect()) {
  338. selectedKeys = new String[((Set<?>) getValue()).size()];
  339. } else {
  340. selectedKeys = new String[(getValue() == null
  341. && getNullSelectionItemId() == null ? 0 : 1)];
  342. }
  343. // ==
  344. // first remove all previous item/property listeners
  345. getCaptionChangeListener().clear();
  346. // Paints the options and create array of selected id keys
  347. target.startTag("options");
  348. int keyIndex = 0;
  349. // Support for external null selection item id
  350. final Collection<?> ids = getItemIds();
  351. if (isNullSelectionAllowed() && getNullSelectionItemId() != null
  352. && !ids.contains(getNullSelectionItemId())) {
  353. final Object id = getNullSelectionItemId();
  354. // Paints option
  355. target.startTag("so");
  356. paintItem(target, id);
  357. if (isSelected(id)) {
  358. selectedKeys[keyIndex++] = itemIdMapper.key(id);
  359. }
  360. target.endTag("so");
  361. }
  362. final Iterator<?> i = getItemIds().iterator();
  363. // Paints the available selection options from data source
  364. while (i.hasNext()) {
  365. // Gets the option attribute values
  366. final Object id = i.next();
  367. if (!isNullSelectionAllowed() && id != null
  368. && id.equals(getNullSelectionItemId())) {
  369. // Remove item if it's the null selection item but null
  370. // selection is not allowed
  371. continue;
  372. }
  373. final String key = itemIdMapper.key(id);
  374. // add listener for each item, to cause repaint if an item changes
  375. getCaptionChangeListener().addNotifierForItem(id);
  376. target.startTag("so");
  377. paintItem(target, id);
  378. if (isSelected(id) && keyIndex < selectedKeys.length) {
  379. selectedKeys[keyIndex++] = key;
  380. }
  381. target.endTag("so");
  382. }
  383. target.endTag("options");
  384. // ==
  385. // Paint variables
  386. target.addVariable(this, "selected", selectedKeys);
  387. if (isNewItemsAllowed()) {
  388. target.addVariable(this, "newitem", "");
  389. }
  390. }
  391. protected void paintItem(PaintTarget target, Object itemId)
  392. throws PaintException {
  393. final String key = itemIdMapper.key(itemId);
  394. final String caption = getItemCaption(itemId);
  395. final Resource icon = getItemIcon(itemId);
  396. if (icon != null) {
  397. target.addAttribute("icon", icon);
  398. }
  399. target.addAttribute("caption", caption);
  400. if (itemId != null && itemId.equals(getNullSelectionItemId())) {
  401. target.addAttribute("nullselection", true);
  402. }
  403. target.addAttribute("key", key);
  404. if (isSelected(itemId)) {
  405. target.addAttribute("selected", true);
  406. }
  407. }
  408. /**
  409. * Invoked when the value of a variable has changed.
  410. *
  411. * @see AbstractComponent#changeVariables(java.lang.Object, java.util.Map)
  412. */
  413. @Override
  414. public void changeVariables(Object source, Map<String, Object> variables) {
  415. // New option entered (and it is allowed)
  416. if (isNewItemsAllowed()) {
  417. final String newitem = (String) variables.get("newitem");
  418. if (newitem != null && !newitem.isEmpty()) {
  419. getNewItemHandler().addNewItem(newitem);
  420. }
  421. }
  422. // Selection change
  423. if (variables.containsKey("selected")) {
  424. final String[] clientSideSelectedKeys = (String[]) variables
  425. .get("selected");
  426. // Multiselect mode
  427. if (isMultiSelect()) {
  428. // TODO Optimize by adding repaintNotNeeded when applicable
  429. // Converts the key-array to id-set
  430. final LinkedList<Object> acceptedSelections = new LinkedList<Object>();
  431. for (int i = 0; i < clientSideSelectedKeys.length; i++) {
  432. final Object id = itemIdMapper
  433. .get(clientSideSelectedKeys[i]);
  434. if (!isNullSelectionAllowed()
  435. && (id == null || id == getNullSelectionItemId())) {
  436. // skip empty selection if nullselection is not allowed
  437. markAsDirty();
  438. } else if (id != null && containsId(id)) {
  439. acceptedSelections.add(id);
  440. }
  441. }
  442. if (!isNullSelectionAllowed()
  443. && acceptedSelections.size() < 1) {
  444. // empty selection not allowed, keep old value
  445. markAsDirty();
  446. return;
  447. }
  448. // Limits the deselection to the set of visible items
  449. // (non-visible items can not be deselected)
  450. Collection<?> visibleNotSelected = getVisibleItemIds();
  451. if (visibleNotSelected != null) {
  452. visibleNotSelected = new HashSet<Object>(
  453. visibleNotSelected);
  454. // Don't remove those that will be added to preserve order
  455. visibleNotSelected.removeAll(acceptedSelections);
  456. @SuppressWarnings("unchecked")
  457. Set<Object> newsel = (Set<Object>) getValue();
  458. if (newsel == null) {
  459. newsel = new LinkedHashSet<Object>();
  460. } else {
  461. newsel = new LinkedHashSet<Object>(newsel);
  462. }
  463. newsel.removeAll(visibleNotSelected);
  464. newsel.addAll(acceptedSelections);
  465. setValue(newsel, true);
  466. }
  467. } else {
  468. // Single select mode
  469. if (!isNullSelectionAllowed()
  470. && (clientSideSelectedKeys.length == 0
  471. || clientSideSelectedKeys[0] == null
  472. || clientSideSelectedKeys[0] == getNullSelectionItemId())) {
  473. markAsDirty();
  474. return;
  475. }
  476. if (clientSideSelectedKeys.length == 0) {
  477. // Allows deselection only if the deselected item is
  478. // visible
  479. final Object current = getValue();
  480. final Collection<?> visible = getVisibleItemIds();
  481. if (visible != null && visible.contains(current)) {
  482. setValue(null, true);
  483. }
  484. } else {
  485. String clientSelectedKey = clientSideSelectedKeys[0];
  486. if ("null".equals(clientSelectedKey)
  487. || itemIdMapper.containsKey(clientSelectedKey)) {
  488. // Happens to work for nullselection
  489. // (get ("null") -> null))
  490. final Object id = itemIdMapper.get(clientSelectedKey);
  491. if (!isNullSelectionAllowed() && id == null) {
  492. markAsDirty();
  493. } else if (id != null
  494. && id.equals(getNullSelectionItemId())) {
  495. setValue(null, true);
  496. } else {
  497. setValue(id, true);
  498. }
  499. }
  500. }
  501. }
  502. }
  503. }
  504. /**
  505. * TODO refine doc Setter for new item handler that is called when user adds
  506. * new item in newItemAllowed mode.
  507. *
  508. * @param newItemHandler
  509. */
  510. public void setNewItemHandler(NewItemHandler newItemHandler) {
  511. this.newItemHandler = newItemHandler;
  512. }
  513. /**
  514. * TODO refine doc
  515. *
  516. * @return
  517. */
  518. public NewItemHandler getNewItemHandler() {
  519. if (newItemHandler == null) {
  520. newItemHandler = new DefaultNewItemHandler();
  521. }
  522. return newItemHandler;
  523. }
  524. @Deprecated
  525. public interface NewItemHandler extends Serializable {
  526. void addNewItem(String newItemCaption);
  527. }
  528. /**
  529. * TODO refine doc
  530. *
  531. * This is a default class that handles adding new items that are typed by
  532. * user to selects container.
  533. *
  534. * By extending this class one may implement some logic on new item addition
  535. * like database inserts.
  536. *
  537. */
  538. @Deprecated
  539. public class DefaultNewItemHandler implements NewItemHandler {
  540. @Override
  541. public void addNewItem(String newItemCaption) {
  542. // Checks for readonly
  543. if (isReadOnly()) {
  544. throw new Property.ReadOnlyException();
  545. }
  546. // Adds new option
  547. if (addItem(newItemCaption) != null) {
  548. // Sets the caption property, if used
  549. if (getItemCaptionPropertyId() != null) {
  550. getContainerProperty(newItemCaption,
  551. getItemCaptionPropertyId())
  552. .setValue(newItemCaption);
  553. }
  554. if (isMultiSelect()) {
  555. Set values = new HashSet((Collection) getValue());
  556. values.add(newItemCaption);
  557. setValue(values);
  558. } else {
  559. setValue(newItemCaption);
  560. }
  561. }
  562. }
  563. }
  564. /**
  565. * Gets the visible item ids. In Select, this returns list of all item ids,
  566. * but can be overriden in subclasses if they paint only part of the items
  567. * to the terminal or null if no items is visible.
  568. */
  569. public Collection<?> getVisibleItemIds() {
  570. return getItemIds();
  571. }
  572. /* Property methods */
  573. /**
  574. * Returns the type of the property. <code>getValue</code> and
  575. * <code>setValue</code> methods must be compatible with this type: one can
  576. * safely cast <code>getValue</code> to given type and pass any variable
  577. * assignable to this type as a parameter to <code>setValue</code>.
  578. *
  579. * @return the Type of the property.
  580. */
  581. @Override
  582. public Class<?> getType() {
  583. if (isMultiSelect()) {
  584. return Set.class;
  585. } else {
  586. return Object.class;
  587. }
  588. }
  589. /**
  590. * Gets the selected item id or in multiselect mode a set of selected ids.
  591. *
  592. * @see AbstractField#getValue()
  593. */
  594. @Override
  595. public Object getValue() {
  596. final Object retValue = super.getValue();
  597. if (isMultiSelect()) {
  598. // If the return value is not a set
  599. if (retValue == null) {
  600. return new HashSet<Object>();
  601. }
  602. if (retValue instanceof Set) {
  603. return Collections.unmodifiableSet((Set<?>) retValue);
  604. } else if (retValue instanceof Collection) {
  605. return new HashSet<Object>((Collection<?>) retValue);
  606. } else {
  607. final Set<Object> s = new HashSet<Object>();
  608. if (items.containsId(retValue)) {
  609. s.add(retValue);
  610. }
  611. return s;
  612. }
  613. } else {
  614. return retValue;
  615. }
  616. }
  617. /**
  618. * Sets the visible value of the property.
  619. *
  620. * <p>
  621. * The value of the select is the selected item id. If the select is in
  622. * multiselect-mode, the value is a set of selected item keys. In
  623. * multiselect mode all collections of id:s can be assigned.
  624. * </p>
  625. *
  626. * @param newValue
  627. * the New selected item or collection of selected items.
  628. * @see AbstractField#setValue(java.lang.Object)
  629. */
  630. @Override
  631. public void setValue(Object newValue) throws Property.ReadOnlyException {
  632. if (newValue == getNullSelectionItemId()) {
  633. newValue = null;
  634. }
  635. setValue(newValue, false);
  636. }
  637. /**
  638. * Sets the visible value of the property.
  639. *
  640. * <p>
  641. * The value of the select is the selected item id. If the select is in
  642. * multiselect-mode, the value is a set of selected item keys. In
  643. * multiselect mode all collections of id:s can be assigned.
  644. * </p>
  645. *
  646. * @since 7.5.7
  647. * @param newValue
  648. * the New selected item or collection of selected items.
  649. * @param repaintIsNotNeeded
  650. * True if caller is sure that repaint is not needed.
  651. * @param ignoreReadOnly
  652. * True if read-only check should be omitted.
  653. * @see AbstractField#setValue(java.lang.Object, java.lang.Boolean)
  654. */
  655. @Override
  656. protected void setValue(Object newFieldValue, boolean repaintIsNotNeeded,
  657. boolean ignoreReadOnly) throws Property.ReadOnlyException,
  658. ConversionException, InvalidValueException {
  659. if (isMultiSelect()) {
  660. if (newFieldValue == null) {
  661. super.setValue(new LinkedHashSet<Object>(), repaintIsNotNeeded,
  662. ignoreReadOnly);
  663. } else if (Collection.class
  664. .isAssignableFrom(newFieldValue.getClass())) {
  665. super.setValue(
  666. new LinkedHashSet<Object>(
  667. (Collection<?>) newFieldValue),
  668. repaintIsNotNeeded, ignoreReadOnly);
  669. }
  670. } else if (newFieldValue == null || items.containsId(newFieldValue)) {
  671. super.setValue(newFieldValue, repaintIsNotNeeded, ignoreReadOnly);
  672. }
  673. }
  674. /* Container methods */
  675. /**
  676. * Gets the item from the container with given id. If the container does not
  677. * contain the requested item, null is returned.
  678. *
  679. * @param itemId
  680. * the item id.
  681. * @return the item from the container.
  682. */
  683. @Override
  684. public Item getItem(Object itemId) {
  685. return items.getItem(itemId);
  686. }
  687. /**
  688. * Gets the item Id collection from the container.
  689. *
  690. * @return the Collection of item ids.
  691. */
  692. @Override
  693. public Collection<?> getItemIds() {
  694. return items.getItemIds();
  695. }
  696. /**
  697. * Gets the property Id collection from the container.
  698. *
  699. * @return the Collection of property ids.
  700. */
  701. @Override
  702. public Collection<?> getContainerPropertyIds() {
  703. return items.getContainerPropertyIds();
  704. }
  705. /**
  706. * Gets the property type.
  707. *
  708. * @param propertyId
  709. * the Id identifying the property.
  710. * @see Container#getType(java.lang.Object)
  711. */
  712. @Override
  713. public Class<?> getType(Object propertyId) {
  714. return items.getType(propertyId);
  715. }
  716. /**
  717. * Gets the number of items in the container.
  718. *
  719. * @return the Number of items in the container.
  720. *
  721. * @see Container#size()
  722. */
  723. @Override
  724. public int size() {
  725. int size = items.size();
  726. assert size >= 0;
  727. return size;
  728. }
  729. /**
  730. * Tests, if the collection contains an item with given id.
  731. *
  732. * @param itemId
  733. * the Id the of item to be tested.
  734. */
  735. @Override
  736. public boolean containsId(Object itemId) {
  737. if (itemId != null) {
  738. return items.containsId(itemId);
  739. } else {
  740. return false;
  741. }
  742. }
  743. /**
  744. * Gets the Property identified by the given itemId and propertyId from the
  745. * Container
  746. *
  747. * @see Container#getContainerProperty(Object, Object)
  748. */
  749. @Override
  750. public Property getContainerProperty(Object itemId, Object propertyId) {
  751. return items.getContainerProperty(itemId, propertyId);
  752. }
  753. /**
  754. * Adds the new property to all items. Adds a property with given id, type
  755. * and default value to all items in the container.
  756. *
  757. * This functionality is optional. If the function is unsupported, it always
  758. * returns false.
  759. *
  760. * @return True if the operation succeeded.
  761. * @see Container#addContainerProperty(java.lang.Object, java.lang.Class,
  762. * java.lang.Object)
  763. */
  764. @Override
  765. public boolean addContainerProperty(Object propertyId, Class<?> type,
  766. Object defaultValue) throws UnsupportedOperationException {
  767. final boolean retval = items.addContainerProperty(propertyId, type,
  768. defaultValue);
  769. if (retval && !(items instanceof Container.PropertySetChangeNotifier)) {
  770. firePropertySetChange();
  771. }
  772. return retval;
  773. }
  774. /**
  775. * Removes all items from the container.
  776. *
  777. * This functionality is optional. If the function is unsupported, it always
  778. * returns false.
  779. *
  780. * @return True if the operation succeeded.
  781. * @see Container#removeAllItems()
  782. */
  783. @Override
  784. public boolean removeAllItems() throws UnsupportedOperationException {
  785. final boolean retval = items.removeAllItems();
  786. itemIdMapper.removeAll();
  787. if (retval) {
  788. setValue(null);
  789. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  790. fireItemSetChange();
  791. }
  792. }
  793. return retval;
  794. }
  795. /**
  796. * Creates a new item into container with container managed id. The id of
  797. * the created new item is returned. The item can be fetched with getItem()
  798. * method. if the creation fails, null is returned.
  799. *
  800. * @return the Id of the created item or null in case of failure.
  801. * @see Container#addItem()
  802. */
  803. @Override
  804. public Object addItem() throws UnsupportedOperationException {
  805. final Object retval = items.addItem();
  806. if (retval != null
  807. && !(items instanceof Container.ItemSetChangeNotifier)) {
  808. fireItemSetChange();
  809. }
  810. return retval;
  811. }
  812. /**
  813. * Create a new item into container. The created new item is returned and
  814. * ready for setting property values. if the creation fails, null is
  815. * returned. In case the container already contains the item, null is
  816. * returned.
  817. *
  818. * This functionality is optional. If the function is unsupported, it always
  819. * returns null.
  820. *
  821. * @param itemId
  822. * the Identification of the item to be created.
  823. * @return the Created item with the given id, or null in case of failure.
  824. * @see Container#addItem(java.lang.Object)
  825. */
  826. @Override
  827. public Item addItem(Object itemId) throws UnsupportedOperationException {
  828. final Item retval = items.addItem(itemId);
  829. if (retval != null
  830. && !(items instanceof Container.ItemSetChangeNotifier)) {
  831. fireItemSetChange();
  832. }
  833. return retval;
  834. }
  835. /**
  836. * Adds given items with given item ids to container.
  837. *
  838. * @since 7.2
  839. * @param itemId
  840. * item identifiers to be added to underlying container
  841. * @throws UnsupportedOperationException
  842. * if the underlying container don't support adding items with
  843. * identifiers
  844. */
  845. public void addItems(Object... itemId)
  846. throws UnsupportedOperationException {
  847. for (Object id : itemId) {
  848. addItem(id);
  849. }
  850. }
  851. /**
  852. * Adds given items with given item ids to container.
  853. *
  854. * @since 7.2
  855. * @param itemIds
  856. * item identifiers to be added to underlying container
  857. * @throws UnsupportedOperationException
  858. * if the underlying container don't support adding items with
  859. * identifiers
  860. */
  861. public void addItems(Collection<?> itemIds)
  862. throws UnsupportedOperationException {
  863. addItems(itemIds.toArray());
  864. }
  865. @Override
  866. public boolean removeItem(Object itemId)
  867. throws UnsupportedOperationException {
  868. unselect(itemId);
  869. final boolean retval = items.removeItem(itemId);
  870. itemIdMapper.remove(itemId);
  871. if (retval && !(items instanceof Container.ItemSetChangeNotifier)) {
  872. fireItemSetChange();
  873. }
  874. return retval;
  875. }
  876. /**
  877. * Checks that the current selection is valid, i.e. the selected item ids
  878. * exist in the container. Updates the selection if one or several selected
  879. * item ids are no longer available in the container.
  880. */
  881. @SuppressWarnings("unchecked")
  882. public void sanitizeSelection() {
  883. Object value = getValue();
  884. if (value == null) {
  885. return;
  886. }
  887. boolean changed = false;
  888. if (isMultiSelect()) {
  889. Collection<Object> valueAsCollection = (Collection<Object>) value;
  890. List<Object> newSelection = new ArrayList<Object>(
  891. valueAsCollection.size());
  892. for (Object subValue : valueAsCollection) {
  893. if (containsId(subValue)) {
  894. newSelection.add(subValue);
  895. } else {
  896. changed = true;
  897. }
  898. }
  899. if (changed) {
  900. setValue(newSelection);
  901. }
  902. } else {
  903. if (!containsId(value)) {
  904. setValue(null);
  905. }
  906. }
  907. }
  908. /**
  909. * Removes the property from all items. Removes a property with given id
  910. * from all the items in the container.
  911. *
  912. * This functionality is optional. If the function is unsupported, it always
  913. * returns false.
  914. *
  915. * @return True if the operation succeeded.
  916. * @see Container#removeContainerProperty(java.lang.Object)
  917. */
  918. @Override
  919. public boolean removeContainerProperty(Object propertyId)
  920. throws UnsupportedOperationException {
  921. final boolean retval = items.removeContainerProperty(propertyId);
  922. if (retval && !(items instanceof Container.PropertySetChangeNotifier)) {
  923. firePropertySetChange();
  924. }
  925. return retval;
  926. }
  927. /* Container.Viewer methods */
  928. /**
  929. * Sets the Container that serves as the data source of the viewer.
  930. *
  931. * As a side-effect the fields value (selection) is set to null due old
  932. * selection not necessary exists in new Container.
  933. *
  934. * @see Container.Viewer#setContainerDataSource(Container)
  935. *
  936. * @param newDataSource
  937. * the new data source.
  938. */
  939. @Override
  940. public void setContainerDataSource(Container newDataSource) {
  941. if (newDataSource == null) {
  942. newDataSource = new IndexedContainer();
  943. }
  944. getCaptionChangeListener().clear();
  945. if (items != newDataSource) {
  946. // Removes listeners from the old datasource
  947. if (items != null) {
  948. if (items instanceof Container.ItemSetChangeNotifier) {
  949. ((Container.ItemSetChangeNotifier) items)
  950. .removeItemSetChangeListener(this);
  951. }
  952. if (items instanceof Container.PropertySetChangeNotifier) {
  953. ((Container.PropertySetChangeNotifier) items)
  954. .removePropertySetChangeListener(this);
  955. }
  956. }
  957. // Assigns new data source
  958. items = newDataSource;
  959. // Clears itemIdMapper also
  960. itemIdMapper.removeAll();
  961. // Adds listeners
  962. if (items != null) {
  963. if (items instanceof Container.ItemSetChangeNotifier) {
  964. ((Container.ItemSetChangeNotifier) items)
  965. .addItemSetChangeListener(this);
  966. }
  967. if (items instanceof Container.PropertySetChangeNotifier) {
  968. ((Container.PropertySetChangeNotifier) items)
  969. .addPropertySetChangeListener(this);
  970. }
  971. }
  972. /*
  973. * We expect changing the data source should also clean value. See
  974. * #810, #4607, #5281
  975. */
  976. setValue(null);
  977. markAsDirty();
  978. }
  979. }
  980. /**
  981. * Gets the viewing data-source container.
  982. *
  983. * @see Container.Viewer#getContainerDataSource()
  984. */
  985. @Override
  986. public Container getContainerDataSource() {
  987. return items;
  988. }
  989. /* Select attributes */
  990. /**
  991. * Is the select in multiselect mode? In multiselect mode
  992. *
  993. * @return the Value of property multiSelect.
  994. */
  995. public boolean isMultiSelect() {
  996. return multiSelect;
  997. }
  998. /**
  999. * Sets the multiselect mode. Setting multiselect mode false may lose
  1000. * selection information: if selected items set contains one or more
  1001. * selected items, only one of the selected items is kept as selected.
  1002. *
  1003. * Subclasses of AbstractSelect can choose not to support changing the
  1004. * multiselect mode, and may throw {@link UnsupportedOperationException}.
  1005. *
  1006. * @param multiSelect
  1007. * the New value of property multiSelect.
  1008. */
  1009. public void setMultiSelect(boolean multiSelect) {
  1010. if (multiSelect && getNullSelectionItemId() != null) {
  1011. throw new IllegalStateException(
  1012. "Multiselect and NullSelectionItemId can not be set at the same time.");
  1013. }
  1014. if (multiSelect != this.multiSelect) {
  1015. // Selection before mode change
  1016. final Object oldValue = getValue();
  1017. this.multiSelect = multiSelect;
  1018. // Convert the value type
  1019. if (multiSelect) {
  1020. final Set<Object> s = new HashSet<Object>();
  1021. if (oldValue != null) {
  1022. s.add(oldValue);
  1023. }
  1024. setValue(s);
  1025. } else {
  1026. final Set<?> s = (Set<?>) oldValue;
  1027. if (s == null || s.isEmpty()) {
  1028. setValue(null);
  1029. } else {
  1030. // Set the single select to contain only the first
  1031. // selected value in the multiselect
  1032. setValue(s.iterator().next());
  1033. }
  1034. }
  1035. markAsDirty();
  1036. }
  1037. }
  1038. /**
  1039. * Does the select allow adding new options by the user. If true, the new
  1040. * options can be added to the Container. The text entered by the user is
  1041. * used as id. Note that data-source must allow adding new items.
  1042. *
  1043. * @return True if additions are allowed.
  1044. */
  1045. public boolean isNewItemsAllowed() {
  1046. return allowNewOptions;
  1047. }
  1048. /**
  1049. * Enables or disables possibility to add new options by the user.
  1050. *
  1051. * @param allowNewOptions
  1052. * the New value of property allowNewOptions.
  1053. */
  1054. public void setNewItemsAllowed(boolean allowNewOptions) {
  1055. // Only handle change requests
  1056. if (this.allowNewOptions != allowNewOptions) {
  1057. this.allowNewOptions = allowNewOptions;
  1058. markAsDirty();
  1059. }
  1060. }
  1061. /**
  1062. * Override the caption of an item. Setting caption explicitly overrides id,
  1063. * item and index captions.
  1064. *
  1065. * @param itemId
  1066. * the id of the item to be recaptioned.
  1067. * @param caption
  1068. * the New caption.
  1069. */
  1070. public void setItemCaption(Object itemId, String caption) {
  1071. if (itemId != null) {
  1072. itemCaptions.put(itemId, caption);
  1073. markAsDirty();
  1074. }
  1075. }
  1076. /**
  1077. * Gets the caption of an item. The caption is generated as specified by the
  1078. * item caption mode. See <code>setItemCaptionMode()</code> for more
  1079. * details.
  1080. *
  1081. * @param itemId
  1082. * the id of the item to be queried.
  1083. * @return the caption for specified item.
  1084. */
  1085. public String getItemCaption(Object itemId) {
  1086. // Null items can not be found
  1087. if (itemId == null) {
  1088. return null;
  1089. }
  1090. String caption = null;
  1091. switch (getItemCaptionMode()) {
  1092. case ID:
  1093. caption = idToCaption(itemId);
  1094. break;
  1095. case ID_TOSTRING:
  1096. caption = itemId.toString();
  1097. break;
  1098. case INDEX:
  1099. if (items instanceof Container.Indexed) {
  1100. caption = String
  1101. .valueOf(((Container.Indexed) items).indexOfId(itemId));
  1102. } else {
  1103. caption = "ERROR: Container is not indexed";
  1104. }
  1105. break;
  1106. case ITEM:
  1107. final Item i = getItem(itemId);
  1108. if (i != null) {
  1109. caption = i.toString();
  1110. }
  1111. break;
  1112. case EXPLICIT:
  1113. caption = itemCaptions.get(itemId);
  1114. break;
  1115. case EXPLICIT_DEFAULTS_ID:
  1116. caption = itemCaptions.get(itemId);
  1117. if (caption == null) {
  1118. caption = idToCaption(itemId);
  1119. }
  1120. break;
  1121. case PROPERTY:
  1122. final Property<?> p = getContainerProperty(itemId,
  1123. getItemCaptionPropertyId());
  1124. if (p != null) {
  1125. Object value = p.getValue();
  1126. if (value != null) {
  1127. caption = value.toString();
  1128. }
  1129. }
  1130. break;
  1131. }
  1132. // All items must have some captions
  1133. return caption != null ? caption : "";
  1134. }
  1135. private String idToCaption(Object itemId) {
  1136. try {
  1137. Converter<String, Object> c = (Converter<String, Object>) ConverterUtil
  1138. .getConverter(String.class, itemId.getClass(),
  1139. getSession());
  1140. return ConverterUtil.convertFromModel(itemId, String.class, c,
  1141. getLocale());
  1142. } catch (Exception e) {
  1143. return itemId.toString();
  1144. }
  1145. }
  1146. /**
  1147. * Sets the icon for an item.
  1148. *
  1149. * @param itemId
  1150. * the id of the item to be assigned an icon.
  1151. * @param icon
  1152. * the icon to use or null.
  1153. */
  1154. public void setItemIcon(Object itemId, Resource icon) {
  1155. if (itemId != null) {
  1156. if (icon == null) {
  1157. itemIcons.remove(itemId);
  1158. } else {
  1159. itemIcons.put(itemId, icon);
  1160. }
  1161. markAsDirty();
  1162. }
  1163. }
  1164. /**
  1165. * Gets the item icon.
  1166. *
  1167. * @param itemId
  1168. * the id of the item to be assigned an icon.
  1169. * @return the icon for the item or null, if not specified.
  1170. */
  1171. public Resource getItemIcon(Object itemId) {
  1172. final Resource explicit = itemIcons.get(itemId);
  1173. if (explicit != null) {
  1174. return explicit;
  1175. }
  1176. if (getItemIconPropertyId() == null) {
  1177. return null;
  1178. }
  1179. final Property<?> ip = getContainerProperty(itemId,
  1180. getItemIconPropertyId());
  1181. if (ip == null) {
  1182. return null;
  1183. }
  1184. final Object icon = ip.getValue();
  1185. if (icon instanceof Resource) {
  1186. return (Resource) icon;
  1187. }
  1188. return null;
  1189. }
  1190. /**
  1191. * Sets the item caption mode.
  1192. *
  1193. * See {@link ItemCaptionMode} for a description of the modes.
  1194. * <p>
  1195. * {@link ItemCaptionMode#EXPLICIT_DEFAULTS_ID} is the default mode.
  1196. * </p>
  1197. *
  1198. * @param mode
  1199. * the One of the modes listed above.
  1200. */
  1201. public void setItemCaptionMode(ItemCaptionMode mode) {
  1202. if (mode != null) {
  1203. itemCaptionMode = mode;
  1204. markAsDirty();
  1205. }
  1206. }
  1207. /**
  1208. * Gets the item caption mode.
  1209. *
  1210. * <p>
  1211. * The mode can be one of the following ones:
  1212. * <ul>
  1213. * <li><code>ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID</code> : Items
  1214. * Id-objects <code>toString</code> is used as item caption. If caption is
  1215. * explicitly specified, it overrides the id-caption.
  1216. * <li><code>ITEM_CAPTION_MODE_ID</code> : Items Id-objects
  1217. * <code>toString</code> is used as item caption.</li>
  1218. * <li><code>ITEM_CAPTION_MODE_ITEM</code> : Item-objects
  1219. * <code>toString</code> is used as item caption.</li>
  1220. * <li><code>ITEM_CAPTION_MODE_INDEX</code> : The index of the item is used
  1221. * as item caption. The index mode can only be used with the containers
  1222. * implementing <code>Container.Indexed</code> interface.</li>
  1223. * <li><code>ITEM_CAPTION_MODE_EXPLICIT</code> : The item captions must be
  1224. * explicitly specified.</li>
  1225. * <li><code>ITEM_CAPTION_MODE_PROPERTY</code> : The item captions are read
  1226. * from property, that must be specified with
  1227. * <code>setItemCaptionPropertyId</code>.</li>
  1228. * </ul>
  1229. * The <code>ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID</code> is the default
  1230. * mode.
  1231. * </p>
  1232. *
  1233. * @return the One of the modes listed above.
  1234. */
  1235. public ItemCaptionMode getItemCaptionMode() {
  1236. return itemCaptionMode;
  1237. }
  1238. /**
  1239. * Sets the item caption property.
  1240. *
  1241. * <p>
  1242. * Setting the id to a existing property implicitly sets the item caption
  1243. * mode to <code>ITEM_CAPTION_MODE_PROPERTY</code>. If the object is in
  1244. * <code>ITEM_CAPTION_MODE_PROPERTY</code> mode, setting caption property id
  1245. * null resets the item caption mode to
  1246. * <code>ITEM_CAPTION_EXPLICIT_DEFAULTS_ID</code>.
  1247. * </p>
  1248. * <p>
  1249. * Note that the type of the property used for caption must be String
  1250. * </p>
  1251. * <p>
  1252. * Setting the property id to null disables this feature. The id is null by
  1253. * default
  1254. * </p>
  1255. * .
  1256. *
  1257. * @param propertyId
  1258. * the id of the property.
  1259. *
  1260. */
  1261. public void setItemCaptionPropertyId(Object propertyId) {
  1262. if (propertyId != null) {
  1263. itemCaptionPropertyId = propertyId;
  1264. setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY);
  1265. markAsDirty();
  1266. } else {
  1267. itemCaptionPropertyId = null;
  1268. if (getItemCaptionMode() == ITEM_CAPTION_MODE_PROPERTY) {
  1269. setItemCaptionMode(ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID);
  1270. }
  1271. markAsDirty();
  1272. }
  1273. }
  1274. /**
  1275. * Gets the item caption property.
  1276. *
  1277. * @return the Id of the property used as item caption source.
  1278. */
  1279. public Object getItemCaptionPropertyId() {
  1280. return itemCaptionPropertyId;
  1281. }
  1282. /**
  1283. * Sets the item icon property.
  1284. *
  1285. * <p>
  1286. * If the property id is set to a valid value, each item is given an icon
  1287. * got from the given property of the items. The type of the property must
  1288. * be assignable to Resource.
  1289. * </p>
  1290. *
  1291. * <p>
  1292. * Note : The icons set with <code>setItemIcon</code> function override the
  1293. * icons from the property.
  1294. * </p>
  1295. *
  1296. * <p>
  1297. * Setting the property id to null disables this feature. The id is null by
  1298. * default
  1299. * </p>
  1300. * .
  1301. *
  1302. * @param propertyId
  1303. * the id of the property that specifies icons for items or null
  1304. * @throws IllegalArgumentException
  1305. * If the propertyId is not in the container or is not of a
  1306. * valid type
  1307. */
  1308. public void setItemIconPropertyId(Object propertyId)
  1309. throws IllegalArgumentException {
  1310. if (propertyId == null) {
  1311. itemIconPropertyId = null;
  1312. } else if (!getContainerPropertyIds().contains(propertyId)) {
  1313. throw new IllegalArgumentException(
  1314. "Property id not found in the container");
  1315. } else if (Resource.class.isAssignableFrom(getType(propertyId))) {
  1316. itemIconPropertyId = propertyId;
  1317. } else {
  1318. throw new IllegalArgumentException(
  1319. "Property type must be assignable to Resource");
  1320. }
  1321. markAsDirty();
  1322. }
  1323. /**
  1324. * Gets the item icon property.
  1325. *
  1326. * <p>
  1327. * If the property id is set to a valid value, each item is given an icon
  1328. * got from the given property of the items. The type of the property must
  1329. * be assignable to Icon.
  1330. * </p>
  1331. *
  1332. * <p>
  1333. * Note : The icons set with <code>setItemIcon</code> function override the
  1334. * icons from the property.
  1335. * </p>
  1336. *
  1337. * <p>
  1338. * Setting the property id to null disables this feature. The id is null by
  1339. * default
  1340. * </p>
  1341. * .
  1342. *
  1343. * @return the Id of the property containing the item icons.
  1344. */
  1345. public Object getItemIconPropertyId() {
  1346. return itemIconPropertyId;
  1347. }
  1348. /**
  1349. * Tests if an item is selected.
  1350. *
  1351. * <p>
  1352. * In single select mode testing selection status of the item identified by
  1353. * {@link #getNullSelectionItemId()} returns true if the value of the
  1354. * property is null.
  1355. * </p>
  1356. *
  1357. * @param itemId
  1358. * the Id the of the item to be tested.
  1359. * @see #getNullSelectionItemId()
  1360. * @see #setNullSelectionItemId(Object)
  1361. *
  1362. */
  1363. public boolean isSelected(Object itemId) {
  1364. if (itemId == null) {
  1365. return false;
  1366. }
  1367. if (isMultiSelect()) {
  1368. return ((Set<?>) getValue()).contains(itemId);
  1369. } else {
  1370. final Object value = getValue();
  1371. return itemId
  1372. .equals(value == null ? getNullSelectionItemId() : value);
  1373. }
  1374. }
  1375. /**
  1376. * Selects an item.
  1377. *
  1378. * <p>
  1379. * In single select mode selecting item identified by
  1380. * {@link #getNullSelectionItemId()} sets the value of the property to null.
  1381. * </p>
  1382. *
  1383. * @param itemId
  1384. * the identifier of Item to be selected.
  1385. * @see #getNullSelectionItemId()
  1386. * @see #setNullSelectionItemId(Object)
  1387. *
  1388. */
  1389. public void select(Object itemId) {
  1390. if (!isMultiSelect()) {
  1391. setValue(itemId);
  1392. } else if (!isSelected(itemId) && itemId != null
  1393. && items.containsId(itemId)) {
  1394. final Set<Object> s = new HashSet<Object>((Set<?>) getValue());
  1395. s.add(itemId);
  1396. setValue(s);
  1397. }
  1398. }
  1399. /**
  1400. * Unselects an item.
  1401. *
  1402. * @param itemId
  1403. * the identifier of the Item to be unselected.
  1404. * @see #getNullSelectionItemId()
  1405. * @see #setNullSelectionItemId(Object)
  1406. *
  1407. */
  1408. public void unselect(Object itemId) {
  1409. if (isSelected(itemId)) {
  1410. if (isMultiSelect()) {
  1411. final Set<Object> s = new HashSet<Object>((Set<?>) getValue());
  1412. s.remove(itemId);
  1413. setValue(s);
  1414. } else {
  1415. setValue(null);
  1416. }
  1417. }
  1418. }
  1419. /**
  1420. * Notifies this listener that the Containers contents has changed.
  1421. *
  1422. * @see Container.PropertySetChangeListener#containerPropertySetChange(Container.PropertySetChangeEvent)
  1423. */
  1424. @Override
  1425. public void containerPropertySetChange(
  1426. Container.PropertySetChangeEvent event) {
  1427. firePropertySetChange();
  1428. }
  1429. /**
  1430. * Adds a new Property set change listener for this Container.
  1431. *
  1432. * @see Container.PropertySetChangeNotifier#addListener(Container.PropertySetChangeListener)
  1433. */
  1434. @Override
  1435. public void addPropertySetChangeListener(
  1436. Container.PropertySetChangeListener listener) {
  1437. if (propertySetEventListeners == null) {
  1438. propertySetEventListeners = new LinkedHashSet<PropertySetChangeListener>();
  1439. }
  1440. propertySetEventListeners.add(listener);
  1441. }
  1442. /**
  1443. * @deprecated As of 7.0, replaced by
  1444. * {@link #addPropertySetChangeListener(Container.PropertySetChangeListener)}
  1445. **/
  1446. @Override
  1447. @Deprecated
  1448. public void addListener(Container.PropertySetChangeListener listener) {
  1449. addPropertySetChangeListener(listener);
  1450. }
  1451. /**
  1452. * Removes a previously registered Property set change listener.
  1453. *
  1454. * @see Container.PropertySetChangeNotifier#removeListener(Container.PropertySetChangeListener)
  1455. */
  1456. @Override
  1457. public void removePropertySetChangeListener(
  1458. Container.PropertySetChangeListener listener) {
  1459. if (propertySetEventListeners != null) {
  1460. propertySetEventListeners.remove(listener);
  1461. if (propertySetEventListeners.isEmpty()) {
  1462. propertySetEventListeners = null;
  1463. }
  1464. }
  1465. }
  1466. /**
  1467. * @deprecated As of 7.0, replaced by
  1468. * {@link #removePropertySetChangeListener(Container.PropertySetChangeListener)}
  1469. **/
  1470. @Override
  1471. @Deprecated
  1472. public void removeListener(Container.PropertySetChangeListener listener) {
  1473. removePropertySetChangeListener(listener);
  1474. }
  1475. /**
  1476. * Adds an Item set change listener for the object.
  1477. *
  1478. * @see Container.ItemSetChangeNotifier#addListener(Container.ItemSetChangeListener)
  1479. */
  1480. @Override
  1481. public void addItemSetChangeListener(
  1482. Container.ItemSetChangeListener listener) {
  1483. if (itemSetEventListeners == null) {
  1484. itemSetEventListeners = new LinkedHashSet<ItemSetChangeListener>();
  1485. }
  1486. itemSetEventListeners.add(listener);
  1487. }
  1488. /**
  1489. * @deprecated As of 7.0, replaced by
  1490. * {@link #addItemSetChangeListener(Container.ItemSetChangeListener)}
  1491. **/
  1492. @Override
  1493. @Deprecated
  1494. public void addListener(Container.ItemSetChangeListener listener) {
  1495. addItemSetChangeListener(listener);
  1496. }
  1497. /**
  1498. * Removes the Item set change listener from the object.
  1499. *
  1500. * @see Container.ItemSetChangeNotifier#removeListener(Container.ItemSetChangeListener)
  1501. */
  1502. @Override
  1503. public void removeItemSetChangeListener(
  1504. Container.ItemSetChangeListener listener) {
  1505. if (itemSetEventListeners != null) {
  1506. itemSetEventListeners.remove(listener);
  1507. if (itemSetEventListeners.isEmpty()) {
  1508. itemSetEventListeners = null;
  1509. }
  1510. }
  1511. }
  1512. /**
  1513. * @deprecated As of 7.0, replaced by
  1514. * {@link #removeItemSetChangeListener(Container.ItemSetChangeListener)}
  1515. **/
  1516. @Override
  1517. @Deprecated
  1518. public void removeListener(Container.ItemSetChangeListener listener) {
  1519. removeItemSetChangeListener(listener);
  1520. }
  1521. @Override
  1522. public Collection<?> getListeners(Class<?> eventType) {
  1523. if (Container.ItemSetChangeEvent.class.isAssignableFrom(eventType)) {
  1524. if (itemSetEventListeners == null) {
  1525. return Collections.EMPTY_LIST;
  1526. } else {
  1527. return Collections
  1528. .unmodifiableCollection(itemSetEventListeners);
  1529. }
  1530. } else if (Container.PropertySetChangeEvent.class
  1531. .isAssignableFrom(eventType)) {
  1532. if (propertySetEventListeners == null) {
  1533. return Collections.EMPTY_LIST;
  1534. } else {
  1535. return Collections
  1536. .unmodifiableCollection(propertySetEventListeners);
  1537. }
  1538. }
  1539. return super.getListeners(eventType);
  1540. }
  1541. /**
  1542. * Lets the listener know a Containers Item set has changed.
  1543. *
  1544. * @see Container.ItemSetChangeListener#containerItemSetChange(Container.ItemSetChangeEvent)
  1545. */
  1546. @Override
  1547. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  1548. // Clears the item id mapping table
  1549. itemIdMapper.removeAll();
  1550. // Notify all listeners
  1551. fireItemSetChange();
  1552. }
  1553. /**
  1554. * Fires the property set change event.
  1555. */
  1556. protected void firePropertySetChange() {
  1557. if (propertySetEventListeners != null
  1558. && !propertySetEventListeners.isEmpty()) {
  1559. final Container.PropertySetChangeEvent event = new PropertySetChangeEvent(
  1560. this);
  1561. final Object[] listeners = propertySetEventListeners.toArray();
  1562. for (int i = 0; i < listeners.length; i++) {
  1563. ((Container.PropertySetChangeListener) listeners[i])
  1564. .containerPropertySetChange(event);
  1565. }
  1566. }
  1567. markAsDirty();
  1568. }
  1569. /**
  1570. * Fires the item set change event.
  1571. */
  1572. protected void fireItemSetChange() {
  1573. if (itemSetEventListeners != null && !itemSetEventListeners.isEmpty()) {
  1574. final Container.ItemSetChangeEvent event = new ItemSetChangeEvent(
  1575. this);
  1576. final Object[] listeners = itemSetEventListeners.toArray();
  1577. for (int i = 0; i < listeners.length; i++) {
  1578. ((Container.ItemSetChangeListener) listeners[i])
  1579. .containerItemSetChange(event);
  1580. }
  1581. }
  1582. markAsDirty();
  1583. }
  1584. /**
  1585. * Implementation of item set change event.
  1586. */
  1587. private static class ItemSetChangeEvent extends EventObject
  1588. implements Serializable, Container.ItemSetChangeEvent {
  1589. private ItemSetChangeEvent(Container source) {
  1590. super(source);
  1591. }
  1592. /**
  1593. * Gets the Property where the event occurred.
  1594. *
  1595. * @see Container.ItemSetChangeEvent#getContainer()
  1596. */
  1597. @Override
  1598. public Container getContainer() {
  1599. return (Container) getSource();
  1600. }
  1601. }
  1602. /**
  1603. * Implementation of property set change event.
  1604. */
  1605. private static class PropertySetChangeEvent extends EventObject
  1606. implements Container.PropertySetChangeEvent, Serializable {
  1607. private PropertySetChangeEvent(Container source) {
  1608. super(source);
  1609. }
  1610. /**
  1611. * Retrieves the Container whose contents have been modified.
  1612. *
  1613. * @see Container.PropertySetChangeEvent#getContainer()
  1614. */
  1615. @Override
  1616. public Container getContainer() {
  1617. return (Container) getSource();
  1618. }
  1619. }
  1620. /**
  1621. * For multi-selectable fields, also an empty collection of values is
  1622. * considered to be an empty field.
  1623. *
  1624. * @see LegacyAbstractField#isEmpty().
  1625. */
  1626. @Override
  1627. public boolean isEmpty() {
  1628. if (!multiSelect) {
  1629. return super.isEmpty();
  1630. } else {
  1631. Object value = getValue();
  1632. return super.isEmpty() || (value instanceof Collection
  1633. && ((Collection<?>) value).isEmpty());
  1634. }
  1635. }
  1636. /**
  1637. * Allow or disallow empty selection by the user. If the select is in
  1638. * single-select mode, you can make an item represent the empty selection by
  1639. * calling <code>setNullSelectionItemId()</code>. This way you can for
  1640. * instance set an icon and caption for the null selection item.
  1641. *
  1642. * @param nullSelectionAllowed
  1643. * whether or not to allow empty selection
  1644. * @see #setNullSelectionItemId(Object)
  1645. * @see #isNullSelectionAllowed()
  1646. */
  1647. public void setNullSelectionAllowed(boolean nullSelectionAllowed) {
  1648. if (nullSelectionAllowed != this.nullSelectionAllowed) {
  1649. this.nullSelectionAllowed = nullSelectionAllowed;
  1650. markAsDirty();
  1651. }
  1652. }
  1653. /**
  1654. * Checks if null empty selection is allowed by the user.
  1655. *
  1656. * @return whether or not empty selection is allowed
  1657. * @see #setNullSelectionAllowed(boolean)
  1658. */
  1659. public boolean isNullSelectionAllowed() {
  1660. return nullSelectionAllowed;
  1661. }
  1662. /**
  1663. * Returns the item id that represents null value of this select in single
  1664. * select mode.
  1665. *
  1666. * <p>
  1667. * Data interface does not support nulls as item ids. Selecting the item
  1668. * identified by this id is the same as selecting no items at all. This
  1669. * setting only affects the single select mode.
  1670. * </p>
  1671. *
  1672. * @return the Object Null value item id.
  1673. * @see #setNullSelectionItemId(Object)
  1674. * @see #isSelected(Object)
  1675. * @see #select(Object)
  1676. */
  1677. public Object getNullSelectionItemId() {
  1678. return nullSelectionItemId;
  1679. }
  1680. /**
  1681. * Sets the item id that represents null value of this select.
  1682. *
  1683. * <p>
  1684. * Data interface does not support nulls as item ids. Selecting the item
  1685. * identified by this id is the same as selecting no items at all. This
  1686. * setting only affects the single select mode.
  1687. * </p>
  1688. *
  1689. * @param nullSelectionItemId
  1690. * the nullSelectionItemId to set.
  1691. * @see #getNullSelectionItemId()
  1692. * @see #isSelected(Object)
  1693. * @see #select(Object)
  1694. */
  1695. public void setNullSelectionItemId(Object nullSelectionItemId) {
  1696. if (nullSelectionItemId != null && isMultiSelect()) {
  1697. throw new IllegalStateException(
  1698. "Multiselect and NullSelectionItemId can not be set at the same time.");
  1699. }
  1700. this.nullSelectionItemId = nullSelectionItemId;
  1701. }
  1702. /**
  1703. * Notifies the component that it is connected to an application.
  1704. *
  1705. * @see AbstractField#attach()
  1706. */
  1707. @Override
  1708. public void attach() {
  1709. super.attach();
  1710. }
  1711. /**
  1712. * Detaches the component from application.
  1713. *
  1714. * @see vaadin.ui.AbstractComponent#detach()
  1715. */
  1716. @Override
  1717. public void detach() {
  1718. getCaptionChangeListener().clear();
  1719. super.detach();
  1720. }
  1721. // Caption change listener
  1722. protected CaptionChangeListener getCaptionChangeListener() {
  1723. if (captionChangeListener == null) {
  1724. captionChangeListener = new CaptionChangeListener();
  1725. }
  1726. return captionChangeListener;
  1727. }
  1728. /**
  1729. * This is a listener helper for Item and Property changes that should cause
  1730. * a repaint. It should be attached to all items that are displayed, and the
  1731. * default implementation does this in paintContent(). Especially
  1732. * "lazyloading" components should take care to add and remove listeners as
  1733. * appropriate. Call addNotifierForItem() for each painted item (and
  1734. * remember to clear).
  1735. *
  1736. * NOTE: singleton, use getCaptionChangeListener().
  1737. *
  1738. */
  1739. @Deprecated
  1740. protected class CaptionChangeListener implements
  1741. Item.PropertySetChangeListener, Property.ValueChangeListener {
  1742. // TODO clean this up - type is either Item.PropertySetChangeNotifier or
  1743. // Property.ValueChangeNotifier
  1744. HashSet<Object> captionChangeNotifiers = new HashSet<Object>();
  1745. public void addNotifierForItem(Object itemId) {
  1746. switch (getItemCaptionMode()) {
  1747. case ITEM:
  1748. final Item i = getItem(itemId);
  1749. if (i == null) {
  1750. return;
  1751. }
  1752. if (i instanceof Item.PropertySetChangeNotifier) {
  1753. ((Item.PropertySetChangeNotifier) i)
  1754. .addPropertySetChangeListener(
  1755. getCaptionChangeListener());
  1756. captionChangeNotifiers.add(i);
  1757. }
  1758. Collection<?> pids = i.getItemPropertyIds();
  1759. if (pids != null) {
  1760. for (Iterator<?> it = pids.iterator(); it.hasNext();) {
  1761. Property<?> p = i.getItemProperty(it.next());
  1762. if (p != null
  1763. && p instanceof Property.ValueChangeNotifier) {
  1764. ((Property.ValueChangeNotifier) p)
  1765. .addValueChangeListener(
  1766. getCaptionChangeListener());
  1767. captionChangeNotifiers.add(p);
  1768. }
  1769. }
  1770. }
  1771. break;
  1772. case PROPERTY:
  1773. final Property<?> p = getContainerProperty(itemId,
  1774. getItemCaptionPropertyId());
  1775. if (p != null && p instanceof Property.ValueChangeNotifier) {
  1776. ((Property.ValueChangeNotifier) p)
  1777. .addValueChangeListener(getCaptionChangeListener());
  1778. captionChangeNotifiers.add(p);
  1779. }
  1780. break;
  1781. }
  1782. if (getItemIconPropertyId() != null) {
  1783. final Property p = getContainerProperty(itemId,
  1784. getItemIconPropertyId());
  1785. if (p != null && p instanceof Property.ValueChangeNotifier) {
  1786. ((Property.ValueChangeNotifier) p)
  1787. .addValueChangeListener(getCaptionChangeListener());
  1788. captionChangeNotifiers.add(p);
  1789. }
  1790. }
  1791. }
  1792. public void clear() {
  1793. for (Iterator<Object> it = captionChangeNotifiers.iterator(); it
  1794. .hasNext();) {
  1795. Object notifier = it.next();
  1796. if (notifier instanceof Item.PropertySetChangeNotifier) {
  1797. ((Item.PropertySetChangeNotifier) notifier)
  1798. .removePropertySetChangeListener(
  1799. getCaptionChangeListener());
  1800. } else {
  1801. ((Property.ValueChangeNotifier) notifier)
  1802. .removeValueChangeListener(
  1803. getCaptionChangeListener());
  1804. }
  1805. }
  1806. captionChangeNotifiers.clear();
  1807. }
  1808. @Override
  1809. public void valueChange(Property.ValueChangeEvent event) {
  1810. markAsDirty();
  1811. }
  1812. @Override
  1813. public void itemPropertySetChange(Item.PropertySetChangeEvent event) {
  1814. markAsDirty();
  1815. }
  1816. }
  1817. /**
  1818. * Criterion which accepts a drop only if the drop target is (one of) the
  1819. * given Item identifier(s). Criterion can be used only on a drop targets
  1820. * that extends AbstractSelect like {@link Table} and {@link Tree}. The
  1821. * target and identifiers of valid Items are given in constructor.
  1822. *
  1823. * @since 6.3
  1824. */
  1825. @Deprecated
  1826. public static class TargetItemIs extends AbstractItemSetCriterion {
  1827. /**
  1828. * @param select
  1829. * the select implementation that is used as a drop target
  1830. * @param itemId
  1831. * the identifier(s) that are valid drop locations
  1832. */
  1833. public TargetItemIs(AbstractSelect select, Object... itemId) {
  1834. super(select, itemId);
  1835. }
  1836. @Override
  1837. public boolean accept(DragAndDropEvent dragEvent) {
  1838. AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent
  1839. .getTargetDetails();
  1840. if (dropTargetData.getTarget() != select) {
  1841. return false;
  1842. }
  1843. return itemIds.contains(dropTargetData.getItemIdOver());
  1844. }
  1845. }
  1846. /**
  1847. * Abstract helper class to implement item id based criterion.
  1848. *
  1849. * Note, inner class used not to open itemIdMapper for public access.
  1850. *
  1851. * @since 6.3
  1852. *
  1853. */
  1854. private abstract static class AbstractItemSetCriterion
  1855. extends ClientSideCriterion {
  1856. protected final Collection<Object> itemIds = new HashSet<Object>();
  1857. protected AbstractSelect select;
  1858. public AbstractItemSetCriterion(AbstractSelect select,
  1859. Object... itemId) {
  1860. if (itemIds == null || select == null) {
  1861. throw new IllegalArgumentException(
  1862. "Accepted item identifiers must be accepted.");
  1863. }
  1864. Collections.addAll(itemIds, itemId);
  1865. this.select = select;
  1866. }
  1867. @Override
  1868. public void paintContent(PaintTarget target) throws PaintException {
  1869. super.paintContent(target);
  1870. String[] keys = new String[itemIds.size()];
  1871. int i = 0;
  1872. for (Object itemId : itemIds) {
  1873. String key = select.itemIdMapper.key(itemId);
  1874. keys[i++] = key;
  1875. }
  1876. target.addAttribute("keys", keys);
  1877. target.addAttribute("s", select);
  1878. }
  1879. }
  1880. /**
  1881. * This criterion accepts a only a {@link Transferable} that contains given
  1882. * Item (practically its identifier) from a specific AbstractSelect.
  1883. *
  1884. * @since 6.3
  1885. */
  1886. @Deprecated
  1887. public static class AcceptItem extends AbstractItemSetCriterion {
  1888. /**
  1889. * @param select
  1890. * the select from which the item id's are checked
  1891. * @param itemId
  1892. * the item identifier(s) of the select that are accepted
  1893. */
  1894. public AcceptItem(AbstractSelect select, Object... itemId) {
  1895. super(select, itemId);
  1896. }
  1897. @Override
  1898. public boolean accept(DragAndDropEvent dragEvent) {
  1899. DataBoundTransferable transferable = (DataBoundTransferable) dragEvent
  1900. .getTransferable();
  1901. if (transferable.getSourceComponent() != select) {
  1902. return false;
  1903. }
  1904. return itemIds.contains(transferable.getItemId());
  1905. }
  1906. /**
  1907. * A simple accept criterion which ensures that {@link Transferable}
  1908. * contains an {@link Item} (or actually its identifier). In other words
  1909. * the criterion check that drag is coming from a {@link Container} like
  1910. * {@link Tree} or {@link Table}.
  1911. */
  1912. public static final ClientSideCriterion ALL = new ContainsDataFlavor(
  1913. "itemId");
  1914. }
  1915. /**
  1916. * TargetDetails implementation for subclasses of {@link AbstractSelect}
  1917. * that implement {@link DropTarget}.
  1918. *
  1919. * @since 6.3
  1920. */
  1921. @Deprecated
  1922. public class AbstractSelectTargetDetails extends TargetDetailsImpl {
  1923. /**
  1924. * The item id over which the drag event happened.
  1925. */
  1926. protected Object idOver;
  1927. /**
  1928. * Constructor that automatically converts itemIdOver key to
  1929. * corresponding item Id
  1930. *
  1931. */
  1932. protected AbstractSelectTargetDetails(
  1933. Map<String, Object> rawVariables) {
  1934. super(rawVariables, (DropTarget) AbstractSelect.this);
  1935. // eagar fetch itemid, mapper may be emptied
  1936. String keyover = (String) getData("itemIdOver");
  1937. if (keyover != null) {
  1938. idOver = itemIdMapper.get(keyover);
  1939. }
  1940. }
  1941. /**
  1942. * If the drag operation is currently over an {@link Item}, this method
  1943. * returns the identifier of that {@link Item}.
  1944. *
  1945. */
  1946. public Object getItemIdOver() {
  1947. return idOver;
  1948. }
  1949. /**
  1950. * Returns a detailed vertical location where the drop happened on Item.
  1951. */
  1952. public VerticalDropLocation getDropLocation() {
  1953. String detail = (String) getData("detail");
  1954. if (detail == null) {
  1955. return null;
  1956. }
  1957. return VerticalDropLocation.valueOf(detail);
  1958. }
  1959. }
  1960. /**
  1961. * An accept criterion to accept drops only on a specific vertical location
  1962. * of an item.
  1963. * <p>
  1964. * This accept criterion is currently usable in Tree and Table
  1965. * implementations.
  1966. */
  1967. @Deprecated
  1968. public static class VerticalLocationIs extends TargetDetailIs {
  1969. public static VerticalLocationIs TOP = new VerticalLocationIs(
  1970. VerticalDropLocation.TOP);
  1971. public static VerticalLocationIs BOTTOM = new VerticalLocationIs(
  1972. VerticalDropLocation.BOTTOM);
  1973. public static VerticalLocationIs MIDDLE = new VerticalLocationIs(
  1974. VerticalDropLocation.MIDDLE);
  1975. private VerticalLocationIs(VerticalDropLocation l) {
  1976. super("detail", l.name());
  1977. }
  1978. }
  1979. /**
  1980. * Implement this interface and pass it to Tree.setItemDescriptionGenerator
  1981. * or Table.setItemDescriptionGenerator to generate mouse over descriptions
  1982. * ("tooltips") for the rows and cells in Table or for the items in Tree.
  1983. */
  1984. @Deprecated
  1985. public interface ItemDescriptionGenerator extends Serializable {
  1986. /**
  1987. * Called by Table when a cell (and row) is painted or a item is painted
  1988. * in Tree
  1989. *
  1990. * @param source
  1991. * The source of the generator, the Tree or Table the
  1992. * generator is attached to
  1993. * @param itemId
  1994. * The itemId of the painted cell
  1995. * @param propertyId
  1996. * The propertyId of the cell, null when getting row
  1997. * description
  1998. * @return The description or "tooltip" of the item.
  1999. */
  2000. public String generateDescription(Component source, Object itemId,
  2001. Object propertyId);
  2002. }
  2003. @Override
  2004. public void readDesign(Element design, DesignContext context) {
  2005. // handle default attributes
  2006. super.readDesign(design, context);
  2007. // handle children specifying selectable items (<option>)
  2008. readItems(design, context);
  2009. }
  2010. protected void readItems(Element design, DesignContext context) {
  2011. Set<String> selected = new HashSet<String>();
  2012. for (Element child : design.children()) {
  2013. readItem(child, selected, context);
  2014. }
  2015. if (!selected.isEmpty()) {
  2016. if (isMultiSelect()) {
  2017. setValue(selected, false, true);
  2018. } else if (selected.size() == 1) {
  2019. setValue(selected.iterator().next(), false, true);
  2020. } else {
  2021. throw new DesignException(
  2022. "Multiple values selected for a single select component");
  2023. }
  2024. }
  2025. }
  2026. /**
  2027. * Reads an Item from a design and inserts it into the data source.
  2028. * Hierarchical select components should override this method to recursively
  2029. * recursively read any child items as well.
  2030. *
  2031. * @since 7.5.0
  2032. * @param child
  2033. * a child element representing the item
  2034. * @param selected
  2035. * A set accumulating selected items. If the item that is read is
  2036. * marked as selected, its item id should be added to this set.
  2037. * @param context
  2038. * the DesignContext instance used in parsing
  2039. * @return the item id of the new item
  2040. *
  2041. * @throws DesignException
  2042. * if the tag name of the {@code child} element is not
  2043. * {@code option}.
  2044. */
  2045. protected Object readItem(Element child, Set<String> selected,
  2046. DesignContext context) {
  2047. if (!"option".equals(child.tagName())) {
  2048. throw new DesignException("Unrecognized child element in "
  2049. + getClass().getSimpleName() + ": " + child.tagName());
  2050. }
  2051. String itemId;
  2052. String caption = DesignFormatter.decodeFromTextNode(child.html());
  2053. if (child.hasAttr("item-id")) {
  2054. itemId = child.attr("item-id");
  2055. addItem(itemId);
  2056. setItemCaption(itemId, caption);
  2057. } else {
  2058. addItem(itemId = caption);
  2059. }
  2060. if (child.hasAttr("icon")) {
  2061. setItemIcon(itemId, DesignAttributeHandler.readAttribute("icon",
  2062. child.attributes(), Resource.class));
  2063. }
  2064. if (child.hasAttr("selected")) {
  2065. selected.add(itemId);
  2066. }
  2067. return itemId;
  2068. }
  2069. @Override
  2070. public void writeDesign(Element design, DesignContext context) {
  2071. // Write default attributes
  2072. super.writeDesign(design, context);
  2073. // Write options if warranted
  2074. if (context.shouldWriteData(this)) {
  2075. writeItems(design, context);
  2076. }
  2077. }
  2078. /**
  2079. * Writes the data source items to a design. Hierarchical select components
  2080. * should override this method to only write the root items.
  2081. *
  2082. * @since 7.5.0
  2083. * @param design
  2084. * the element into which to insert the items
  2085. * @param context
  2086. * the DesignContext instance used in writing
  2087. */
  2088. protected void writeItems(Element design, DesignContext context) {
  2089. for (Object itemId : getItemIds()) {
  2090. writeItem(design, itemId, context);
  2091. }
  2092. }
  2093. /**
  2094. * Writes a data source Item to a design. Hierarchical select components
  2095. * should override this method to recursively write any child items as well.
  2096. *
  2097. * @since 7.5.0
  2098. * @param design
  2099. * the element into which to insert the item
  2100. * @param itemId
  2101. * the id of the item to write
  2102. * @param context
  2103. * the DesignContext instance used in writing
  2104. * @return
  2105. */
  2106. protected Element writeItem(Element design, Object itemId,
  2107. DesignContext context) {
  2108. Element element = design.appendElement("option");
  2109. String caption = getItemCaption(itemId);
  2110. if (caption != null && !caption.equals(itemId.toString())) {
  2111. element.html(DesignFormatter.encodeForTextNode(caption));
  2112. element.attr("item-id", itemId.toString());
  2113. } else {
  2114. element.html(DesignFormatter.encodeForTextNode(itemId.toString()));
  2115. }
  2116. Resource icon = getItemIcon(itemId);
  2117. if (icon != null) {
  2118. DesignAttributeHandler.writeAttribute("icon", element.attributes(),
  2119. icon, null, Resource.class, context);
  2120. }
  2121. if (isSelected(itemId)) {
  2122. element.attr("selected", "");
  2123. }
  2124. return element;
  2125. }
  2126. @Override
  2127. protected AbstractSelectState getState() {
  2128. return (AbstractSelectState) super.getState();
  2129. }
  2130. @Override
  2131. protected AbstractSelectState getState(boolean markAsDirty) {
  2132. return (AbstractSelectState) super.getState(markAsDirty);
  2133. }
  2134. }