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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.v7.ui;
  17. import java.io.Serializable;
  18. import java.lang.reflect.Method;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  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 java.util.Stack;
  29. import java.util.StringTokenizer;
  30. import org.jsoup.nodes.Element;
  31. import com.vaadin.event.Action;
  32. import com.vaadin.event.Action.Handler;
  33. import com.vaadin.event.ContextClickEvent;
  34. import com.vaadin.event.Transferable;
  35. import com.vaadin.event.dd.DragAndDropEvent;
  36. import com.vaadin.event.dd.DragSource;
  37. import com.vaadin.event.dd.DropHandler;
  38. import com.vaadin.event.dd.DropTarget;
  39. import com.vaadin.event.dd.TargetDetails;
  40. import com.vaadin.event.dd.acceptcriteria.ClientSideCriterion;
  41. import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
  42. import com.vaadin.event.dd.acceptcriteria.TargetDetailIs;
  43. import com.vaadin.server.KeyMapper;
  44. import com.vaadin.server.PaintException;
  45. import com.vaadin.server.PaintTarget;
  46. import com.vaadin.server.Resource;
  47. import com.vaadin.shared.MouseEventDetails;
  48. import com.vaadin.shared.ui.MultiSelectMode;
  49. import com.vaadin.shared.ui.dd.VerticalDropLocation;
  50. import com.vaadin.ui.AbstractComponent;
  51. import com.vaadin.ui.Component;
  52. import com.vaadin.ui.declarative.DesignAttributeHandler;
  53. import com.vaadin.ui.declarative.DesignContext;
  54. import com.vaadin.ui.declarative.DesignException;
  55. import com.vaadin.util.ReflectTools;
  56. import com.vaadin.v7.data.Container;
  57. import com.vaadin.v7.data.Item;
  58. import com.vaadin.v7.data.util.ContainerHierarchicalWrapper;
  59. import com.vaadin.v7.data.util.HierarchicalContainer;
  60. import com.vaadin.v7.event.DataBoundTransferable;
  61. import com.vaadin.v7.event.ItemClickEvent;
  62. import com.vaadin.v7.event.ItemClickEvent.ItemClickListener;
  63. import com.vaadin.v7.event.ItemClickEvent.ItemClickNotifier;
  64. import com.vaadin.v7.shared.ui.tree.TreeConstants;
  65. import com.vaadin.v7.shared.ui.tree.TreeServerRpc;
  66. import com.vaadin.v7.shared.ui.tree.TreeState;
  67. /**
  68. * Tree component. A Tree can be used to select an item (or multiple items) from
  69. * a hierarchical set of items.
  70. *
  71. * @author Vaadin Ltd.
  72. * @since 3.0
  73. *
  74. * @deprecated See {@code com.vaadin.ui.Tree}.
  75. */
  76. @SuppressWarnings({ "serial", "deprecation" })
  77. @Deprecated
  78. public class Tree extends AbstractSelect implements Container.Hierarchical,
  79. Action.Container, ItemClickNotifier, DragSource, DropTarget {
  80. /**
  81. * ContextClickEvent for the Tree Component.
  82. *
  83. * @since 7.6
  84. */
  85. @Deprecated
  86. public static class TreeContextClickEvent extends ContextClickEvent {
  87. private final Object itemId;
  88. public TreeContextClickEvent(Tree source, Object itemId,
  89. MouseEventDetails mouseEventDetails) {
  90. super(source, mouseEventDetails);
  91. this.itemId = itemId;
  92. }
  93. @Override
  94. public Tree getComponent() {
  95. return (Tree) super.getComponent();
  96. }
  97. /**
  98. * Returns the item id of context clicked row.
  99. *
  100. * @return item id of clicked row; <code>null</code> if no row is
  101. * present at the location
  102. */
  103. public Object getItemId() {
  104. return itemId;
  105. }
  106. }
  107. /* Private members */
  108. private static final String NULL_ALT_EXCEPTION_MESSAGE = "Parameter 'altText' needs to be non null";
  109. /**
  110. * Item icons alt texts.
  111. */
  112. private final HashMap<Object, String> itemIconAlts = new HashMap<Object, String>();
  113. /**
  114. * Set of expanded nodes.
  115. */
  116. private HashSet<Object> expanded = new HashSet<Object>();
  117. /**
  118. * List of action handlers.
  119. */
  120. private LinkedList<Action.Handler> actionHandlers = null;
  121. /**
  122. * Action mapper.
  123. */
  124. private KeyMapper<Action> actionMapper = null;
  125. /**
  126. * Is the tree selectable on the client side.
  127. */
  128. private boolean selectable = true;
  129. /**
  130. * Flag to indicate sub-tree loading
  131. */
  132. private boolean partialUpdate = false;
  133. /**
  134. * Holds a itemId which was recently expanded
  135. */
  136. private Object expandedItemId;
  137. /**
  138. * a flag which indicates initial paint. After this flag set true partial
  139. * updates are allowed.
  140. */
  141. private boolean initialPaint = true;
  142. /**
  143. * Item tooltip generator
  144. */
  145. private ItemDescriptionGenerator itemDescriptionGenerator;
  146. /**
  147. * Supported drag modes for Tree.
  148. */
  149. @Deprecated
  150. public enum TreeDragMode {
  151. /**
  152. * When drag mode is NONE, dragging from Tree is not supported. Browsers
  153. * may still support selecting text/icons from Tree which can initiate
  154. * HTML 5 style drag and drop operation.
  155. */
  156. NONE,
  157. /**
  158. * When drag mode is NODE, users can initiate drag from Tree nodes that
  159. * represent {@link Item}s in from the backed {@link Container}.
  160. */
  161. NODE
  162. // , SUBTREE
  163. }
  164. private TreeDragMode dragMode = TreeDragMode.NONE;
  165. private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT;
  166. /* Tree constructors */
  167. /**
  168. * Creates a new empty tree.
  169. */
  170. public Tree() {
  171. this(null);
  172. registerRpc(new TreeServerRpc() {
  173. @Override
  174. public void contextClick(String rowKey, MouseEventDetails details) {
  175. fireEvent(new TreeContextClickEvent(Tree.this,
  176. itemIdMapper.get(rowKey), details));
  177. }
  178. });
  179. }
  180. /**
  181. * Creates a new empty tree with caption.
  182. *
  183. * @param caption
  184. */
  185. public Tree(String caption) {
  186. this(caption, new HierarchicalContainer());
  187. }
  188. /**
  189. * Creates a new tree with caption and connect it to a Container.
  190. *
  191. * @param caption
  192. * @param dataSource
  193. */
  194. public Tree(String caption, Container dataSource) {
  195. super(caption, dataSource);
  196. }
  197. @Override
  198. public void setItemIcon(Object itemId, Resource icon) {
  199. setItemIcon(itemId, icon, "");
  200. }
  201. /**
  202. * Sets the icon for an item.
  203. *
  204. * @param itemId
  205. * the id of the item to be assigned an icon.
  206. * @param icon
  207. * the icon to use or null.
  208. *
  209. * @param altText
  210. * the alternative text for the icon
  211. */
  212. public void setItemIcon(Object itemId, Resource icon, String altText) {
  213. if (itemId != null) {
  214. super.setItemIcon(itemId, icon);
  215. if (icon == null) {
  216. itemIconAlts.remove(itemId);
  217. } else if (altText == null) {
  218. throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE);
  219. } else {
  220. itemIconAlts.put(itemId, altText);
  221. }
  222. markAsDirty();
  223. }
  224. }
  225. /**
  226. * Set the alternate text for an item.
  227. *
  228. * Used when the item has an icon.
  229. *
  230. * @param itemId
  231. * the id of the item to be assigned an icon.
  232. * @param altText
  233. * the alternative text for the icon
  234. */
  235. public void setItemIconAlternateText(Object itemId, String altText) {
  236. if (itemId != null) {
  237. if (altText == null) {
  238. throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE);
  239. } else {
  240. itemIconAlts.put(itemId, altText);
  241. }
  242. }
  243. }
  244. /**
  245. * Return the alternate text of an icon in a tree item.
  246. *
  247. * @param itemId
  248. * Object with the ID of the item
  249. * @return String with the alternate text of the icon, or null when no icon
  250. * was set
  251. */
  252. public String getItemIconAlternateText(Object itemId) {
  253. String storedAlt = itemIconAlts.get(itemId);
  254. return storedAlt == null ? "" : storedAlt;
  255. }
  256. /* Expanding and collapsing */
  257. /**
  258. * Check is an item is expanded
  259. *
  260. * @param itemId
  261. * the item id.
  262. * @return true iff the item is expanded.
  263. */
  264. public boolean isExpanded(Object itemId) {
  265. return expanded.contains(itemId);
  266. }
  267. /**
  268. * Expands an item.
  269. *
  270. * @param itemId
  271. * the item id.
  272. * @return True iff the expand operation succeeded
  273. */
  274. public boolean expandItem(Object itemId) {
  275. boolean success = expandItem(itemId, true);
  276. markAsDirty();
  277. return success;
  278. }
  279. /**
  280. * Expands an item.
  281. *
  282. * @param itemId
  283. * the item id.
  284. * @param sendChildTree
  285. * flag to indicate if client needs subtree or not (may be
  286. * cached)
  287. * @return True if the expand operation succeeded
  288. */
  289. private boolean expandItem(Object itemId, boolean sendChildTree) {
  290. // Succeeds if the node is already expanded
  291. if (isExpanded(itemId)) {
  292. return true;
  293. }
  294. // Nodes that can not have children are not expandable
  295. if (!areChildrenAllowed(itemId)) {
  296. return false;
  297. }
  298. // Expands
  299. expanded.add(itemId);
  300. expandedItemId = itemId;
  301. if (initialPaint) {
  302. markAsDirty();
  303. } else if (sendChildTree) {
  304. requestPartialRepaint();
  305. }
  306. fireExpandEvent(itemId);
  307. return true;
  308. }
  309. @Override
  310. public void markAsDirty() {
  311. super.markAsDirty();
  312. partialUpdate = false;
  313. }
  314. private void requestPartialRepaint() {
  315. super.markAsDirty();
  316. partialUpdate = true;
  317. }
  318. /**
  319. * Expands the items recursively
  320. *
  321. * Expands all the children recursively starting from an item. Operation
  322. * succeeds only if all expandable items are expanded.
  323. *
  324. * @param startItemId
  325. * @return True iff the expand operation succeeded
  326. */
  327. public boolean expandItemsRecursively(Object startItemId) {
  328. boolean result = true;
  329. // Initial stack
  330. final Stack<Object> todo = new Stack<Object>();
  331. todo.add(startItemId);
  332. // Expands recursively
  333. while (!todo.isEmpty()) {
  334. final Object id = todo.pop();
  335. if (areChildrenAllowed(id) && !expandItem(id, false)) {
  336. result = false;
  337. }
  338. if (hasChildren(id)) {
  339. todo.addAll(getChildren(id));
  340. }
  341. }
  342. markAsDirty();
  343. return result;
  344. }
  345. /**
  346. * Collapses an item.
  347. *
  348. * @param itemId
  349. * the item id.
  350. * @return True iff the collapse operation succeeded
  351. */
  352. public boolean collapseItem(Object itemId) {
  353. // Succeeds if the node is already collapsed
  354. if (!isExpanded(itemId)) {
  355. return true;
  356. }
  357. // Collapse
  358. expanded.remove(itemId);
  359. markAsDirty();
  360. fireCollapseEvent(itemId);
  361. return true;
  362. }
  363. /**
  364. * Collapses the items recursively.
  365. *
  366. * Collapse all the children recursively starting from an item. Operation
  367. * succeeds only if all expandable items are collapsed.
  368. *
  369. * @param startItemId
  370. * @return True iff the collapse operation succeeded
  371. */
  372. public boolean collapseItemsRecursively(Object startItemId) {
  373. boolean result = true;
  374. // Initial stack
  375. final Stack<Object> todo = new Stack<Object>();
  376. todo.add(startItemId);
  377. // Collapse recursively
  378. while (!todo.isEmpty()) {
  379. final Object id = todo.pop();
  380. if (areChildrenAllowed(id) && !collapseItem(id)) {
  381. result = false;
  382. }
  383. if (hasChildren(id)) {
  384. todo.addAll(getChildren(id));
  385. }
  386. }
  387. return result;
  388. }
  389. /**
  390. * Returns the current selectable state. Selectable determines if the a node
  391. * can be selected on the client side. Selectable does not affect
  392. * {@link #setValue(Object)} or {@link #select(Object)}.
  393. *
  394. * <p>
  395. * The tree is selectable by default.
  396. * </p>
  397. *
  398. * @return the current selectable state.
  399. */
  400. public boolean isSelectable() {
  401. return selectable;
  402. }
  403. /**
  404. * Sets the selectable state. Selectable determines if the a node can be
  405. * selected on the client side. Selectable does not affect
  406. * {@link #setValue(Object)} or {@link #select(Object)}.
  407. *
  408. * <p>
  409. * The tree is selectable by default.
  410. * </p>
  411. *
  412. * @param selectable
  413. * The new selectable state.
  414. */
  415. public void setSelectable(boolean selectable) {
  416. if (this.selectable != selectable) {
  417. this.selectable = selectable;
  418. markAsDirty();
  419. }
  420. }
  421. /**
  422. * Sets the behavior of the multiselect mode
  423. *
  424. * @param mode
  425. * The mode to set
  426. */
  427. public void setMultiselectMode(MultiSelectMode mode) {
  428. if (multiSelectMode != mode && mode != null) {
  429. multiSelectMode = mode;
  430. markAsDirty();
  431. }
  432. }
  433. /**
  434. * Returns the mode the multiselect is in. The mode controls how
  435. * multiselection can be done.
  436. *
  437. * @return The mode
  438. */
  439. public MultiSelectMode getMultiselectMode() {
  440. return multiSelectMode;
  441. }
  442. /* Component API */
  443. @Override
  444. public void changeVariables(Object source, Map<String, Object> variables) {
  445. if (variables.containsKey("clickedKey")) {
  446. String key = (String) variables.get("clickedKey");
  447. Object id = itemIdMapper.get(key);
  448. MouseEventDetails details = MouseEventDetails
  449. .deSerialize((String) variables.get("clickEvent"));
  450. Item item = getItem(id);
  451. if (item != null) {
  452. fireEvent(new ItemClickEvent(this, item, id, null, details));
  453. }
  454. }
  455. if (!isSelectable() && variables.containsKey("selected")) {
  456. // Not-selectable is a special case, AbstractSelect does not support
  457. // TODO could be optimized.
  458. variables = new HashMap<String, Object>(variables);
  459. variables.remove("selected");
  460. }
  461. // Collapses the nodes
  462. if (variables.containsKey("collapse")) {
  463. final String[] keys = (String[]) variables.get("collapse");
  464. for (int i = 0; i < keys.length; i++) {
  465. final Object id = itemIdMapper.get(keys[i]);
  466. if (id != null && isExpanded(id)) {
  467. expanded.remove(id);
  468. if (expandedItemId == id) {
  469. expandedItemId = null;
  470. }
  471. fireCollapseEvent(id);
  472. }
  473. }
  474. }
  475. // Expands the nodes
  476. if (variables.containsKey("expand")) {
  477. boolean sendChildTree = false;
  478. if (variables.containsKey("requestChildTree")) {
  479. sendChildTree = true;
  480. }
  481. final String[] keys = (String[]) variables.get("expand");
  482. for (int i = 0; i < keys.length; i++) {
  483. final Object id = itemIdMapper.get(keys[i]);
  484. if (id != null) {
  485. expandItem(id, sendChildTree);
  486. }
  487. }
  488. }
  489. // AbstractSelect cannot handle multiselection so we handle
  490. // it ourself
  491. if (variables.containsKey("selected") && isMultiSelect()
  492. && multiSelectMode == MultiSelectMode.DEFAULT) {
  493. handleSelectedItems(variables);
  494. variables = new HashMap<String, Object>(variables);
  495. variables.remove("selected");
  496. }
  497. // Selections are handled by the select component
  498. super.changeVariables(source, variables);
  499. // Actions
  500. if (variables.containsKey("action")) {
  501. final StringTokenizer st = new StringTokenizer(
  502. (String) variables.get("action"), ",");
  503. if (st.countTokens() == 2) {
  504. final Object itemId = itemIdMapper.get(st.nextToken());
  505. final Action action = actionMapper.get(st.nextToken());
  506. if (action != null && (itemId == null || containsId(itemId))
  507. && actionHandlers != null) {
  508. for (Handler ah : actionHandlers) {
  509. ah.handleAction(action, this, itemId);
  510. }
  511. }
  512. }
  513. }
  514. }
  515. /**
  516. * Handles the selection
  517. *
  518. * @param variables
  519. * The variables sent to the server from the client
  520. */
  521. private void handleSelectedItems(Map<String, Object> variables) {
  522. final String[] ka = (String[]) variables.get("selected");
  523. // Converts the key-array to id-set
  524. final LinkedList<Object> s = new LinkedList<Object>();
  525. for (int i = 0; i < ka.length; i++) {
  526. final Object id = itemIdMapper.get(ka[i]);
  527. if (!isNullSelectionAllowed()
  528. && (id == null || id == getNullSelectionItemId())) {
  529. // skip empty selection if nullselection is not allowed
  530. markAsDirty();
  531. } else if (id != null && containsId(id)) {
  532. s.add(id);
  533. }
  534. }
  535. if (!isNullSelectionAllowed() && s.size() < 1) {
  536. // empty selection not allowed, keep old value
  537. markAsDirty();
  538. return;
  539. }
  540. setValue(s, true);
  541. }
  542. /**
  543. * Paints any needed component-specific things to the given UIDL stream.
  544. *
  545. * @see AbstractComponent#paintContent(PaintTarget)
  546. */
  547. @Override
  548. public void paintContent(PaintTarget target) throws PaintException {
  549. initialPaint = false;
  550. if (partialUpdate) {
  551. target.addAttribute("partialUpdate", true);
  552. target.addAttribute("rootKey", itemIdMapper.key(expandedItemId));
  553. } else {
  554. getCaptionChangeListener().clear();
  555. // The tab ordering number
  556. if (getTabIndex() > 0) {
  557. target.addAttribute("tabindex", getTabIndex());
  558. }
  559. // Paint tree attributes
  560. if (isSelectable()) {
  561. target.addAttribute("selectmode",
  562. (isMultiSelect() ? "multi" : "single"));
  563. if (isMultiSelect()) {
  564. target.addAttribute("multiselectmode",
  565. multiSelectMode.toString());
  566. }
  567. } else {
  568. target.addAttribute("selectmode", "none");
  569. }
  570. if (isNewItemsAllowed()) {
  571. target.addAttribute("allownewitem", true);
  572. }
  573. if (isNullSelectionAllowed()) {
  574. target.addAttribute("nullselect", true);
  575. }
  576. if (dragMode != TreeDragMode.NONE) {
  577. target.addAttribute("dragMode", dragMode.ordinal());
  578. }
  579. if (isHtmlContentAllowed()) {
  580. target.addAttribute(TreeConstants.ATTRIBUTE_HTML_ALLOWED, true);
  581. }
  582. }
  583. // Initialize variables
  584. final Set<Action> actionSet = new LinkedHashSet<Action>();
  585. // rendered selectedKeys
  586. LinkedList<String> selectedKeys = new LinkedList<String>();
  587. final LinkedList<String> expandedKeys = new LinkedList<String>();
  588. // Iterates through hierarchical tree using a stack of iterators
  589. final Stack<Iterator<?>> iteratorStack = new Stack<Iterator<?>>();
  590. Collection<?> ids;
  591. if (partialUpdate) {
  592. ids = getChildren(expandedItemId);
  593. } else {
  594. ids = rootItemIds();
  595. }
  596. if (ids != null) {
  597. iteratorStack.push(ids.iterator());
  598. }
  599. /*
  600. * Body actions - Actions which has the target null and can be invoked
  601. * by right clicking on the Tree body
  602. */
  603. if (actionHandlers != null) {
  604. final ArrayList<String> keys = new ArrayList<String>();
  605. for (Handler ah : actionHandlers) {
  606. // Getting action for the null item, which in this case
  607. // means the body item
  608. final Action[] aa = ah.getActions(null, this);
  609. if (aa != null) {
  610. for (int ai = 0; ai < aa.length; ai++) {
  611. final String akey = actionMapper.key(aa[ai]);
  612. actionSet.add(aa[ai]);
  613. keys.add(akey);
  614. }
  615. }
  616. }
  617. target.addAttribute("alb", keys.toArray());
  618. }
  619. while (!iteratorStack.isEmpty()) {
  620. // Gets the iterator for current tree level
  621. final Iterator<?> i = iteratorStack.peek();
  622. // If the level is finished, back to previous tree level
  623. if (!i.hasNext()) {
  624. // Removes used iterator from the stack
  625. iteratorStack.pop();
  626. // Closes node
  627. if (!iteratorStack.isEmpty()) {
  628. target.endTag("node");
  629. }
  630. }
  631. // Adds the item on current level
  632. else {
  633. final Object itemId = i.next();
  634. // Starts the item / node
  635. final boolean isNode = areChildrenAllowed(itemId);
  636. if (isNode) {
  637. target.startTag("node");
  638. } else {
  639. target.startTag("leaf");
  640. }
  641. if (itemStyleGenerator != null) {
  642. String stylename = itemStyleGenerator.getStyle(this,
  643. itemId);
  644. if (stylename != null) {
  645. target.addAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE,
  646. stylename);
  647. }
  648. }
  649. if (itemDescriptionGenerator != null) {
  650. String description = itemDescriptionGenerator
  651. .generateDescription(this, itemId, null);
  652. if (description != null && !description.equals("")) {
  653. target.addAttribute("descr", description);
  654. }
  655. }
  656. // Adds the attributes
  657. target.addAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION,
  658. getItemCaption(itemId));
  659. final Resource icon = getItemIcon(itemId);
  660. if (icon != null) {
  661. target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON,
  662. getItemIcon(itemId));
  663. target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT,
  664. getItemIconAlternateText(itemId));
  665. }
  666. final String key = itemIdMapper.key(itemId);
  667. target.addAttribute("key", key);
  668. if (isSelected(itemId)) {
  669. target.addAttribute("selected", true);
  670. selectedKeys.add(key);
  671. }
  672. if (areChildrenAllowed(itemId) && isExpanded(itemId)) {
  673. target.addAttribute("expanded", true);
  674. expandedKeys.add(key);
  675. }
  676. // Add caption change listener
  677. getCaptionChangeListener().addNotifierForItem(itemId);
  678. // Actions
  679. if (actionHandlers != null) {
  680. final ArrayList<String> keys = new ArrayList<String>();
  681. final Iterator<Action.Handler> ahi = actionHandlers
  682. .iterator();
  683. while (ahi.hasNext()) {
  684. final Action[] aa = ahi.next().getActions(itemId, this);
  685. if (aa != null) {
  686. for (int ai = 0; ai < aa.length; ai++) {
  687. final String akey = actionMapper.key(aa[ai]);
  688. actionSet.add(aa[ai]);
  689. keys.add(akey);
  690. }
  691. }
  692. }
  693. target.addAttribute("al", keys.toArray());
  694. }
  695. // Adds the children if expanded, or close the tag
  696. if (isExpanded(itemId) && hasChildren(itemId)
  697. && areChildrenAllowed(itemId)) {
  698. iteratorStack.push(getChildren(itemId).iterator());
  699. } else {
  700. if (isNode) {
  701. target.endTag("node");
  702. } else {
  703. target.endTag("leaf");
  704. }
  705. }
  706. }
  707. }
  708. // Actions
  709. if (!actionSet.isEmpty()) {
  710. target.addVariable(this, "action", "");
  711. target.startTag("actions");
  712. final Iterator<Action> i = actionSet.iterator();
  713. while (i.hasNext()) {
  714. final Action a = i.next();
  715. target.startTag("action");
  716. if (a.getCaption() != null) {
  717. target.addAttribute(TreeConstants.ATTRIBUTE_ACTION_CAPTION,
  718. a.getCaption());
  719. }
  720. if (a.getIcon() != null) {
  721. target.addAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON,
  722. a.getIcon());
  723. }
  724. target.addAttribute("key", actionMapper.key(a));
  725. target.endTag("action");
  726. }
  727. target.endTag("actions");
  728. }
  729. if (partialUpdate) {
  730. partialUpdate = false;
  731. } else {
  732. // Selected
  733. target.addVariable(this, "selected",
  734. selectedKeys.toArray(new String[selectedKeys.size()]));
  735. // Expand and collapse
  736. target.addVariable(this, "expand", new String[] {});
  737. target.addVariable(this, "collapse", new String[] {});
  738. // New items
  739. target.addVariable(this, "newitem", new String[] {});
  740. if (dropHandler != null) {
  741. dropHandler.getAcceptCriterion().paint(target);
  742. }
  743. }
  744. }
  745. /* Container.Hierarchical API */
  746. /**
  747. * Tests if the Item with given ID can have any children.
  748. *
  749. * @see Container.Hierarchical#areChildrenAllowed(Object)
  750. */
  751. @Override
  752. public boolean areChildrenAllowed(Object itemId) {
  753. return ((Container.Hierarchical) items).areChildrenAllowed(itemId);
  754. }
  755. /**
  756. * Gets the IDs of all Items that are children of the specified Item.
  757. *
  758. * @see Container.Hierarchical#getChildren(Object)
  759. */
  760. @Override
  761. public Collection<?> getChildren(Object itemId) {
  762. return ((Container.Hierarchical) items).getChildren(itemId);
  763. }
  764. /**
  765. * Gets the ID of the parent Item of the specified Item.
  766. *
  767. * @see Container.Hierarchical#getParent(Object)
  768. */
  769. @Override
  770. public Object getParent(Object itemId) {
  771. return ((Container.Hierarchical) items).getParent(itemId);
  772. }
  773. /**
  774. * Tests if the Item specified with <code>itemId</code> has child Items.
  775. *
  776. * @see Container.Hierarchical#hasChildren(Object)
  777. */
  778. @Override
  779. public boolean hasChildren(Object itemId) {
  780. return ((Container.Hierarchical) items).hasChildren(itemId);
  781. }
  782. /**
  783. * Tests if the Item specified with <code>itemId</code> is a root Item.
  784. *
  785. * @see Container.Hierarchical#isRoot(Object)
  786. */
  787. @Override
  788. public boolean isRoot(Object itemId) {
  789. return ((Container.Hierarchical) items).isRoot(itemId);
  790. }
  791. /**
  792. * Gets the IDs of all Items in the container that don't have a parent.
  793. *
  794. * @see Container.Hierarchical#rootItemIds()
  795. */
  796. @Override
  797. public Collection<?> rootItemIds() {
  798. return ((Container.Hierarchical) items).rootItemIds();
  799. }
  800. /**
  801. * Sets the given Item's capability to have children.
  802. *
  803. * @see Container.Hierarchical#setChildrenAllowed(Object, boolean)
  804. */
  805. @Override
  806. public boolean setChildrenAllowed(Object itemId,
  807. boolean areChildrenAllowed) {
  808. final boolean success = ((Container.Hierarchical) items)
  809. .setChildrenAllowed(itemId, areChildrenAllowed);
  810. if (success) {
  811. markAsDirty();
  812. }
  813. return success;
  814. }
  815. /*
  816. * (non-Javadoc)
  817. *
  818. * @see com.vaadin.data.Container.Hierarchical#setParent(java.lang.Object ,
  819. * java.lang.Object)
  820. */
  821. @Override
  822. public boolean setParent(Object itemId, Object newParentId) {
  823. final boolean success = ((Container.Hierarchical) items)
  824. .setParent(itemId, newParentId);
  825. if (success) {
  826. markAsDirty();
  827. }
  828. return success;
  829. }
  830. /* Overriding select behavior */
  831. /**
  832. * Sets the Container that serves as the data source of the viewer.
  833. *
  834. * @see Container.Viewer#setContainerDataSource(Container)
  835. */
  836. @Override
  837. public void setContainerDataSource(Container newDataSource) {
  838. if (newDataSource == null) {
  839. newDataSource = new HierarchicalContainer();
  840. }
  841. // Assure that the data source is ordered by making unordered
  842. // containers ordered by wrapping them
  843. if (Container.Hierarchical.class
  844. .isAssignableFrom(newDataSource.getClass())) {
  845. super.setContainerDataSource(newDataSource);
  846. } else {
  847. super.setContainerDataSource(
  848. new ContainerHierarchicalWrapper(newDataSource));
  849. }
  850. /*
  851. * Ensure previous expanded items are cleaned up if they don't exist in
  852. * the new container
  853. */
  854. if (expanded != null) {
  855. /*
  856. * We need to check that the expanded-field is not null since
  857. * setContainerDataSource() is called from the parent constructor
  858. * (AbstractSelect()) and at that time the expanded field is not yet
  859. * initialized.
  860. */
  861. cleanupExpandedItems();
  862. }
  863. }
  864. @Override
  865. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  866. super.containerItemSetChange(event);
  867. if (getContainerDataSource() instanceof Filterable) {
  868. boolean hasFilters = !((Filterable) getContainerDataSource())
  869. .getContainerFilters().isEmpty();
  870. if (!hasFilters) {
  871. /*
  872. * If Container is not filtered then the itemsetchange is caused
  873. * by either adding or removing items to the container. To
  874. * prevent a memory leak we should cleanup the expanded list
  875. * from items which was removed.
  876. *
  877. * However, there will still be a leak if the container is
  878. * filtered to show only a subset of the items in the tree and
  879. * later unfiltered items are removed from the container. In
  880. * that case references to the unfiltered item ids will remain
  881. * in the expanded list until the Tree instance is removed and
  882. * the list is destroyed, or the container data source is
  883. * replaced/updated. To force the removal of the removed items
  884. * the application developer needs to a) remove the container
  885. * filters temporarly or b) re-apply the container datasource
  886. * using setContainerDataSource(getContainerDataSource())
  887. */
  888. cleanupExpandedItems();
  889. }
  890. }
  891. }
  892. /* Expand event and listener */
  893. /**
  894. * Event to fired when a node is expanded. ExapandEvent is fired when a node
  895. * is to be expanded. it can me used to dynamically fill the sub-nodes of
  896. * the node.
  897. *
  898. * @author Vaadin Ltd.
  899. * @since 3.0
  900. */
  901. @Deprecated
  902. public static class ExpandEvent extends Component.Event {
  903. private final Object expandedItemId;
  904. /**
  905. * New instance of options change event
  906. *
  907. * @param source
  908. * the Source of the event.
  909. * @param expandedItemId
  910. */
  911. public ExpandEvent(Component source, Object expandedItemId) {
  912. super(source);
  913. this.expandedItemId = expandedItemId;
  914. }
  915. /**
  916. * Node where the event occurred.
  917. *
  918. * @return the Source of the event.
  919. */
  920. public Object getItemId() {
  921. return expandedItemId;
  922. }
  923. }
  924. /**
  925. * Expand event listener.
  926. *
  927. * @author Vaadin Ltd.
  928. * @since 3.0
  929. */
  930. @Deprecated
  931. public interface ExpandListener extends Serializable {
  932. public static final Method EXPAND_METHOD = ReflectTools.findMethod(
  933. ExpandListener.class, "nodeExpand", ExpandEvent.class);
  934. /**
  935. * A node has been expanded.
  936. *
  937. * @param event
  938. * the Expand event.
  939. */
  940. public void nodeExpand(ExpandEvent event);
  941. }
  942. /**
  943. * Adds the expand listener.
  944. *
  945. * @param listener
  946. * the Listener to be added.
  947. */
  948. public void addExpandListener(ExpandListener listener) {
  949. addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD);
  950. }
  951. /**
  952. * @deprecated As of 7.0, replaced by
  953. * {@link #addExpandListener(ExpandListener)}
  954. **/
  955. @Deprecated
  956. public void addListener(ExpandListener listener) {
  957. addExpandListener(listener);
  958. }
  959. /**
  960. * Removes the expand listener.
  961. *
  962. * @param listener
  963. * the Listener to be removed.
  964. */
  965. public void removeExpandListener(ExpandListener listener) {
  966. removeListener(ExpandEvent.class, listener,
  967. ExpandListener.EXPAND_METHOD);
  968. }
  969. /**
  970. * @deprecated As of 7.0, replaced by
  971. * {@link #removeExpandListener(ExpandListener)}
  972. **/
  973. @Deprecated
  974. public void removeListener(ExpandListener listener) {
  975. removeExpandListener(listener);
  976. }
  977. /**
  978. * Emits the expand event.
  979. *
  980. * @param itemId
  981. * the item id.
  982. */
  983. protected void fireExpandEvent(Object itemId) {
  984. fireEvent(new ExpandEvent(this, itemId));
  985. }
  986. /* Collapse event */
  987. /**
  988. * Collapse event
  989. *
  990. * @author Vaadin Ltd.
  991. * @since 3.0
  992. */
  993. @Deprecated
  994. public static class CollapseEvent extends Component.Event {
  995. private final Object collapsedItemId;
  996. /**
  997. * New instance of options change event.
  998. *
  999. * @param source
  1000. * the Source of the event.
  1001. * @param collapsedItemId
  1002. */
  1003. public CollapseEvent(Component source, Object collapsedItemId) {
  1004. super(source);
  1005. this.collapsedItemId = collapsedItemId;
  1006. }
  1007. /**
  1008. * Gets tge Collapsed Item id.
  1009. *
  1010. * @return the collapsed item id.
  1011. */
  1012. public Object getItemId() {
  1013. return collapsedItemId;
  1014. }
  1015. }
  1016. /**
  1017. * Collapse event listener.
  1018. *
  1019. * @author Vaadin Ltd.
  1020. * @since 3.0
  1021. */
  1022. @Deprecated
  1023. public interface CollapseListener extends Serializable {
  1024. public static final Method COLLAPSE_METHOD = ReflectTools.findMethod(
  1025. CollapseListener.class, "nodeCollapse", CollapseEvent.class);
  1026. /**
  1027. * A node has been collapsed.
  1028. *
  1029. * @param event
  1030. * the Collapse event.
  1031. */
  1032. public void nodeCollapse(CollapseEvent event);
  1033. }
  1034. /**
  1035. * Adds the collapse listener.
  1036. *
  1037. * @param listener
  1038. * the Listener to be added.
  1039. */
  1040. public void addCollapseListener(CollapseListener listener) {
  1041. addListener(CollapseEvent.class, listener,
  1042. CollapseListener.COLLAPSE_METHOD);
  1043. }
  1044. /**
  1045. * @deprecated As of 7.0, replaced by
  1046. * {@link #addCollapseListener(CollapseListener)}
  1047. **/
  1048. @Deprecated
  1049. public void addListener(CollapseListener listener) {
  1050. addCollapseListener(listener);
  1051. }
  1052. /**
  1053. * Removes the collapse listener.
  1054. *
  1055. * @param listener
  1056. * the Listener to be removed.
  1057. */
  1058. public void removeCollapseListener(CollapseListener listener) {
  1059. removeListener(CollapseEvent.class, listener,
  1060. CollapseListener.COLLAPSE_METHOD);
  1061. }
  1062. /**
  1063. * @deprecated As of 7.0, replaced by
  1064. * {@link #removeCollapseListener(CollapseListener)}
  1065. **/
  1066. @Deprecated
  1067. public void removeListener(CollapseListener listener) {
  1068. removeCollapseListener(listener);
  1069. }
  1070. /**
  1071. * Emits collapse event.
  1072. *
  1073. * @param itemId
  1074. * the item id.
  1075. */
  1076. protected void fireCollapseEvent(Object itemId) {
  1077. fireEvent(new CollapseEvent(this, itemId));
  1078. }
  1079. /* Action container */
  1080. /**
  1081. * Adds an action handler.
  1082. *
  1083. * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
  1084. */
  1085. @Override
  1086. public void addActionHandler(Action.Handler actionHandler) {
  1087. if (actionHandler != null) {
  1088. if (actionHandlers == null) {
  1089. actionHandlers = new LinkedList<Action.Handler>();
  1090. actionMapper = new KeyMapper<Action>();
  1091. }
  1092. if (!actionHandlers.contains(actionHandler)) {
  1093. actionHandlers.add(actionHandler);
  1094. markAsDirty();
  1095. }
  1096. }
  1097. }
  1098. /**
  1099. * Removes an action handler.
  1100. *
  1101. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  1102. */
  1103. @Override
  1104. public void removeActionHandler(Action.Handler actionHandler) {
  1105. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  1106. actionHandlers.remove(actionHandler);
  1107. if (actionHandlers.isEmpty()) {
  1108. actionHandlers = null;
  1109. actionMapper = null;
  1110. }
  1111. markAsDirty();
  1112. }
  1113. }
  1114. /**
  1115. * Removes all action handlers
  1116. */
  1117. public void removeAllActionHandlers() {
  1118. actionHandlers = null;
  1119. actionMapper = null;
  1120. markAsDirty();
  1121. }
  1122. /**
  1123. * Gets the visible item ids.
  1124. *
  1125. * @see Select#getVisibleItemIds()
  1126. */
  1127. @Override
  1128. public Collection<?> getVisibleItemIds() {
  1129. final LinkedList<Object> visible = new LinkedList<Object>();
  1130. // Iterates trough hierarchical tree using a stack of iterators
  1131. final Stack<Iterator<?>> iteratorStack = new Stack<Iterator<?>>();
  1132. final Collection<?> ids = rootItemIds();
  1133. if (ids != null) {
  1134. iteratorStack.push(ids.iterator());
  1135. }
  1136. while (!iteratorStack.isEmpty()) {
  1137. // Gets the iterator for current tree level
  1138. final Iterator<?> i = iteratorStack.peek();
  1139. // If the level is finished, back to previous tree level
  1140. if (!i.hasNext()) {
  1141. // Removes used iterator from the stack
  1142. iteratorStack.pop();
  1143. }
  1144. // Adds the item on current level
  1145. else {
  1146. final Object itemId = i.next();
  1147. visible.add(itemId);
  1148. // Adds children if expanded, or close the tag
  1149. if (isExpanded(itemId) && hasChildren(itemId)) {
  1150. iteratorStack.push(getChildren(itemId).iterator());
  1151. }
  1152. }
  1153. }
  1154. return visible;
  1155. }
  1156. /**
  1157. * Tree does not support <code>setNullSelectionItemId</code>.
  1158. *
  1159. * @see AbstractSelect#setNullSelectionItemId(java.lang.Object)
  1160. */
  1161. @Override
  1162. public void setNullSelectionItemId(Object nullSelectionItemId)
  1163. throws UnsupportedOperationException {
  1164. if (nullSelectionItemId != null) {
  1165. throw new UnsupportedOperationException();
  1166. }
  1167. }
  1168. /**
  1169. * Adding new items is not supported.
  1170. *
  1171. * @throws UnsupportedOperationException
  1172. * if set to true.
  1173. * @see Select#setNewItemsAllowed(boolean)
  1174. */
  1175. @Override
  1176. public void setNewItemsAllowed(boolean allowNewOptions)
  1177. throws UnsupportedOperationException {
  1178. if (allowNewOptions) {
  1179. throw new UnsupportedOperationException();
  1180. }
  1181. }
  1182. private ItemStyleGenerator itemStyleGenerator;
  1183. private DropHandler dropHandler;
  1184. private boolean htmlContentAllowed;
  1185. @Override
  1186. public void addItemClickListener(ItemClickListener listener) {
  1187. addListener(TreeConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  1188. listener, ItemClickEvent.ITEM_CLICK_METHOD);
  1189. }
  1190. /**
  1191. * @deprecated As of 7.0, replaced by
  1192. * {@link #addItemClickListener(ItemClickListener)}
  1193. **/
  1194. @Override
  1195. @Deprecated
  1196. public void addListener(ItemClickListener listener) {
  1197. addItemClickListener(listener);
  1198. }
  1199. @Override
  1200. public void removeItemClickListener(ItemClickListener listener) {
  1201. removeListener(TreeConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  1202. listener);
  1203. }
  1204. /**
  1205. * @deprecated As of 7.0, replaced by
  1206. * {@link #removeItemClickListener(ItemClickListener)}
  1207. **/
  1208. @Override
  1209. @Deprecated
  1210. public void removeListener(ItemClickListener listener) {
  1211. removeItemClickListener(listener);
  1212. }
  1213. /**
  1214. * Sets the {@link ItemStyleGenerator} to be used with this tree.
  1215. *
  1216. * @param itemStyleGenerator
  1217. * item style generator or null to remove generator
  1218. */
  1219. public void setItemStyleGenerator(ItemStyleGenerator itemStyleGenerator) {
  1220. if (this.itemStyleGenerator != itemStyleGenerator) {
  1221. this.itemStyleGenerator = itemStyleGenerator;
  1222. markAsDirty();
  1223. }
  1224. }
  1225. /**
  1226. * @return the current {@link ItemStyleGenerator} for this tree. Null if
  1227. * {@link ItemStyleGenerator} is not set.
  1228. */
  1229. public ItemStyleGenerator getItemStyleGenerator() {
  1230. return itemStyleGenerator;
  1231. }
  1232. /**
  1233. * ItemStyleGenerator can be used to add custom styles to tree items. The
  1234. * CSS class name that will be added to the item content is
  1235. * <tt>v-tree-node-[style name]</tt>.
  1236. */
  1237. @Deprecated
  1238. public interface ItemStyleGenerator extends Serializable {
  1239. /**
  1240. * Called by Tree when an item is painted.
  1241. *
  1242. * @param source
  1243. * the source Tree
  1244. * @param itemId
  1245. * The itemId of the item to be painted
  1246. * @return The style name to add to this item. (the CSS class name will
  1247. * be v-tree-node-[style name]
  1248. */
  1249. public abstract String getStyle(Tree source, Object itemId);
  1250. }
  1251. // Overriden so javadoc comes from Container.Hierarchical
  1252. @Override
  1253. public boolean removeItem(Object itemId)
  1254. throws UnsupportedOperationException {
  1255. return super.removeItem(itemId);
  1256. }
  1257. @Override
  1258. public DropHandler getDropHandler() {
  1259. return dropHandler;
  1260. }
  1261. public void setDropHandler(DropHandler dropHandler) {
  1262. this.dropHandler = dropHandler;
  1263. }
  1264. /**
  1265. * A {@link TargetDetails} implementation with Tree specific api.
  1266. *
  1267. * @since 6.3
  1268. */
  1269. @Deprecated
  1270. public class TreeTargetDetails extends AbstractSelectTargetDetails {
  1271. TreeTargetDetails(Map<String, Object> rawVariables) {
  1272. super(rawVariables);
  1273. }
  1274. @Override
  1275. public Tree getTarget() {
  1276. return (Tree) super.getTarget();
  1277. }
  1278. /**
  1279. * If the event is on a node that can not have children (see
  1280. * {@link Tree#areChildrenAllowed(Object)}), this method returns the
  1281. * parent item id of the target item (see {@link #getItemIdOver()} ).
  1282. * The identifier of the parent node is also returned if the cursor is
  1283. * on the top part of node. Else this method returns the same as
  1284. * {@link #getItemIdOver()}.
  1285. * <p>
  1286. * In other words this method returns the identifier of the "folder"
  1287. * into the drag operation is targeted.
  1288. * <p>
  1289. * If the method returns null, the current target is on a root node or
  1290. * on other undefined area over the tree component.
  1291. * <p>
  1292. * The default Tree implementation marks the targetted tree node with
  1293. * CSS classnames v-tree-node-dragfolder and
  1294. * v-tree-node-caption-dragfolder (for the caption element).
  1295. */
  1296. public Object getItemIdInto() {
  1297. Object itemIdOver = getItemIdOver();
  1298. if (areChildrenAllowed(itemIdOver)
  1299. && getDropLocation() == VerticalDropLocation.MIDDLE) {
  1300. return itemIdOver;
  1301. }
  1302. return getParent(itemIdOver);
  1303. }
  1304. /**
  1305. * If drop is targeted into "folder node" (see {@link #getItemIdInto()}
  1306. * ), this method returns the item id of the node after the drag was
  1307. * targeted. This method is useful when implementing drop into specific
  1308. * location (between specific nodes) in tree.
  1309. *
  1310. * @return the id of the item after the user targets the drop or null if
  1311. * "target" is a first item in node list (or the first in root
  1312. * node list)
  1313. */
  1314. public Object getItemIdAfter() {
  1315. Object itemIdOver = getItemIdOver();
  1316. Object itemIdInto2 = getItemIdInto();
  1317. if (itemIdOver.equals(itemIdInto2)) {
  1318. return null;
  1319. }
  1320. VerticalDropLocation dropLocation = getDropLocation();
  1321. if (VerticalDropLocation.TOP == dropLocation) {
  1322. // if on top of the caption area, add before
  1323. Collection<?> children;
  1324. Object itemIdInto = getItemIdInto();
  1325. if (itemIdInto != null) {
  1326. // seek the previous from child list
  1327. children = getChildren(itemIdInto);
  1328. } else {
  1329. children = rootItemIds();
  1330. }
  1331. Object ref = null;
  1332. for (Object object : children) {
  1333. if (object.equals(itemIdOver)) {
  1334. return ref;
  1335. }
  1336. ref = object;
  1337. }
  1338. }
  1339. return itemIdOver;
  1340. }
  1341. }
  1342. /*
  1343. * (non-Javadoc)
  1344. *
  1345. * @see
  1346. * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map)
  1347. */
  1348. @Override
  1349. public TreeTargetDetails translateDropTargetDetails(
  1350. Map<String, Object> clientVariables) {
  1351. return new TreeTargetDetails(clientVariables);
  1352. }
  1353. /**
  1354. * Helper API for {@link TreeDropCriterion}
  1355. *
  1356. * @param itemId
  1357. * @return
  1358. */
  1359. private String key(Object itemId) {
  1360. return itemIdMapper.key(itemId);
  1361. }
  1362. /**
  1363. * Sets the drag mode that controls how Tree behaves as a {@link DragSource}
  1364. * .
  1365. *
  1366. * @param dragMode
  1367. */
  1368. public void setDragMode(TreeDragMode dragMode) {
  1369. this.dragMode = dragMode;
  1370. markAsDirty();
  1371. }
  1372. /**
  1373. * @return the drag mode that controls how Tree behaves as a
  1374. * {@link DragSource}.
  1375. *
  1376. * @see TreeDragMode
  1377. */
  1378. public TreeDragMode getDragMode() {
  1379. return dragMode;
  1380. }
  1381. /**
  1382. * Concrete implementation of {@link DataBoundTransferable} for data
  1383. * transferred from a tree.
  1384. *
  1385. * @see DataBoundTransferable
  1386. *
  1387. * @since 6.3
  1388. */
  1389. @Deprecated
  1390. protected class TreeTransferable extends DataBoundTransferable {
  1391. public TreeTransferable(Component sourceComponent,
  1392. Map<String, Object> rawVariables) {
  1393. super(sourceComponent, rawVariables);
  1394. }
  1395. @Override
  1396. public Object getItemId() {
  1397. return getData("itemId");
  1398. }
  1399. @Override
  1400. public Object getPropertyId() {
  1401. return getItemCaptionPropertyId();
  1402. }
  1403. }
  1404. /*
  1405. * (non-Javadoc)
  1406. *
  1407. * @see com.vaadin.event.dd.DragSource#getTransferable(java.util.Map)
  1408. */
  1409. @Override
  1410. public Transferable getTransferable(Map<String, Object> payload) {
  1411. TreeTransferable transferable = new TreeTransferable(this, payload);
  1412. // updating drag source variables
  1413. Object object = payload.get("itemId");
  1414. if (object != null) {
  1415. transferable.setData("itemId", itemIdMapper.get((String) object));
  1416. }
  1417. return transferable;
  1418. }
  1419. /**
  1420. * Lazy loading accept criterion for Tree. Accepted target nodes are loaded
  1421. * from server once per drag and drop operation. Developer must override one
  1422. * method that decides accepted tree nodes for the whole Tree.
  1423. *
  1424. * <p>
  1425. * Initially pretty much no data is sent to client. On first required
  1426. * criterion check (per drag request) the client side data structure is
  1427. * initialized from server and no subsequent requests requests are needed
  1428. * during that drag and drop operation.
  1429. */
  1430. @Deprecated
  1431. public abstract static class TreeDropCriterion extends ServerSideCriterion {
  1432. private Tree tree;
  1433. private Set<Object> allowedItemIds;
  1434. /*
  1435. * (non-Javadoc)
  1436. *
  1437. * @see
  1438. * com.vaadin.event.dd.acceptCriteria.ServerSideCriterion#getIdentifier
  1439. * ()
  1440. */
  1441. @Override
  1442. protected String getIdentifier() {
  1443. return TreeDropCriterion.class.getCanonicalName();
  1444. }
  1445. /*
  1446. * (non-Javadoc)
  1447. *
  1448. * @see
  1449. * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#accepts(com.vaadin
  1450. * .event.dd.DragAndDropEvent)
  1451. */
  1452. @Override
  1453. public boolean accept(DragAndDropEvent dragEvent) {
  1454. AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent
  1455. .getTargetDetails();
  1456. tree = (Tree) dragEvent.getTargetDetails().getTarget();
  1457. allowedItemIds = getAllowedItemIds(dragEvent, tree);
  1458. return allowedItemIds.contains(dropTargetData.getItemIdOver());
  1459. }
  1460. /*
  1461. * (non-Javadoc)
  1462. *
  1463. * @see
  1464. * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#paintResponse(
  1465. * com.vaadin.server.PaintTarget)
  1466. */
  1467. @Override
  1468. public void paintResponse(PaintTarget target) throws PaintException {
  1469. /*
  1470. * send allowed nodes to client so subsequent requests can be
  1471. * avoided
  1472. */
  1473. Object[] array = allowedItemIds.toArray();
  1474. for (int i = 0; i < array.length; i++) {
  1475. String key = tree.key(array[i]);
  1476. array[i] = key;
  1477. }
  1478. target.addAttribute("allowedIds", array);
  1479. }
  1480. protected abstract Set<Object> getAllowedItemIds(
  1481. DragAndDropEvent dragEvent, Tree tree);
  1482. }
  1483. /**
  1484. * A criterion that accepts {@link Transferable} only directly on a tree
  1485. * node that can have children.
  1486. * <p>
  1487. * Class is singleton, use {@link TargetItemAllowsChildren#get()} to get the
  1488. * instance.
  1489. *
  1490. * @see Tree#setChildrenAllowed(Object, boolean)
  1491. *
  1492. * @since 6.3
  1493. */
  1494. @Deprecated
  1495. public static class TargetItemAllowsChildren extends TargetDetailIs {
  1496. private static TargetItemAllowsChildren instance = new TargetItemAllowsChildren();
  1497. public static TargetItemAllowsChildren get() {
  1498. return instance;
  1499. }
  1500. private TargetItemAllowsChildren() {
  1501. super("itemIdOverIsNode", Boolean.TRUE);
  1502. }
  1503. /*
  1504. * Uses enhanced server side check
  1505. */
  1506. @Override
  1507. public boolean accept(DragAndDropEvent dragEvent) {
  1508. try {
  1509. // must be over tree node and in the middle of it (not top or
  1510. // bottom
  1511. // part)
  1512. TreeTargetDetails eventDetails = (TreeTargetDetails) dragEvent
  1513. .getTargetDetails();
  1514. Object itemIdOver = eventDetails.getItemIdOver();
  1515. if (!eventDetails.getTarget().areChildrenAllowed(itemIdOver)) {
  1516. return false;
  1517. }
  1518. // return true if directly over
  1519. return eventDetails
  1520. .getDropLocation() == VerticalDropLocation.MIDDLE;
  1521. } catch (Exception e) {
  1522. return false;
  1523. }
  1524. }
  1525. }
  1526. /**
  1527. * An accept criterion that checks the parent node (or parent hierarchy) for
  1528. * the item identifier given in constructor. If the parent is found, content
  1529. * is accepted. Criterion can be used to accepts drags on a specific sub
  1530. * tree only.
  1531. * <p>
  1532. * The root items is also consider to be valid target.
  1533. */
  1534. @Deprecated
  1535. public class TargetInSubtree extends ClientSideCriterion {
  1536. private Object rootId;
  1537. private int depthToCheck = -1;
  1538. /**
  1539. * Constructs a criteria that accepts the drag if the targeted Item is a
  1540. * descendant of Item identified by given id
  1541. *
  1542. * @param parentItemId
  1543. * the item identifier of the parent node
  1544. */
  1545. public TargetInSubtree(Object parentItemId) {
  1546. rootId = parentItemId;
  1547. }
  1548. /**
  1549. * Constructs a criteria that accepts drops within given level below the
  1550. * subtree root identified by given id.
  1551. *
  1552. * @param rootId
  1553. * the item identifier to be sought for
  1554. * @param depthToCheck
  1555. * the depth that tree is traversed upwards to seek for the
  1556. * parent, -1 means that the whole structure should be
  1557. * checked
  1558. */
  1559. public TargetInSubtree(Object rootId, int depthToCheck) {
  1560. this.rootId = rootId;
  1561. this.depthToCheck = depthToCheck;
  1562. }
  1563. @Override
  1564. public boolean accept(DragAndDropEvent dragEvent) {
  1565. try {
  1566. TreeTargetDetails eventDetails = (TreeTargetDetails) dragEvent
  1567. .getTargetDetails();
  1568. if (eventDetails.getItemIdOver() != null) {
  1569. Object itemId = eventDetails.getItemIdOver();
  1570. int i = 0;
  1571. while (itemId != null
  1572. && (depthToCheck == -1 || i <= depthToCheck)) {
  1573. if (itemId.equals(rootId)) {
  1574. return true;
  1575. }
  1576. itemId = getParent(itemId);
  1577. i++;
  1578. }
  1579. }
  1580. return false;
  1581. } catch (Exception e) {
  1582. return false;
  1583. }
  1584. }
  1585. @Override
  1586. public void paintContent(PaintTarget target) throws PaintException {
  1587. super.paintContent(target);
  1588. target.addAttribute("depth", depthToCheck);
  1589. target.addAttribute("key", key(rootId));
  1590. }
  1591. }
  1592. /**
  1593. * Set the item description generator which generates tooltips for the tree
  1594. * items
  1595. *
  1596. * @param generator
  1597. * The generator to use or null to disable
  1598. */
  1599. public void setItemDescriptionGenerator(
  1600. ItemDescriptionGenerator generator) {
  1601. if (generator != itemDescriptionGenerator) {
  1602. itemDescriptionGenerator = generator;
  1603. markAsDirty();
  1604. }
  1605. }
  1606. /**
  1607. * Get the item description generator which generates tooltips for tree
  1608. * items
  1609. */
  1610. public ItemDescriptionGenerator getItemDescriptionGenerator() {
  1611. return itemDescriptionGenerator;
  1612. }
  1613. private void cleanupExpandedItems() {
  1614. Set<Object> removedItemIds = new HashSet<Object>();
  1615. for (Object expandedItemId : expanded) {
  1616. if (getItem(expandedItemId) == null) {
  1617. removedItemIds.add(expandedItemId);
  1618. if (this.expandedItemId == expandedItemId) {
  1619. this.expandedItemId = null;
  1620. }
  1621. }
  1622. }
  1623. expanded.removeAll(removedItemIds);
  1624. }
  1625. /**
  1626. * Reads an Item from a design and inserts it into the data source.
  1627. * Recursively handles any children of the item as well.
  1628. *
  1629. * @since 7.5.0
  1630. * @param node
  1631. * an element representing the item (tree node).
  1632. * @param selected
  1633. * A set accumulating selected items. If the item that is read is
  1634. * marked as selected, its item id should be added to this set.
  1635. * @param context
  1636. * the DesignContext instance used in parsing
  1637. * @return the item id of the new item
  1638. *
  1639. * @throws DesignException
  1640. * if the tag name of the {@code node} element is not
  1641. * {@code node}.
  1642. */
  1643. @Override
  1644. protected String readItem(Element node, Set<String> selected,
  1645. DesignContext context) {
  1646. if (!"node".equals(node.tagName())) {
  1647. throw new DesignException("Unrecognized child element in "
  1648. + getClass().getSimpleName() + ": " + node.tagName());
  1649. }
  1650. String itemId = node.attr("text");
  1651. addItem(itemId);
  1652. if (node.hasAttr("icon")) {
  1653. Resource icon = DesignAttributeHandler.readAttribute("icon",
  1654. node.attributes(), Resource.class);
  1655. setItemIcon(itemId, icon);
  1656. }
  1657. if (node.hasAttr("selected")) {
  1658. selected.add(itemId);
  1659. }
  1660. for (Element child : node.children()) {
  1661. String childItemId = readItem(child, selected, context);
  1662. setParent(childItemId, itemId);
  1663. }
  1664. return itemId;
  1665. }
  1666. /**
  1667. * Recursively writes the root items and their children to a design.
  1668. *
  1669. * @since 7.5.0
  1670. * @param design
  1671. * the element into which to insert the items
  1672. * @param context
  1673. * the DesignContext instance used in writing
  1674. */
  1675. @Override
  1676. protected void writeItems(Element design, DesignContext context) {
  1677. for (Object itemId : rootItemIds()) {
  1678. writeItem(design, itemId, context);
  1679. }
  1680. }
  1681. /**
  1682. * Recursively writes a data source Item and its children to a design.
  1683. *
  1684. * @since 7.5.0
  1685. * @param design
  1686. * the element into which to insert the item
  1687. * @param itemId
  1688. * the id of the item to write
  1689. * @param context
  1690. * the DesignContext instance used in writing
  1691. * @return
  1692. */
  1693. @Override
  1694. protected Element writeItem(Element design, Object itemId,
  1695. DesignContext context) {
  1696. Element element = design.appendElement("node");
  1697. element.attr("text", itemId.toString());
  1698. Resource icon = getItemIcon(itemId);
  1699. if (icon != null) {
  1700. DesignAttributeHandler.writeAttribute("icon", element.attributes(),
  1701. icon, null, Resource.class, context);
  1702. }
  1703. if (isSelected(itemId)) {
  1704. element.attr("selected", "");
  1705. }
  1706. Collection<?> children = getChildren(itemId);
  1707. if (children != null) {
  1708. // Yeah... see #5864
  1709. for (Object childItemId : children) {
  1710. writeItem(element, childItemId, context);
  1711. }
  1712. }
  1713. return element;
  1714. }
  1715. /**
  1716. * Sets whether html is allowed in the item captions. If set to
  1717. * <code>true</code>, the captions are passed to the browser as html and the
  1718. * developer is responsible for ensuring no harmful html is used. If set to
  1719. * <code>false</code>, the content is passed to the browser as plain text.
  1720. * The default setting is <code>false</code>
  1721. *
  1722. * @since 7.6
  1723. * @param htmlContentAllowed
  1724. * <code>true</code> if the captions are used as html,
  1725. * <code>false</code> if used as plain text
  1726. */
  1727. public void setHtmlContentAllowed(boolean htmlContentAllowed) {
  1728. this.htmlContentAllowed = htmlContentAllowed;
  1729. markAsDirty();
  1730. }
  1731. /**
  1732. * Checks whether captions are interpreted as html or plain text.
  1733. *
  1734. * @since 7.6
  1735. * @return <code>true</code> if the captions are displayed as html,
  1736. * <code>false</code> if displayed as plain text
  1737. * @see #setHtmlContentAllowed(boolean)
  1738. */
  1739. public boolean isHtmlContentAllowed() {
  1740. return htmlContentAllowed;
  1741. }
  1742. @Override
  1743. protected TreeState getState() {
  1744. return (TreeState) super.getState();
  1745. }
  1746. }