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

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