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.

Tree.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.util.Collection;
  18. import java.util.HashMap;
  19. import java.util.HashSet;
  20. import java.util.Map;
  21. import java.util.Objects;
  22. import java.util.Set;
  23. import com.vaadin.data.Binder;
  24. import com.vaadin.data.HasDataProvider;
  25. import com.vaadin.data.SelectionModel;
  26. import com.vaadin.data.provider.DataGenerator;
  27. import com.vaadin.data.provider.DataProvider;
  28. import com.vaadin.data.provider.HierarchicalDataProvider;
  29. import com.vaadin.event.CollapseEvent;
  30. import com.vaadin.event.CollapseEvent.CollapseListener;
  31. import com.vaadin.event.ExpandEvent;
  32. import com.vaadin.event.ExpandEvent.ExpandListener;
  33. import com.vaadin.event.selection.SelectionListener;
  34. import com.vaadin.server.Resource;
  35. import com.vaadin.shared.Registration;
  36. import com.vaadin.shared.ui.grid.HeightMode;
  37. import com.vaadin.shared.ui.tree.TreeRendererState;
  38. import com.vaadin.ui.Grid.SelectionMode;
  39. import com.vaadin.ui.renderers.AbstractRenderer;
  40. import elemental.json.JsonObject;
  41. /**
  42. * Tree component. A Tree can be used to select an item from a hierarchical set
  43. * of items.
  44. *
  45. * @author Vaadin Ltd
  46. * @since 8.1
  47. *
  48. * @param <T>
  49. * the data type
  50. */
  51. public class Tree<T> extends Composite implements HasDataProvider<T> {
  52. /**
  53. * String renderer that handles icon resources and stores their identifiers
  54. * into data objects.
  55. *
  56. * @since 8.1
  57. */
  58. public final class TreeRenderer extends AbstractRenderer<T, String>
  59. implements DataGenerator<T> {
  60. /**
  61. * Constructs a new TreeRenderer.
  62. */
  63. protected TreeRenderer() {
  64. super(String.class);
  65. }
  66. private Map<T, String> resourceKeyMap = new HashMap<>();
  67. private int counter = 0;
  68. @Override
  69. public void generateData(T item, JsonObject jsonObject) {
  70. Resource resource = iconProvider.apply(item);
  71. if (resource == null) {
  72. destroyData(item);
  73. return;
  74. }
  75. if (!resourceKeyMap.containsKey(item)) {
  76. resourceKeyMap.put(item, "icon" + (counter++));
  77. }
  78. setResource(resourceKeyMap.get(item), resource);
  79. jsonObject.put("itemIcon", resourceKeyMap.get(item));
  80. }
  81. @Override
  82. public void destroyData(T item) {
  83. if (resourceKeyMap.containsKey(item)) {
  84. setResource(resourceKeyMap.get(item), null);
  85. resourceKeyMap.remove(item);
  86. }
  87. }
  88. @Override
  89. public void destroyAllData() {
  90. Set<T> keys = new HashSet<>(resourceKeyMap.keySet());
  91. for (T key : keys) {
  92. destroyData(key);
  93. }
  94. }
  95. @Override
  96. protected TreeRendererState getState() {
  97. return (TreeRendererState) super.getState();
  98. }
  99. @Override
  100. protected TreeRendererState getState(boolean markAsDirty) {
  101. return (TreeRendererState) super.getState(markAsDirty);
  102. }
  103. }
  104. private TreeGrid<T> treeGrid = new TreeGrid<>();
  105. private ItemCaptionGenerator<T> captionGenerator = String::valueOf;
  106. private IconGenerator<T> iconProvider = t -> null;
  107. /**
  108. * Constructs a new Tree Component.
  109. */
  110. public Tree() {
  111. setCompositionRoot(treeGrid);
  112. TreeRenderer renderer = new TreeRenderer();
  113. treeGrid.getDataCommunicator().addDataGenerator(renderer);
  114. treeGrid.addColumn(i -> captionGenerator.apply(i), renderer)
  115. .setId("column");
  116. treeGrid.setHierarchyColumn("column");
  117. while (treeGrid.getHeaderRowCount() > 0) {
  118. treeGrid.removeHeaderRow(0);
  119. }
  120. treeGrid.setPrimaryStyleName("v-newtree");
  121. setWidth("100%");
  122. setHeightUndefined();
  123. treeGrid.setHeightMode(HeightMode.UNDEFINED);
  124. treeGrid.addExpandListener(e -> fireExpandEvent(e.getExpandedItem(),
  125. e.isUserOriginated()));
  126. treeGrid.addCollapseListener(e -> fireCollapseEvent(
  127. e.getCollapsedItem(), e.isUserOriginated()));
  128. }
  129. /**
  130. * Constructs a new Tree Component.
  131. *
  132. * @param caption
  133. * the caption for component
  134. */
  135. public Tree(String caption) {
  136. this();
  137. setCaption(caption);
  138. }
  139. @Override
  140. public HierarchicalDataProvider<T, ?> getDataProvider() {
  141. return treeGrid.getDataProvider();
  142. }
  143. @Override
  144. public void setDataProvider(DataProvider<T, ?> dataProvider) {
  145. treeGrid.setDataProvider(dataProvider);
  146. }
  147. /**
  148. * Adds an ExpandListener to this Tree.
  149. *
  150. * @see ExpandEvent
  151. *
  152. * @param listener
  153. * the listener to add
  154. * @return a registration for the listener
  155. */
  156. public Registration addExpandListener(ExpandListener<T> listener) {
  157. return addListener(ExpandEvent.class, listener,
  158. ExpandListener.EXPAND_METHOD);
  159. }
  160. /**
  161. * Adds a CollapseListener to this Tree.
  162. *
  163. * @see CollapseEvent
  164. *
  165. * @param listener
  166. * the listener to add
  167. * @return a registration for the listener
  168. */
  169. public Registration addCollapseListener(CollapseListener<T> listener) {
  170. return addListener(CollapseEvent.class, listener,
  171. CollapseListener.COLLAPSE_METHOD);
  172. }
  173. /**
  174. * Fires an expand event with given item.
  175. *
  176. * @param item
  177. * the expanded item
  178. * @param userOriginated
  179. * whether the expand was triggered by a user interaction or the
  180. * server
  181. */
  182. protected void fireExpandEvent(T item, boolean userOriginated) {
  183. fireEvent(new ExpandEvent<>(this, item, userOriginated));
  184. }
  185. /**
  186. * Fires a collapse event with given item.
  187. *
  188. * @param item
  189. * the collapsed item
  190. * @param userOriginated
  191. * whether the collapse was triggered by a user interaction or
  192. * the server
  193. */
  194. protected void fireCollapseEvent(T item, boolean userOriginated) {
  195. fireEvent(new CollapseEvent<>(this, item, userOriginated));
  196. }
  197. /**
  198. * Expands the given item.
  199. * <p>
  200. * If the item is currently expanded, does nothing. If the item does not
  201. * have any children, does nothing.
  202. *
  203. * @param item
  204. * the item to expand
  205. */
  206. public void expand(T item) {
  207. treeGrid.expand(item);
  208. }
  209. /**
  210. * Collapses the given item.
  211. * <p>
  212. * If the item is already collapsed, does nothing.
  213. *
  214. * @param item
  215. * the item to collapse
  216. */
  217. public void collapse(T item) {
  218. treeGrid.collapse(item);
  219. }
  220. /**
  221. * This method is a shorthand that delegates to the currently set selection
  222. * model.
  223. *
  224. * @see #getSelectionModel()
  225. *
  226. * @return set of selected items
  227. */
  228. public Set<T> getSelectedItems() {
  229. return treeGrid.getSelectedItems();
  230. }
  231. /**
  232. * This method is a shorthand that delegates to the currently set selection
  233. * model.
  234. *
  235. * @param item
  236. * item to select
  237. *
  238. * @see SelectionModel#select(Object)
  239. * @see #getSelectionModel()
  240. */
  241. public void select(T item) {
  242. treeGrid.select(item);
  243. }
  244. /**
  245. * This method is a shorthand that delegates to the currently set selection
  246. * model.
  247. *
  248. * @param item
  249. * item to deselect
  250. *
  251. * @see SelectionModel#deselect(Object)
  252. * @see #getSelectionModel()
  253. */
  254. public void deselect(T item) {
  255. treeGrid.deselect(item);
  256. }
  257. /**
  258. * Adds a selection listener to the current selection model.
  259. * <p>
  260. * <strong>NOTE:</strong> If selection mode is switched with
  261. * {@link setSelectionMode(SelectionMode)}, then this listener is not
  262. * triggered anymore when selection changes!
  263. *
  264. * @param listener
  265. * the listener to add
  266. * @return a registration handle to remove the listener
  267. *
  268. * @throws UnsupportedOperationException
  269. * if selection has been disabled with
  270. * {@link SelectionMode.NONE}
  271. */
  272. public Registration addSelectionListener(SelectionListener<T> listener) {
  273. return treeGrid.addSelectionListener(listener);
  274. }
  275. /**
  276. * Use this tree as a single select in {@link Binder}. Throws
  277. * {@link IllegalStateException} if the tree is not using
  278. * {@link SelectionMode#SINGLE}.
  279. *
  280. * @return the single select wrapper that can be used in binder
  281. */
  282. public SingleSelect<T> asSingleSelect() {
  283. return treeGrid.asSingleSelect();
  284. }
  285. /**
  286. * Returns the selection model for this Tree.
  287. *
  288. * @return the selection model, not <code>null</code>
  289. */
  290. public SelectionModel<T> getSelectionModel() {
  291. return treeGrid.getSelectionModel();
  292. }
  293. @Override
  294. public void setItems(Collection<T> items) {
  295. treeGrid.setItems(items);
  296. }
  297. /**
  298. * Sets the item caption generator that is used to produce the strings shown
  299. * as the text for each item. By default, {@link String#valueOf(Object)} is
  300. * used.
  301. *
  302. * @param captionGenerator
  303. * the item caption provider to use, not <code>null</code>
  304. */
  305. public void setItemCaptionGenerator(
  306. ItemCaptionGenerator<T> captionGenerator) {
  307. Objects.requireNonNull(captionGenerator,
  308. "Caption generator must not be null");
  309. this.captionGenerator = captionGenerator;
  310. treeGrid.getDataCommunicator().reset();
  311. }
  312. /**
  313. * Sets the item icon generator that is used to produce custom icons for
  314. * items. The generator can return <code>null</code> for items with no icon.
  315. *
  316. * @see IconGenerator
  317. *
  318. * @param iconGenerator
  319. * the item icon generator to set, not <code>null</code>
  320. * @throws NullPointerException
  321. * if {@code itemIconGenerator} is {@code null}
  322. */
  323. public void setItemIconGenerator(IconGenerator<T> iconGenerator) {
  324. Objects.requireNonNull(iconGenerator,
  325. "Item icon generator must not be null");
  326. this.iconProvider = iconGenerator;
  327. treeGrid.getDataCommunicator().reset();
  328. }
  329. }