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.

TreeConnector.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. * Copyright 2000-2013 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.client.ui.tree;
  17. import java.util.HashMap;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import com.google.gwt.aria.client.Roles;
  21. import com.google.gwt.dom.client.Element;
  22. import com.vaadin.client.ApplicationConnection;
  23. import com.vaadin.client.BrowserInfo;
  24. import com.vaadin.client.Paintable;
  25. import com.vaadin.client.TooltipInfo;
  26. import com.vaadin.client.UIDL;
  27. import com.vaadin.client.Util;
  28. import com.vaadin.client.VConsole;
  29. import com.vaadin.client.communication.StateChangeEvent;
  30. import com.vaadin.client.ui.AbstractComponentConnector;
  31. import com.vaadin.client.ui.VTree;
  32. import com.vaadin.client.ui.VTree.TreeNode;
  33. import com.vaadin.shared.ui.Connect;
  34. import com.vaadin.shared.ui.MultiSelectMode;
  35. import com.vaadin.shared.ui.tree.TreeConstants;
  36. import com.vaadin.shared.ui.tree.TreeState;
  37. import com.vaadin.ui.Tree;
  38. @Connect(Tree.class)
  39. public class TreeConnector extends AbstractComponentConnector implements
  40. Paintable {
  41. protected final Map<TreeNode, TooltipInfo> tooltipMap = new HashMap<TreeNode, TooltipInfo>();
  42. @Override
  43. protected void init() {
  44. getWidget().connector = this;
  45. }
  46. @Override
  47. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  48. if (!isRealUpdate(uidl)) {
  49. return;
  50. }
  51. getWidget().rendering = true;
  52. getWidget().client = client;
  53. if (uidl.hasAttribute("partialUpdate")) {
  54. handleUpdate(uidl);
  55. getWidget().rendering = false;
  56. return;
  57. }
  58. getWidget().paintableId = uidl.getId();
  59. getWidget().immediate = getState().immediate;
  60. getWidget().disabled = !isEnabled();
  61. getWidget().readonly = isReadOnly();
  62. getWidget().dragMode = uidl.hasAttribute("dragMode") ? uidl
  63. .getIntAttribute("dragMode") : 0;
  64. getWidget().isNullSelectionAllowed = uidl
  65. .getBooleanAttribute("nullselect");
  66. if (uidl.hasAttribute("alb")) {
  67. getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
  68. }
  69. getWidget().body.clear();
  70. // clear out any references to nodes that no longer are attached
  71. getWidget().clearNodeToKeyMap();
  72. tooltipMap.clear();
  73. TreeNode childTree = null;
  74. UIDL childUidl = null;
  75. for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
  76. childUidl = (UIDL) i.next();
  77. if ("actions".equals(childUidl.getTag())) {
  78. updateActionMap(childUidl);
  79. continue;
  80. } else if ("-ac".equals(childUidl.getTag())) {
  81. getWidget().updateDropHandler(childUidl);
  82. continue;
  83. }
  84. childTree = getWidget().new TreeNode();
  85. getConnection().getVTooltip().connectHandlersToWidget(childTree);
  86. updateNodeFromUIDL(childTree, childUidl, 1);
  87. getWidget().body.add(childTree);
  88. childTree.addStyleDependentName("root");
  89. childTree.childNodeContainer.addStyleDependentName("root");
  90. }
  91. if (childTree != null && childUidl != null) {
  92. boolean leaf = !childUidl.getTag().equals("node");
  93. childTree.addStyleDependentName(leaf ? "leaf-last" : "last");
  94. childTree.childNodeContainer.addStyleDependentName("last");
  95. }
  96. final String selectMode = uidl.getStringAttribute("selectmode");
  97. getWidget().selectable = !"none".equals(selectMode);
  98. getWidget().isMultiselect = "multi".equals(selectMode);
  99. if (getWidget().isMultiselect) {
  100. Roles.getTreeRole().setAriaMultiselectableProperty(
  101. getWidget().getElement(), true);
  102. if (BrowserInfo.get().isTouchDevice()) {
  103. // Always use the simple mode for touch devices that do not have
  104. // shift/ctrl keys (#8595)
  105. getWidget().multiSelectMode = MultiSelectMode.SIMPLE;
  106. } else {
  107. getWidget().multiSelectMode = MultiSelectMode.valueOf(uidl
  108. .getStringAttribute("multiselectmode"));
  109. }
  110. } else {
  111. Roles.getTreeRole().setAriaMultiselectableProperty(
  112. getWidget().getElement(), false);
  113. }
  114. getWidget().selectedIds = uidl.getStringArrayVariableAsSet("selected");
  115. // Update lastSelection and focusedNode to point to *actual* nodes again
  116. // after the old ones have been cleared from the body. This fixes focus
  117. // and keyboard navigation issues as described in #7057 and other
  118. // tickets.
  119. if (getWidget().lastSelection != null) {
  120. getWidget().lastSelection = getWidget().getNodeByKey(
  121. getWidget().lastSelection.key);
  122. }
  123. if (getWidget().focusedNode != null) {
  124. getWidget().setFocusedNode(
  125. getWidget().getNodeByKey(getWidget().focusedNode.key));
  126. }
  127. if (getWidget().lastSelection == null
  128. && getWidget().focusedNode == null
  129. && !getWidget().selectedIds.isEmpty()) {
  130. getWidget().setFocusedNode(
  131. getWidget().getNodeByKey(
  132. getWidget().selectedIds.iterator().next()));
  133. getWidget().focusedNode.setFocused(false);
  134. }
  135. // IE8 needs a hack to measure the tree again after update
  136. Util.forceIE8Redraw(getWidget().getElement());
  137. getWidget().rendering = false;
  138. }
  139. @Override
  140. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  141. super.onStateChanged(stateChangeEvent);
  142. // VTree does not implement Focusable
  143. getWidget().setTabIndex(getState().tabIndex);
  144. }
  145. @Override
  146. public VTree getWidget() {
  147. return (VTree) super.getWidget();
  148. }
  149. private void handleUpdate(UIDL uidl) {
  150. final TreeNode rootNode = getWidget().getNodeByKey(
  151. uidl.getStringAttribute("rootKey"));
  152. if (rootNode != null) {
  153. if (!rootNode.getState()) {
  154. // expanding node happened server side
  155. rootNode.setState(true, false);
  156. }
  157. String levelPropertyString = Roles.getTreeitemRole()
  158. .getAriaLevelProperty(rootNode.getElement());
  159. int levelProperty;
  160. try {
  161. levelProperty = Integer.valueOf(levelPropertyString);
  162. } catch (NumberFormatException e) {
  163. levelProperty = 1;
  164. VConsole.error(e);
  165. }
  166. renderChildNodes(rootNode, (Iterator) uidl.getChildIterator(),
  167. levelProperty + 1);
  168. }
  169. }
  170. /**
  171. * Registers action for the root and also for individual nodes
  172. *
  173. * @param uidl
  174. */
  175. private void updateActionMap(UIDL uidl) {
  176. final Iterator<?> it = uidl.getChildIterator();
  177. while (it.hasNext()) {
  178. final UIDL action = (UIDL) it.next();
  179. final String key = action.getStringAttribute("key");
  180. final String caption = action
  181. .getStringAttribute(TreeConstants.ATTRIBUTE_ACTION_CAPTION);
  182. String iconUrl = null;
  183. if (action.hasAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON)) {
  184. iconUrl = getConnection()
  185. .translateVaadinUri(
  186. action.getStringAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON));
  187. }
  188. getWidget().registerAction(key, caption, iconUrl);
  189. }
  190. }
  191. public void updateNodeFromUIDL(TreeNode treeNode, UIDL uidl, int level) {
  192. Roles.getTreeitemRole().setAriaLevelProperty(treeNode.getElement(),
  193. level);
  194. String nodeKey = uidl.getStringAttribute("key");
  195. treeNode.setText(uidl
  196. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION));
  197. treeNode.key = nodeKey;
  198. getWidget().registerNode(treeNode);
  199. if (uidl.hasAttribute("al")) {
  200. treeNode.actionKeys = uidl.getStringArrayAttribute("al");
  201. }
  202. if (uidl.getTag().equals("node")) {
  203. if (uidl.getChildCount() == 0) {
  204. treeNode.childNodeContainer.setVisible(false);
  205. } else {
  206. renderChildNodes(treeNode, (Iterator) uidl.getChildIterator(),
  207. level + 1);
  208. treeNode.childrenLoaded = true;
  209. }
  210. } else {
  211. treeNode.addStyleName(TreeNode.CLASSNAME + "-leaf");
  212. }
  213. if (uidl.hasAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE)) {
  214. treeNode.setNodeStyleName(uidl
  215. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE));
  216. }
  217. String description = uidl.getStringAttribute("descr");
  218. if (description != null) {
  219. tooltipMap.put(treeNode, new TooltipInfo(description));
  220. }
  221. if (uidl.getBooleanAttribute("expanded") && !treeNode.getState()) {
  222. treeNode.setState(true, false);
  223. }
  224. if (uidl.getBooleanAttribute("selected")) {
  225. treeNode.setSelected(true);
  226. // ensure that identifier is in selectedIds array (this may be a
  227. // partial update)
  228. getWidget().selectedIds.add(nodeKey);
  229. }
  230. String iconUrl = uidl
  231. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON);
  232. String iconAltText = uidl
  233. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT);
  234. treeNode.setIcon(iconUrl, iconAltText);
  235. }
  236. void renderChildNodes(TreeNode containerNode, Iterator<UIDL> i, int level) {
  237. containerNode.childNodeContainer.clear();
  238. containerNode.childNodeContainer.setVisible(true);
  239. while (i.hasNext()) {
  240. final UIDL childUidl = i.next();
  241. // actions are in bit weird place, don't mix them with children,
  242. // but current node's actions
  243. if ("actions".equals(childUidl.getTag())) {
  244. updateActionMap(childUidl);
  245. continue;
  246. }
  247. final TreeNode childTree = getWidget().new TreeNode();
  248. getConnection().getVTooltip().connectHandlersToWidget(childTree);
  249. updateNodeFromUIDL(childTree, childUidl, level);
  250. containerNode.childNodeContainer.add(childTree);
  251. if (!i.hasNext()) {
  252. childTree
  253. .addStyleDependentName(childTree.isLeaf() ? "leaf-last"
  254. : "last");
  255. childTree.childNodeContainer.addStyleDependentName("last");
  256. }
  257. }
  258. containerNode.childrenLoaded = true;
  259. }
  260. @Override
  261. public boolean isReadOnly() {
  262. return super.isReadOnly() || getState().propertyReadOnly;
  263. }
  264. @Override
  265. public TreeState getState() {
  266. return (TreeState) super.getState();
  267. }
  268. @Override
  269. public TooltipInfo getTooltipInfo(Element element) {
  270. TooltipInfo info = null;
  271. // Try to find a tooltip for a node
  272. if (element != getWidget().getElement()) {
  273. Object node = Util.findWidget(
  274. (com.google.gwt.user.client.Element) element,
  275. TreeNode.class);
  276. if (node != null) {
  277. TreeNode tnode = (TreeNode) node;
  278. if (tnode.isCaptionElement(element)) {
  279. info = tooltipMap.get(tnode);
  280. }
  281. }
  282. }
  283. // If no tooltip found for the node or if the target was not a node, use
  284. // the default tooltip
  285. if (info == null) {
  286. info = super.getTooltipInfo(element);
  287. }
  288. return info;
  289. }
  290. @Override
  291. public boolean hasTooltip() {
  292. /*
  293. * Item tooltips are not processed until updateFromUIDL, so we can't be
  294. * sure that there are no tooltips during onStateChange when this method
  295. * is used.
  296. */
  297. return true;
  298. }
  299. }