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

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