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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.lang.reflect.Method;
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9. import java.util.HashMap;
  10. import java.util.HashSet;
  11. import java.util.Iterator;
  12. import java.util.LinkedHashSet;
  13. import java.util.LinkedList;
  14. import java.util.Map;
  15. import java.util.Set;
  16. import java.util.Stack;
  17. import java.util.StringTokenizer;
  18. import com.vaadin.data.Container;
  19. import com.vaadin.data.Item;
  20. import com.vaadin.data.util.ContainerHierarchicalWrapper;
  21. import com.vaadin.data.util.IndexedContainer;
  22. import com.vaadin.event.Action;
  23. import com.vaadin.event.DataBoundTransferable;
  24. import com.vaadin.event.ItemClickEvent;
  25. import com.vaadin.event.Transferable;
  26. import com.vaadin.event.ItemClickEvent.ItemClickListener;
  27. import com.vaadin.event.ItemClickEvent.ItemClickSource;
  28. import com.vaadin.event.dd.DragAndDropEvent;
  29. import com.vaadin.event.dd.DragSource;
  30. import com.vaadin.event.dd.DropHandler;
  31. import com.vaadin.event.dd.DropTarget;
  32. import com.vaadin.event.dd.acceptCriteria.ClientCriterion;
  33. import com.vaadin.event.dd.acceptCriteria.ClientSideCriterion;
  34. import com.vaadin.event.dd.acceptCriteria.ServerSideCriterion;
  35. import com.vaadin.terminal.KeyMapper;
  36. import com.vaadin.terminal.PaintException;
  37. import com.vaadin.terminal.PaintTarget;
  38. import com.vaadin.terminal.Resource;
  39. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  40. import com.vaadin.terminal.gwt.client.ui.VTree;
  41. import com.vaadin.terminal.gwt.client.ui.dd.VLazyInitItemIdentifiers;
  42. import com.vaadin.terminal.gwt.client.ui.dd.VOverTreeNode;
  43. import com.vaadin.terminal.gwt.client.ui.dd.VTargetNodeIsChildOf;
  44. import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
  45. /**
  46. * Tree component. A Tree can be used to select an item (or multiple items) from
  47. * a hierarchical set of items.
  48. *
  49. * @author IT Mill Ltd.
  50. * @version
  51. * @VERSION@
  52. * @since 3.0
  53. */
  54. @SuppressWarnings("serial")
  55. @ClientWidget(VTree.class)
  56. public class Tree extends AbstractSelect implements Container.Hierarchical,
  57. Action.Container, ItemClickSource, DragSource, DropTarget {
  58. private static final Method EXPAND_METHOD;
  59. private static final Method COLLAPSE_METHOD;
  60. static {
  61. try {
  62. EXPAND_METHOD = ExpandListener.class.getDeclaredMethod(
  63. "nodeExpand", new Class[] { ExpandEvent.class });
  64. COLLAPSE_METHOD = CollapseListener.class.getDeclaredMethod(
  65. "nodeCollapse", new Class[] { CollapseEvent.class });
  66. } catch (final java.lang.NoSuchMethodException e) {
  67. // This should never happen
  68. throw new java.lang.RuntimeException(
  69. "Internal error finding methods in Tree");
  70. }
  71. }
  72. /* Private members */
  73. /**
  74. * Set of expanded nodes.
  75. */
  76. private final HashSet expanded = new HashSet();
  77. /**
  78. * List of action handlers.
  79. */
  80. private LinkedList<Action.Handler> actionHandlers = null;
  81. /**
  82. * Action mapper.
  83. */
  84. private KeyMapper actionMapper = null;
  85. /**
  86. * Is the tree selectable on the client side.
  87. */
  88. private boolean selectable = true;
  89. /**
  90. * Flag to indicate sub-tree loading
  91. */
  92. private boolean partialUpdate = false;
  93. /**
  94. * Holds a itemId which was recently expanded
  95. */
  96. private Object expandedItemId;
  97. /**
  98. * a flag which indicates initial paint. After this flag set true partial
  99. * updates are allowed.
  100. */
  101. private boolean initialPaint = true;
  102. /**
  103. * Supported drag modes for Tree.
  104. */
  105. public enum TreeDragMode {
  106. NONE, NODE
  107. // , SUBTREE
  108. }
  109. private TreeDragMode dragMode = TreeDragMode.NONE;
  110. /* Tree constructors */
  111. /**
  112. * Creates a new empty tree.
  113. */
  114. public Tree() {
  115. }
  116. /**
  117. * Creates a new empty tree with caption.
  118. *
  119. * @param caption
  120. */
  121. public Tree(String caption) {
  122. setCaption(caption);
  123. }
  124. /**
  125. * Creates a new tree with caption and connect it to a Container.
  126. *
  127. * @param caption
  128. * @param dataSource
  129. */
  130. public Tree(String caption, Container dataSource) {
  131. setCaption(caption);
  132. setContainerDataSource(dataSource);
  133. }
  134. /* Expanding and collapsing */
  135. /**
  136. * Check is an item is expanded
  137. *
  138. * @param itemId
  139. * the item id.
  140. * @return true iff the item is expanded.
  141. */
  142. public boolean isExpanded(Object itemId) {
  143. return expanded.contains(itemId);
  144. }
  145. /**
  146. * Expands an item.
  147. *
  148. * @param itemId
  149. * the item id.
  150. * @return True iff the expand operation succeeded
  151. */
  152. public boolean expandItem(Object itemId) {
  153. boolean success = expandItem(itemId, true);
  154. requestRepaint();
  155. return success;
  156. }
  157. /**
  158. * Expands an item.
  159. *
  160. * @param itemId
  161. * the item id.
  162. * @param sendChildTree
  163. * flag to indicate if client needs subtree or not (may be
  164. * cached)
  165. * @return True iff the expand operation succeeded
  166. */
  167. private boolean expandItem(Object itemId, boolean sendChildTree) {
  168. // Succeeds if the node is already expanded
  169. if (isExpanded(itemId)) {
  170. return true;
  171. }
  172. // Nodes that can not have children are not expandable
  173. if (!areChildrenAllowed(itemId)) {
  174. return false;
  175. }
  176. // Expands
  177. expanded.add(itemId);
  178. expandedItemId = itemId;
  179. if (initialPaint) {
  180. requestRepaint();
  181. } else if (sendChildTree) {
  182. requestPartialRepaint();
  183. }
  184. fireExpandEvent(itemId);
  185. return true;
  186. }
  187. @Override
  188. public void requestRepaint() {
  189. super.requestRepaint();
  190. partialUpdate = false;
  191. }
  192. private void requestPartialRepaint() {
  193. super.requestRepaint();
  194. partialUpdate = true;
  195. }
  196. /**
  197. * Expands the items recursively
  198. *
  199. * Expands all the children recursively starting from an item. Operation
  200. * succeeds only if all expandable items are expanded.
  201. *
  202. * @param startItemId
  203. * @return True iff the expand operation succeeded
  204. */
  205. public boolean expandItemsRecursively(Object startItemId) {
  206. boolean result = true;
  207. // Initial stack
  208. final Stack todo = new Stack();
  209. todo.add(startItemId);
  210. // Expands recursively
  211. while (!todo.isEmpty()) {
  212. final Object id = todo.pop();
  213. if (areChildrenAllowed(id) && !expandItem(id, false)) {
  214. result = false;
  215. }
  216. if (hasChildren(id)) {
  217. todo.addAll(getChildren(id));
  218. }
  219. }
  220. requestRepaint();
  221. return result;
  222. }
  223. /**
  224. * Collapses an item.
  225. *
  226. * @param itemId
  227. * the item id.
  228. * @return True iff the collapse operation succeeded
  229. */
  230. public boolean collapseItem(Object itemId) {
  231. // Succeeds if the node is already collapsed
  232. if (!isExpanded(itemId)) {
  233. return true;
  234. }
  235. // Collapse
  236. expanded.remove(itemId);
  237. requestRepaint();
  238. fireCollapseEvent(itemId);
  239. return true;
  240. }
  241. /**
  242. * Collapses the items recursively.
  243. *
  244. * Collapse all the children recursively starting from an item. Operation
  245. * succeeds only if all expandable items are collapsed.
  246. *
  247. * @param startItemId
  248. * @return True iff the collapse operation succeeded
  249. */
  250. public boolean collapseItemsRecursively(Object startItemId) {
  251. boolean result = true;
  252. // Initial stack
  253. final Stack todo = new Stack();
  254. todo.add(startItemId);
  255. // Collapse recursively
  256. while (!todo.isEmpty()) {
  257. final Object id = todo.pop();
  258. if (areChildrenAllowed(id) && !collapseItem(id)) {
  259. result = false;
  260. }
  261. if (hasChildren(id)) {
  262. todo.addAll(getChildren(id));
  263. }
  264. }
  265. return result;
  266. }
  267. /**
  268. * Returns the current selectable state. Selectable determines if the a node
  269. * can be selected on the client side. Selectable does not affect
  270. * {@link #setValue(Object)} or {@link #select(Object)}.
  271. *
  272. * <p>
  273. * The tree is selectable by default.
  274. * </p>
  275. *
  276. * @return the current selectable state.
  277. */
  278. public boolean isSelectable() {
  279. return selectable;
  280. }
  281. /**
  282. * Sets the selectable state. Selectable determines if the a node can be
  283. * selected on the client side. Selectable does not affect
  284. * {@link #setValue(Object)} or {@link #select(Object)}.
  285. *
  286. * <p>
  287. * The tree is selectable by default.
  288. * </p>
  289. *
  290. * @param selectable
  291. * The new selectable state.
  292. */
  293. public void setSelectable(boolean selectable) {
  294. if (this.selectable != selectable) {
  295. this.selectable = selectable;
  296. requestRepaint();
  297. }
  298. }
  299. /* Component API */
  300. /*
  301. * (non-Javadoc)
  302. *
  303. * @see com.vaadin.ui.AbstractSelect#changeVariables(java.lang.Object,
  304. * java.util.Map)
  305. */
  306. @Override
  307. public void changeVariables(Object source, Map variables) {
  308. if (variables.containsKey("clickedKey")) {
  309. String key = (String) variables.get("clickedKey");
  310. Object id = itemIdMapper.get(key);
  311. MouseEventDetails details = MouseEventDetails
  312. .deSerialize((String) variables.get("clickEvent"));
  313. Item item = getItem(id);
  314. if (item != null) {
  315. fireEvent(new ItemClickEvent(this, item, id, null, details));
  316. }
  317. }
  318. if (!isSelectable() && variables.containsKey("selected")) {
  319. // Not-selectable is a special case, AbstractSelect does not support
  320. // TODO could be optimized.
  321. variables = new HashMap(variables);
  322. variables.remove("selected");
  323. }
  324. // Collapses the nodes
  325. if (variables.containsKey("collapse")) {
  326. final String[] keys = (String[]) variables.get("collapse");
  327. for (int i = 0; i < keys.length; i++) {
  328. final Object id = itemIdMapper.get(keys[i]);
  329. if (id != null && isExpanded(id)) {
  330. expanded.remove(id);
  331. fireCollapseEvent(id);
  332. }
  333. }
  334. }
  335. // Expands the nodes
  336. if (variables.containsKey("expand")) {
  337. boolean sendChildTree = false;
  338. if (variables.containsKey("requestChildTree")) {
  339. sendChildTree = true;
  340. }
  341. final String[] keys = (String[]) variables.get("expand");
  342. for (int i = 0; i < keys.length; i++) {
  343. final Object id = itemIdMapper.get(keys[i]);
  344. if (id != null) {
  345. expandItem(id, sendChildTree);
  346. }
  347. }
  348. }
  349. // Selections are handled by the select component
  350. super.changeVariables(source, variables);
  351. // Actions
  352. if (variables.containsKey("action")) {
  353. final StringTokenizer st = new StringTokenizer((String) variables
  354. .get("action"), ",");
  355. if (st.countTokens() == 2) {
  356. final Object itemId = itemIdMapper.get(st.nextToken());
  357. final Action action = (Action) actionMapper.get(st.nextToken());
  358. if (action != null && containsId(itemId)
  359. && actionHandlers != null) {
  360. for (final Iterator<Action.Handler> i = actionHandlers
  361. .iterator(); i.hasNext();) {
  362. i.next().handleAction(action, this, itemId);
  363. }
  364. }
  365. }
  366. }
  367. }
  368. /**
  369. * Paints any needed component-specific things to the given UIDL stream.
  370. *
  371. * @see com.vaadin.ui.AbstractComponent#paintContent(PaintTarget)
  372. */
  373. @Override
  374. public void paintContent(PaintTarget target) throws PaintException {
  375. initialPaint = false;
  376. if (partialUpdate) {
  377. target.addAttribute("partialUpdate", true);
  378. target.addAttribute("rootKey", itemIdMapper.key(expandedItemId));
  379. } else {
  380. getCaptionChangeListener().clear();
  381. // The tab ordering number
  382. if (getTabIndex() > 0) {
  383. target.addAttribute("tabindex", getTabIndex());
  384. }
  385. // Paint tree attributes
  386. if (isSelectable()) {
  387. target.addAttribute("selectmode", (isMultiSelect() ? "multi"
  388. : "single"));
  389. } else {
  390. target.addAttribute("selectmode", "none");
  391. }
  392. if (isNewItemsAllowed()) {
  393. target.addAttribute("allownewitem", true);
  394. }
  395. if (isNullSelectionAllowed()) {
  396. target.addAttribute("nullselect", true);
  397. }
  398. if (dragMode != TreeDragMode.NONE) {
  399. target.addAttribute("dragMode", dragMode.ordinal());
  400. }
  401. }
  402. // Initialize variables
  403. final Set<Action> actionSet = new LinkedHashSet<Action>();
  404. // rendered selectedKeys
  405. LinkedList<String> selectedKeys = new LinkedList<String>();
  406. final LinkedList<String> expandedKeys = new LinkedList<String>();
  407. // Iterates through hierarchical tree using a stack of iterators
  408. final Stack<Iterator> iteratorStack = new Stack<Iterator>();
  409. Collection ids;
  410. if (partialUpdate) {
  411. ids = getChildren(expandedItemId);
  412. } else {
  413. ids = rootItemIds();
  414. }
  415. if (ids != null) {
  416. iteratorStack.push(ids.iterator());
  417. }
  418. while (!iteratorStack.isEmpty()) {
  419. // Gets the iterator for current tree level
  420. final Iterator i = iteratorStack.peek();
  421. // If the level is finished, back to previous tree level
  422. if (!i.hasNext()) {
  423. // Removes used iterator from the stack
  424. iteratorStack.pop();
  425. // Closes node
  426. if (!iteratorStack.isEmpty()) {
  427. target.endTag("node");
  428. }
  429. }
  430. // Adds the item on current level
  431. else {
  432. final Object itemId = i.next();
  433. // Starts the item / node
  434. final boolean isNode = areChildrenAllowed(itemId);
  435. if (isNode) {
  436. target.startTag("node");
  437. } else {
  438. target.startTag("leaf");
  439. }
  440. if (itemStyleGenerator != null) {
  441. String stylename = itemStyleGenerator.getStyle(itemId);
  442. if (stylename != null) {
  443. target.addAttribute("style", stylename);
  444. }
  445. }
  446. // Adds the attributes
  447. target.addAttribute("caption", getItemCaption(itemId));
  448. final Resource icon = getItemIcon(itemId);
  449. if (icon != null) {
  450. target.addAttribute("icon", getItemIcon(itemId));
  451. }
  452. final String key = itemIdMapper.key(itemId);
  453. target.addAttribute("key", key);
  454. if (isSelected(itemId)) {
  455. target.addAttribute("selected", true);
  456. selectedKeys.add(key);
  457. }
  458. if (areChildrenAllowed(itemId) && isExpanded(itemId)) {
  459. target.addAttribute("expanded", true);
  460. expandedKeys.add(key);
  461. }
  462. // Add caption change listener
  463. getCaptionChangeListener().addNotifierForItem(itemId);
  464. // Actions
  465. if (actionHandlers != null) {
  466. final ArrayList<String> keys = new ArrayList<String>();
  467. final Iterator<Action.Handler> ahi = actionHandlers
  468. .iterator();
  469. while (ahi.hasNext()) {
  470. final Action[] aa = ahi.next().getActions(itemId, this);
  471. if (aa != null) {
  472. for (int ai = 0; ai < aa.length; ai++) {
  473. final String akey = actionMapper.key(aa[ai]);
  474. actionSet.add(aa[ai]);
  475. keys.add(akey);
  476. }
  477. }
  478. }
  479. target.addAttribute("al", keys.toArray());
  480. }
  481. // Adds the children if expanded, or close the tag
  482. if (isExpanded(itemId) && hasChildren(itemId)
  483. && areChildrenAllowed(itemId)) {
  484. iteratorStack.push(getChildren(itemId).iterator());
  485. } else {
  486. if (isNode) {
  487. target.endTag("node");
  488. } else {
  489. target.endTag("leaf");
  490. }
  491. }
  492. }
  493. }
  494. // Actions
  495. if (!actionSet.isEmpty()) {
  496. target.addVariable(this, "action", "");
  497. target.startTag("actions");
  498. final Iterator<Action> i = actionSet.iterator();
  499. while (i.hasNext()) {
  500. final Action a = i.next();
  501. target.startTag("action");
  502. if (a.getCaption() != null) {
  503. target.addAttribute("caption", a.getCaption());
  504. }
  505. if (a.getIcon() != null) {
  506. target.addAttribute("icon", a.getIcon());
  507. }
  508. target.addAttribute("key", actionMapper.key(a));
  509. target.endTag("action");
  510. }
  511. target.endTag("actions");
  512. }
  513. if (partialUpdate) {
  514. partialUpdate = false;
  515. } else {
  516. // Selected
  517. target.addVariable(this, "selected", selectedKeys
  518. .toArray(new String[selectedKeys.size()]));
  519. // Expand and collapse
  520. target.addVariable(this, "expand", new String[] {});
  521. target.addVariable(this, "collapse", new String[] {});
  522. // New items
  523. target.addVariable(this, "newitem", new String[] {});
  524. if (dropHandler != null) {
  525. dropHandler.getAcceptCriterion().paint(target);
  526. }
  527. }
  528. }
  529. /* Container.Hierarchical API */
  530. /**
  531. * Tests if the Item with given ID can have any children.
  532. *
  533. * @see com.vaadin.data.Container.Hierarchical#areChildrenAllowed(Object)
  534. */
  535. public boolean areChildrenAllowed(Object itemId) {
  536. return ((Container.Hierarchical) items).areChildrenAllowed(itemId);
  537. }
  538. /**
  539. * Gets the IDs of all Items that are children of the specified Item.
  540. *
  541. * @see com.vaadin.data.Container.Hierarchical#getChildren(Object)
  542. */
  543. public Collection getChildren(Object itemId) {
  544. return ((Container.Hierarchical) items).getChildren(itemId);
  545. }
  546. /**
  547. * Gets the ID of the parent Item of the specified Item.
  548. *
  549. * @see com.vaadin.data.Container.Hierarchical#getParent(Object)
  550. */
  551. public Object getParent(Object itemId) {
  552. return ((Container.Hierarchical) items).getParent(itemId);
  553. }
  554. /**
  555. * Tests if the Item specified with <code>itemId</code> has child Items.
  556. *
  557. * @see com.vaadin.data.Container.Hierarchical#hasChildren(Object)
  558. */
  559. public boolean hasChildren(Object itemId) {
  560. return ((Container.Hierarchical) items).hasChildren(itemId);
  561. }
  562. /**
  563. * Tests if the Item specified with <code>itemId</code> is a root Item.
  564. *
  565. * @see com.vaadin.data.Container.Hierarchical#isRoot(Object)
  566. */
  567. public boolean isRoot(Object itemId) {
  568. return ((Container.Hierarchical) items).isRoot(itemId);
  569. }
  570. /**
  571. * Gets the IDs of all Items in the container that don't have a parent.
  572. *
  573. * @see com.vaadin.data.Container.Hierarchical#rootItemIds()
  574. */
  575. public Collection rootItemIds() {
  576. return ((Container.Hierarchical) items).rootItemIds();
  577. }
  578. /**
  579. * Sets the given Item's capability to have children.
  580. *
  581. * @see com.vaadin.data.Container.Hierarchical#setChildrenAllowed(Object,
  582. * boolean)
  583. */
  584. public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed) {
  585. final boolean success = ((Container.Hierarchical) items)
  586. .setChildrenAllowed(itemId, areChildrenAllowed);
  587. if (success) {
  588. fireValueChange(false);
  589. }
  590. return success;
  591. }
  592. /*
  593. * (non-Javadoc)
  594. *
  595. * @see com.vaadin.data.Container.Hierarchical#setParent(java.lang.Object ,
  596. * java.lang.Object)
  597. */
  598. public boolean setParent(Object itemId, Object newParentId) {
  599. final boolean success = ((Container.Hierarchical) items).setParent(
  600. itemId, newParentId);
  601. if (success) {
  602. requestRepaint();
  603. }
  604. return success;
  605. }
  606. /* Overriding select behavior */
  607. /**
  608. * Sets the Container that serves as the data source of the viewer.
  609. *
  610. * @see com.vaadin.data.Container.Viewer#setContainerDataSource(Container)
  611. */
  612. @Override
  613. public void setContainerDataSource(Container newDataSource) {
  614. if (newDataSource == null) {
  615. // Note: using wrapped IndexedContainer to match constructor (super
  616. // creates an IndexedContainer, which is then wrapped).
  617. newDataSource = new ContainerHierarchicalWrapper(
  618. new IndexedContainer());
  619. }
  620. // Assure that the data source is ordered by making unordered
  621. // containers ordered by wrapping them
  622. if (Container.Hierarchical.class.isAssignableFrom(newDataSource
  623. .getClass())) {
  624. super.setContainerDataSource(newDataSource);
  625. } else {
  626. super.setContainerDataSource(new ContainerHierarchicalWrapper(
  627. newDataSource));
  628. }
  629. }
  630. /* Expand event and listener */
  631. /**
  632. * Event to fired when a node is expanded. ExapandEvent is fired when a node
  633. * is to be expanded. it can me used to dynamically fill the sub-nodes of
  634. * the node.
  635. *
  636. * @author IT Mill Ltd.
  637. * @version
  638. * @VERSION@
  639. * @since 3.0
  640. */
  641. public class ExpandEvent extends Component.Event {
  642. private final Object expandedItemId;
  643. /**
  644. * New instance of options change event
  645. *
  646. * @param source
  647. * the Source of the event.
  648. * @param expandedItemId
  649. */
  650. public ExpandEvent(Component source, Object expandedItemId) {
  651. super(source);
  652. this.expandedItemId = expandedItemId;
  653. }
  654. /**
  655. * Node where the event occurred.
  656. *
  657. * @return the Source of the event.
  658. */
  659. public Object getItemId() {
  660. return expandedItemId;
  661. }
  662. }
  663. /**
  664. * Expand event listener.
  665. *
  666. * @author IT Mill Ltd.
  667. * @version
  668. * @VERSION@
  669. * @since 3.0
  670. */
  671. public interface ExpandListener extends Serializable {
  672. /**
  673. * A node has been expanded.
  674. *
  675. * @param event
  676. * the Expand event.
  677. */
  678. public void nodeExpand(ExpandEvent event);
  679. }
  680. /**
  681. * Adds the expand listener.
  682. *
  683. * @param listener
  684. * the Listener to be added.
  685. */
  686. public void addListener(ExpandListener listener) {
  687. addListener(ExpandEvent.class, listener, EXPAND_METHOD);
  688. }
  689. /**
  690. * Removes the expand listener.
  691. *
  692. * @param listener
  693. * the Listener to be removed.
  694. */
  695. public void removeListener(ExpandListener listener) {
  696. removeListener(ExpandEvent.class, listener, EXPAND_METHOD);
  697. }
  698. /**
  699. * Emits the expand event.
  700. *
  701. * @param itemId
  702. * the item id.
  703. */
  704. protected void fireExpandEvent(Object itemId) {
  705. fireEvent(new ExpandEvent(this, itemId));
  706. }
  707. /* Collapse event */
  708. /**
  709. * Collapse event
  710. *
  711. * @author IT Mill Ltd.
  712. * @version
  713. * @VERSION@
  714. * @since 3.0
  715. */
  716. public class CollapseEvent extends Component.Event {
  717. private final Object collapsedItemId;
  718. /**
  719. * New instance of options change event.
  720. *
  721. * @param source
  722. * the Source of the event.
  723. * @param collapsedItemId
  724. */
  725. public CollapseEvent(Component source, Object collapsedItemId) {
  726. super(source);
  727. this.collapsedItemId = collapsedItemId;
  728. }
  729. /**
  730. * Gets tge Collapsed Item id.
  731. *
  732. * @return the collapsed item id.
  733. */
  734. public Object getItemId() {
  735. return collapsedItemId;
  736. }
  737. }
  738. /**
  739. * Collapse event listener.
  740. *
  741. * @author IT Mill Ltd.
  742. * @version
  743. * @VERSION@
  744. * @since 3.0
  745. */
  746. public interface CollapseListener extends Serializable {
  747. /**
  748. * A node has been collapsed.
  749. *
  750. * @param event
  751. * the Collapse event.
  752. */
  753. public void nodeCollapse(CollapseEvent event);
  754. }
  755. /**
  756. * Adds the collapse listener.
  757. *
  758. * @param listener
  759. * the Listener to be added.
  760. */
  761. public void addListener(CollapseListener listener) {
  762. addListener(CollapseEvent.class, listener, COLLAPSE_METHOD);
  763. }
  764. /**
  765. * Removes the collapse listener.
  766. *
  767. * @param listener
  768. * the Listener to be removed.
  769. */
  770. public void removeListener(CollapseListener listener) {
  771. removeListener(CollapseEvent.class, listener, COLLAPSE_METHOD);
  772. }
  773. /**
  774. * Emits collapse event.
  775. *
  776. * @param itemId
  777. * the item id.
  778. */
  779. protected void fireCollapseEvent(Object itemId) {
  780. fireEvent(new CollapseEvent(this, itemId));
  781. }
  782. /* Action container */
  783. /**
  784. * Adds an action handler.
  785. *
  786. * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
  787. */
  788. public void addActionHandler(Action.Handler actionHandler) {
  789. if (actionHandler != null) {
  790. if (actionHandlers == null) {
  791. actionHandlers = new LinkedList<Action.Handler>();
  792. actionMapper = new KeyMapper();
  793. }
  794. if (!actionHandlers.contains(actionHandler)) {
  795. actionHandlers.add(actionHandler);
  796. requestRepaint();
  797. }
  798. }
  799. }
  800. /**
  801. * Removes an action handler.
  802. *
  803. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  804. */
  805. public void removeActionHandler(Action.Handler actionHandler) {
  806. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  807. actionHandlers.remove(actionHandler);
  808. if (actionHandlers.isEmpty()) {
  809. actionHandlers = null;
  810. actionMapper = null;
  811. }
  812. requestRepaint();
  813. }
  814. }
  815. /**
  816. * Removes all action handlers
  817. */
  818. public void removeAllActionHandlers() {
  819. actionHandlers = null;
  820. actionMapper = null;
  821. requestRepaint();
  822. }
  823. /**
  824. * Gets the visible item ids.
  825. *
  826. * @see com.vaadin.ui.Select#getVisibleItemIds()
  827. */
  828. @Override
  829. public Collection getVisibleItemIds() {
  830. final LinkedList visible = new LinkedList();
  831. // Iterates trough hierarchical tree using a stack of iterators
  832. final Stack<Iterator> iteratorStack = new Stack<Iterator>();
  833. final Collection ids = rootItemIds();
  834. if (ids != null) {
  835. iteratorStack.push(ids.iterator());
  836. }
  837. while (!iteratorStack.isEmpty()) {
  838. // Gets the iterator for current tree level
  839. final Iterator i = iteratorStack.peek();
  840. // If the level is finished, back to previous tree level
  841. if (!i.hasNext()) {
  842. // Removes used iterator from the stack
  843. iteratorStack.pop();
  844. }
  845. // Adds the item on current level
  846. else {
  847. final Object itemId = i.next();
  848. visible.add(itemId);
  849. // Adds children if expanded, or close the tag
  850. if (isExpanded(itemId) && hasChildren(itemId)) {
  851. iteratorStack.push(getChildren(itemId).iterator());
  852. }
  853. }
  854. }
  855. return visible;
  856. }
  857. /**
  858. * Tree does not support <code>setNullSelectionItemId</code>.
  859. *
  860. * @see com.vaadin.ui.AbstractSelect#setNullSelectionItemId(java.lang.Object)
  861. */
  862. @Override
  863. public void setNullSelectionItemId(Object nullSelectionItemId)
  864. throws UnsupportedOperationException {
  865. if (nullSelectionItemId != null) {
  866. throw new UnsupportedOperationException();
  867. }
  868. }
  869. /**
  870. * Adding new items is not supported.
  871. *
  872. * @throws UnsupportedOperationException
  873. * if set to true.
  874. * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
  875. */
  876. @Override
  877. public void setNewItemsAllowed(boolean allowNewOptions)
  878. throws UnsupportedOperationException {
  879. if (allowNewOptions) {
  880. throw new UnsupportedOperationException();
  881. }
  882. }
  883. /**
  884. * Focusing to this component is not supported.
  885. *
  886. * @throws UnsupportedOperationException
  887. * if invoked.
  888. * @see com.vaadin.ui.AbstractField#focus()
  889. */
  890. @Override
  891. public void focus() throws UnsupportedOperationException {
  892. throw new UnsupportedOperationException();
  893. }
  894. /**
  895. * Tree does not support lazy options loading mode. Setting this true will
  896. * throw UnsupportedOperationException.
  897. *
  898. * @see com.vaadin.ui.Select#setLazyLoading(boolean)
  899. */
  900. public void setLazyLoading(boolean useLazyLoading) {
  901. if (useLazyLoading) {
  902. throw new UnsupportedOperationException(
  903. "Lazy options loading is not supported by Tree.");
  904. }
  905. }
  906. private ItemStyleGenerator itemStyleGenerator;
  907. private DropHandler dropHandler;
  908. public void addListener(ItemClickListener listener) {
  909. addListener(VTree.ITEM_CLICK_EVENT_ID, ItemClickEvent.class, listener,
  910. ItemClickEvent.ITEM_CLICK_METHOD);
  911. }
  912. public void removeListener(ItemClickListener listener) {
  913. removeListener(VTree.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  914. listener);
  915. }
  916. /**
  917. * Sets the {@link ItemStyleGenerator} to be used with this tree.
  918. *
  919. * @param itemStyleGenerator
  920. * item style generator or null to remove generator
  921. */
  922. public void setItemStyleGenerator(ItemStyleGenerator itemStyleGenerator) {
  923. if (this.itemStyleGenerator != itemStyleGenerator) {
  924. this.itemStyleGenerator = itemStyleGenerator;
  925. requestRepaint();
  926. }
  927. }
  928. /**
  929. * @return the current {@link ItemStyleGenerator} for this tree. Null if
  930. * {@link ItemStyleGenerator} is not set.
  931. */
  932. public ItemStyleGenerator getItemStyleGenerator() {
  933. return itemStyleGenerator;
  934. }
  935. /**
  936. * ItemStyleGenerator can be used to add custom styles to tree items. The
  937. * CSS class name that will be added to the cell content is
  938. * <tt>v-tree-node-[style name]</tt>.
  939. */
  940. public interface ItemStyleGenerator extends Serializable {
  941. /**
  942. * Called by Tree when an item is painted.
  943. *
  944. * @param itemId
  945. * The itemId of the item to be painted
  946. * @return The style name to add to this item. (the CSS class name will
  947. * be v-tree-node-[style name]
  948. */
  949. public abstract String getStyle(Object itemId);
  950. }
  951. // Overriden so javadoc comes from Container.Hierarchical
  952. @Override
  953. public boolean removeItem(Object itemId)
  954. throws UnsupportedOperationException {
  955. return super.removeItem(itemId);
  956. }
  957. public DropHandler getDropHandler() {
  958. return dropHandler;
  959. }
  960. public void setDropHandler(DropHandler dropHandler) {
  961. this.dropHandler = dropHandler;
  962. }
  963. /**
  964. * TODO Javadoc!
  965. *
  966. * @since 6.3
  967. */
  968. public class TreeDropTargetDetails extends AbstractSelectDropTargetDetails {
  969. TreeDropTargetDetails(Map<String, Object> rawVariables) {
  970. super(rawVariables);
  971. }
  972. @Override
  973. public Tree getTarget() {
  974. return (Tree) super.getTarget();
  975. }
  976. /**
  977. * If the event is on a node that can not have children (see
  978. * {@link Tree#areChildrenAllowed(Object)}), this method returns the
  979. * parent item id of the target item (see {@link #getItemIdOver()} ).
  980. * The identifier of the parent node is also returned if the cursor is
  981. * on the top part of node. Else this method returns the same as
  982. * {@link #getItemIdOver()}.
  983. * <p>
  984. * In other words this method returns the identifier of the "folder"
  985. * into the drag operation is targeted.
  986. * <p>
  987. * If the method returns null, the current target is on a root node or
  988. * on other undefined area over the tree component.
  989. * <p>
  990. * The default Tree implementation marks the targetted tree node with
  991. * CSS classnames v-tree-node-dragfolder and
  992. * v-tree-node-caption-dragfolder (for the caption element).
  993. */
  994. public Object getItemIdInto() {
  995. Object itemIdOver = getItemIdOver();
  996. if (areChildrenAllowed(itemIdOver)
  997. && getDropLocation() != VerticalDropLocation.TOP) {
  998. return itemIdOver;
  999. }
  1000. return getParent(itemIdOver);
  1001. }
  1002. }
  1003. /**
  1004. * TODO Javadoc!
  1005. *
  1006. * @since 6.3
  1007. */
  1008. public TreeDropTargetDetails translateDropTargetDetails(
  1009. Map<String, Object> clientVariables) {
  1010. return new TreeDropTargetDetails(clientVariables);
  1011. }
  1012. /**
  1013. * API for {@link TreeDropCriterion}
  1014. *
  1015. * @param itemId
  1016. * @return
  1017. */
  1018. private String key(Object itemId) {
  1019. return itemIdMapper.key(itemId);
  1020. }
  1021. public void setDragMode(TreeDragMode dragMode) {
  1022. this.dragMode = dragMode;
  1023. }
  1024. public TreeDragMode getDragMode() {
  1025. return dragMode;
  1026. }
  1027. /**
  1028. * TODO Javadoc!
  1029. *
  1030. * @since 6.3
  1031. */
  1032. public class TreeTransferable extends DataBoundTransferable {
  1033. public TreeTransferable(Component sourceComponent,
  1034. Map<String, Object> rawVariables) {
  1035. super(sourceComponent, rawVariables);
  1036. }
  1037. @Override
  1038. public Object getItemId() {
  1039. return getData("itemId");
  1040. }
  1041. @Override
  1042. public Object getPropertyId() {
  1043. return getItemCaptionPropertyId();
  1044. }
  1045. }
  1046. /*
  1047. * (non-Javadoc)
  1048. *
  1049. * @see com.vaadin.event.dd.DragSource#getTransferable(java.util.Map)
  1050. */
  1051. public Transferable getTransferable(Map<String, Object> payload) {
  1052. TreeTransferable transferable = new TreeTransferable(this, payload);
  1053. // updating drag source variables
  1054. Object object = payload.get("itemId");
  1055. if (object != null) {
  1056. transferable.setData("itemId", itemIdMapper.get((String) object));
  1057. }
  1058. return transferable;
  1059. }
  1060. /**
  1061. * An example of lazy initializing criterion. Initially pretty much no data
  1062. * is sent to client, on first accepts set (per drag request) the client
  1063. * side data structure is initialized and no subsequent requests requests
  1064. * are needed during that drag and drop operation.
  1065. * <p>
  1066. * See client side counterpart
  1067. */
  1068. @ClientCriterion(VLazyInitItemIdentifiers.class)
  1069. public static abstract class TreeDropCriterion extends ServerSideCriterion {
  1070. private Tree tree;
  1071. private Set<Object> allowedItemIds;
  1072. /*
  1073. * (non-Javadoc)
  1074. *
  1075. * @see
  1076. * com.vaadin.event.dd.acceptCriteria.ServerSideCriterion#getIdentifier
  1077. * ()
  1078. */
  1079. @Override
  1080. protected String getIdentifier() {
  1081. return TreeDropCriterion.class.getCanonicalName();
  1082. }
  1083. /*
  1084. * (non-Javadoc)
  1085. *
  1086. * @see
  1087. * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#accepts(com.vaadin
  1088. * .event.dd.DragAndDropEvent)
  1089. */
  1090. public boolean accepts(DragAndDropEvent dragEvent) {
  1091. AbstractSelectDropTargetDetails dropTargetData = (AbstractSelectDropTargetDetails) dragEvent
  1092. .getDropTargetDetails();
  1093. tree = (Tree) dragEvent.getDropTargetDetails().getTarget();
  1094. allowedItemIds = getAllowedItemIds(dragEvent, tree);
  1095. return allowedItemIds.contains(dropTargetData.getItemIdOver());
  1096. }
  1097. /*
  1098. * (non-Javadoc)
  1099. *
  1100. * @see
  1101. * com.vaadin.event.dd.acceptCriteria.AcceptCriterion#paintResponse(
  1102. * com.vaadin.terminal.PaintTarget)
  1103. */
  1104. @Override
  1105. public void paintResponse(PaintTarget target) throws PaintException {
  1106. /*
  1107. * send allowed nodes to client so subsequent requests can be
  1108. * avoided
  1109. */
  1110. Object[] array = allowedItemIds.toArray();
  1111. for (int i = 0; i < array.length; i++) {
  1112. String key = tree.key(array[i]);
  1113. array[i] = key;
  1114. }
  1115. target.addAttribute("allowedIds", array);
  1116. }
  1117. protected abstract Set<Object> getAllowedItemIds(
  1118. DragAndDropEvent dragEvent, Tree tree);
  1119. }
  1120. /**
  1121. * Accepts transferable only on tree Node (middle of the node + can has
  1122. * child)
  1123. *
  1124. * TODO replace by composition of itemIdIs + drop property
  1125. *
  1126. * @since 6.3
  1127. */
  1128. @ClientCriterion(VOverTreeNode.class)
  1129. public static class OverFolderNode extends ClientSideCriterion {
  1130. private static final long serialVersionUID = 1L;
  1131. public boolean accepts(DragAndDropEvent dragEvent) {
  1132. try {
  1133. // must be over tree node and in the middle of it (not top or
  1134. // bottom
  1135. // part)
  1136. TreeDropTargetDetails eventDetails = (TreeDropTargetDetails) dragEvent
  1137. .getDropTargetDetails();
  1138. Object itemIdOver = eventDetails.getItemIdOver();
  1139. if (!eventDetails.getTarget().areChildrenAllowed(itemIdOver)) {
  1140. return false;
  1141. }
  1142. // return true if directly over
  1143. return eventDetails.getDropLocation() == VerticalDropLocation.MIDDLE;
  1144. } catch (Exception e) {
  1145. return false;
  1146. }
  1147. }
  1148. }
  1149. /**
  1150. * Checks to parent (or parent hierarchy) for the item identifier given in
  1151. * constructor. If the parent is found, content is accepted.
  1152. */
  1153. @ClientCriterion(VTargetNodeIsChildOf.class)
  1154. public class TargetNodeIsChildOf extends ClientSideCriterion {
  1155. private Object parentItemId;
  1156. private int depthToCheck = 1;
  1157. /**
  1158. *
  1159. * @param parentItemId
  1160. */
  1161. public TargetNodeIsChildOf(Object parentItemId) {
  1162. this.parentItemId = parentItemId;
  1163. }
  1164. /**
  1165. *
  1166. * @param parentItemId
  1167. * @param depthToCheck
  1168. * the depth that tree is traversed upwards to seek for the
  1169. * parent, -1 means that the whole structure should be
  1170. * checked
  1171. */
  1172. public TargetNodeIsChildOf(Object parentItemId, int depthToCheck) {
  1173. this.parentItemId = parentItemId;
  1174. this.depthToCheck = depthToCheck;
  1175. }
  1176. private static final long serialVersionUID = 1L;
  1177. public boolean accepts(DragAndDropEvent dragEvent) {
  1178. try {
  1179. TreeDropTargetDetails eventDetails = (TreeDropTargetDetails) dragEvent
  1180. .getDropTargetDetails();
  1181. if (eventDetails.getItemIdOver() != null) {
  1182. Object itemIdOver = eventDetails.getItemIdOver();
  1183. Object parent2 = getParent(itemIdOver);
  1184. int i = 0;
  1185. while (parent2 != null && i < depthToCheck) {
  1186. if (parent2.equals(parentItemId)) {
  1187. return true;
  1188. }
  1189. i++;
  1190. }
  1191. }
  1192. return false;
  1193. } catch (Exception e) {
  1194. return false;
  1195. }
  1196. }
  1197. @Override
  1198. public void paintContent(PaintTarget target) throws PaintException {
  1199. super.paintContent(target);
  1200. target.addAttribute("depth", depthToCheck);
  1201. target.addAttribute("key", key(parentItemId));
  1202. }
  1203. }
  1204. }