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

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