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 64KB

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