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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.v7.client.ui.tree;
  17. import java.util.HashMap;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import java.util.Set;
  21. import java.util.logging.Level;
  22. import java.util.logging.Logger;
  23. import com.google.gwt.aria.client.Roles;
  24. import com.google.gwt.dom.client.Element;
  25. import com.google.gwt.dom.client.EventTarget;
  26. import com.vaadin.client.ApplicationConnection;
  27. import com.vaadin.client.BrowserInfo;
  28. import com.vaadin.client.Paintable;
  29. import com.vaadin.client.TooltipInfo;
  30. import com.vaadin.client.UIDL;
  31. import com.vaadin.client.WidgetUtil;
  32. import com.vaadin.client.communication.StateChangeEvent;
  33. import com.vaadin.shared.MouseEventDetails;
  34. import com.vaadin.shared.ui.Connect;
  35. import com.vaadin.shared.ui.MultiSelectMode;
  36. import com.vaadin.v7.client.ui.AbstractLegacyComponentConnector;
  37. import com.vaadin.v7.client.ui.VTree;
  38. import com.vaadin.v7.client.ui.VTree.TreeNode;
  39. import com.vaadin.v7.shared.ui.tree.TreeConstants;
  40. import com.vaadin.v7.shared.ui.tree.TreeServerRpc;
  41. import com.vaadin.v7.shared.ui.tree.TreeState;
  42. import com.vaadin.v7.ui.Tree;
  43. @Connect(Tree.class)
  44. public class TreeConnector extends AbstractLegacyComponentConnector
  45. implements Paintable {
  46. protected final Map<TreeNode, TooltipInfo> tooltipMap = new HashMap<TreeNode, TooltipInfo>();
  47. @Override
  48. protected void init() {
  49. getWidget().connector = this;
  50. }
  51. @Override
  52. public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
  53. if (!isRealUpdate(uidl)) {
  54. return;
  55. }
  56. getWidget().rendering = true;
  57. getWidget().client = client;
  58. if (uidl.hasAttribute("partialUpdate")) {
  59. handleUpdate(uidl);
  60. // IE8 is no longer supported with Vaadin 8
  61. getWidget().rendering = false;
  62. return;
  63. }
  64. getWidget().paintableId = uidl.getId();
  65. getWidget().immediate = getState().immediate;
  66. getWidget().disabled = !isEnabled();
  67. getWidget().readonly = isReadOnly();
  68. getWidget().dragMode = uidl.hasAttribute("dragMode")
  69. ? uidl.getIntAttribute("dragMode")
  70. : 0;
  71. getWidget().isNullSelectionAllowed = uidl
  72. .getBooleanAttribute("nullselect");
  73. getWidget().isHtmlContentAllowed = uidl
  74. .getBooleanAttribute(TreeConstants.ATTRIBUTE_HTML_ALLOWED);
  75. if (uidl.hasAttribute("alb")) {
  76. getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
  77. }
  78. getWidget().body.clear();
  79. // clear out any references to nodes that no longer are attached
  80. getWidget().clearNodeToKeyMap();
  81. tooltipMap.clear();
  82. TreeNode childTree = null;
  83. UIDL childUidl = null;
  84. for (final Object child : uidl) {
  85. childUidl = (UIDL) child;
  86. if ("actions".equals(childUidl.getTag())) {
  87. updateActionMap(childUidl);
  88. continue;
  89. } else if ("-ac".equals(childUidl.getTag())) {
  90. getWidget().updateDropHandler(childUidl);
  91. continue;
  92. }
  93. childTree = getWidget().new TreeNode();
  94. getConnection().getVTooltip().connectHandlersToWidget(childTree);
  95. updateNodeFromUIDL(childTree, childUidl, 1);
  96. getWidget().body.add(childTree);
  97. childTree.addStyleDependentName("root");
  98. childTree.childNodeContainer.addStyleDependentName("root");
  99. }
  100. if (childTree != null && childUidl != null) {
  101. boolean leaf = !childUidl.getTag().equals("node");
  102. childTree.addStyleDependentName(leaf ? "leaf-last" : "last");
  103. childTree.childNodeContainer.addStyleDependentName("last");
  104. }
  105. final String selectMode = uidl.getStringAttribute("selectmode");
  106. getWidget().selectable = !"none".equals(selectMode);
  107. getWidget().isMultiselect = "multi".equals(selectMode);
  108. if (getWidget().isMultiselect) {
  109. Roles.getTreeRole().setAriaMultiselectableProperty(
  110. getWidget().getElement(), true);
  111. if (BrowserInfo.get().isTouchDevice()) {
  112. // Always use the simple mode for touch devices that do not have
  113. // shift/ctrl keys (#8595)
  114. getWidget().multiSelectMode = MultiSelectMode.SIMPLE;
  115. } else {
  116. getWidget().multiSelectMode = MultiSelectMode
  117. .valueOf(uidl.getStringAttribute("multiselectmode"));
  118. }
  119. } else {
  120. Roles.getTreeRole().setAriaMultiselectableProperty(
  121. getWidget().getElement(), false);
  122. }
  123. getWidget().selectedIds = uidl.getStringArrayVariableAsSet("selected");
  124. // Update lastSelection and focusedNode to point to *actual* nodes again
  125. // after the old ones have been cleared from the body. This fixes focus
  126. // and keyboard navigation issues as described in #7057 and other
  127. // tickets.
  128. if (getWidget().lastSelection != null) {
  129. getWidget().lastSelection = getWidget()
  130. .getNodeByKey(getWidget().lastSelection.key);
  131. }
  132. if (getWidget().focusedNode != null) {
  133. Set<String> selectedIds = getWidget().selectedIds;
  134. // If the focused node is not between the selected nodes, we need to
  135. // refresh the focused node to prevent an undesired scroll. #12618.
  136. if (!selectedIds.isEmpty()
  137. && !selectedIds.contains(getWidget().focusedNode.key)) {
  138. String keySelectedId = selectedIds.iterator().next();
  139. TreeNode nodeToSelect = getWidget().getNodeByKey(keySelectedId);
  140. getWidget().setFocusedNode(nodeToSelect);
  141. } else {
  142. getWidget().setFocusedNode(
  143. getWidget().getNodeByKey(getWidget().focusedNode.key));
  144. }
  145. }
  146. if (getWidget().lastSelection == null && getWidget().focusedNode == null
  147. && !getWidget().selectedIds.isEmpty()) {
  148. getWidget().setFocusedNode(getWidget()
  149. .getNodeByKey(getWidget().selectedIds.iterator().next()));
  150. getWidget().focusedNode.setFocused(false);
  151. }
  152. // IE8 is no longer supported with Vaadin 8
  153. getWidget().rendering = false;
  154. }
  155. @Override
  156. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  157. super.onStateChanged(stateChangeEvent);
  158. // VTree does not implement Focusable
  159. getWidget().setTabIndex(getState().tabIndex);
  160. }
  161. @Override
  162. public VTree getWidget() {
  163. return (VTree) super.getWidget();
  164. }
  165. private void handleUpdate(UIDL uidl) {
  166. final TreeNode rootNode = getWidget()
  167. .getNodeByKey(uidl.getStringAttribute("rootKey"));
  168. if (rootNode != null) {
  169. if (!rootNode.getState()) {
  170. // expanding node happened server side
  171. rootNode.setState(true, false);
  172. }
  173. String levelPropertyString = Roles.getTreeitemRole()
  174. .getAriaLevelProperty(rootNode.getElement());
  175. int levelProperty;
  176. try {
  177. levelProperty = Integer.valueOf(levelPropertyString);
  178. } catch (NumberFormatException e) {
  179. levelProperty = 1;
  180. getLogger().log(Level.SEVERE,
  181. e.getMessage() == null ? "" : e.getMessage(), e);
  182. }
  183. renderChildNodes(rootNode, (Iterator) uidl.iterator(),
  184. levelProperty + 1);
  185. }
  186. }
  187. /**
  188. * Registers action for the root and also for individual nodes
  189. *
  190. * @param uidl
  191. */
  192. private void updateActionMap(UIDL uidl) {
  193. for (final Object child : uidl) {
  194. final UIDL action = (UIDL) child;
  195. final String key = action.getStringAttribute("key");
  196. final String caption = action
  197. .getStringAttribute(TreeConstants.ATTRIBUTE_ACTION_CAPTION);
  198. String iconUrl = null;
  199. if (action.hasAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON)) {
  200. iconUrl = getConnection()
  201. .translateVaadinUri(action.getStringAttribute(
  202. TreeConstants.ATTRIBUTE_ACTION_ICON));
  203. }
  204. getWidget().registerAction(key, caption, iconUrl);
  205. }
  206. }
  207. public void updateNodeFromUIDL(TreeNode treeNode, UIDL uidl, int level) {
  208. Roles.getTreeitemRole().setAriaLevelProperty(treeNode.getElement(),
  209. level);
  210. String nodeKey = uidl.getStringAttribute("key");
  211. String caption = uidl
  212. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION);
  213. if (getWidget().isHtmlContentAllowed) {
  214. treeNode.setHtml(caption);
  215. } else {
  216. treeNode.setText(caption);
  217. }
  218. treeNode.key = nodeKey;
  219. getWidget().registerNode(treeNode);
  220. if (uidl.hasAttribute("al")) {
  221. treeNode.actionKeys = uidl.getStringArrayAttribute("al");
  222. }
  223. if (uidl.getTag().equals("node")) {
  224. if (uidl.getChildCount() == 0) {
  225. treeNode.childNodeContainer.setVisible(false);
  226. } else {
  227. renderChildNodes(treeNode, (Iterator) uidl.iterator(),
  228. level + 1);
  229. treeNode.childrenLoaded = true;
  230. }
  231. } else {
  232. treeNode.addStyleName(TreeNode.CLASSNAME + "-leaf");
  233. }
  234. if (uidl.hasAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE)) {
  235. treeNode.setNodeStyleName(uidl
  236. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE));
  237. }
  238. String description = uidl.getStringAttribute("descr");
  239. if (description != null) {
  240. tooltipMap.put(treeNode,
  241. new TooltipInfo(description, null, treeNode));
  242. }
  243. if (uidl.getBooleanAttribute("expanded") && !treeNode.getState()) {
  244. treeNode.setState(true, false);
  245. }
  246. if (uidl.getBooleanAttribute("selected")) {
  247. treeNode.setSelected(true);
  248. // ensure that identifier is in selectedIds array (this may be a
  249. // partial update)
  250. getWidget().selectedIds.add(nodeKey);
  251. }
  252. String iconUrl = uidl
  253. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON);
  254. String iconAltText = uidl
  255. .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT);
  256. treeNode.setIcon(iconUrl, iconAltText);
  257. }
  258. void renderChildNodes(TreeNode containerNode, Iterator<UIDL> i, int level) {
  259. containerNode.childNodeContainer.clear();
  260. containerNode.childNodeContainer.setVisible(true);
  261. while (i.hasNext()) {
  262. final UIDL childUidl = i.next();
  263. // actions are in bit weird place, don't mix them with children,
  264. // but current node's actions
  265. if ("actions".equals(childUidl.getTag())) {
  266. updateActionMap(childUidl);
  267. continue;
  268. }
  269. final TreeNode childTree = getWidget().new TreeNode();
  270. getConnection().getVTooltip().connectHandlersToWidget(childTree);
  271. updateNodeFromUIDL(childTree, childUidl, level);
  272. containerNode.childNodeContainer.add(childTree);
  273. if (!i.hasNext()) {
  274. childTree.addStyleDependentName(
  275. childTree.isLeaf() ? "leaf-last" : "last");
  276. childTree.childNodeContainer.addStyleDependentName("last");
  277. }
  278. }
  279. containerNode.childrenLoaded = true;
  280. }
  281. @Override
  282. public boolean isReadOnly() {
  283. return super.isReadOnly() || getState().propertyReadOnly;
  284. }
  285. @Override
  286. public TreeState getState() {
  287. return (TreeState) super.getState();
  288. }
  289. @Override
  290. public TooltipInfo getTooltipInfo(Element element) {
  291. TooltipInfo info = null;
  292. // Try to find a tooltip for a node
  293. if (element != getWidget().getElement()) {
  294. Object node = WidgetUtil.findWidget(element, TreeNode.class);
  295. if (node != null) {
  296. TreeNode tnode = (TreeNode) node;
  297. if (tnode.isCaptionElement(element)) {
  298. info = tooltipMap.get(tnode);
  299. }
  300. }
  301. }
  302. // If no tooltip found for the node or if the target was not a node, use
  303. // the default tooltip
  304. if (info == null) {
  305. info = super.getTooltipInfo(element);
  306. }
  307. return info;
  308. }
  309. @Override
  310. public boolean hasTooltip() {
  311. /*
  312. * Item tooltips are not processed until updateFromUIDL, so we can't be
  313. * sure that there are no tooltips during onStateChange when this method
  314. * is used.
  315. */
  316. return true;
  317. }
  318. @Override
  319. protected void sendContextClickEvent(MouseEventDetails details,
  320. EventTarget eventTarget) {
  321. if (!Element.is(eventTarget)) {
  322. return;
  323. }
  324. Element e = Element.as(eventTarget);
  325. String key = null;
  326. if (getWidget().body.getElement().isOrHasChild(e)) {
  327. TreeNode t = WidgetUtil.findWidget(e, TreeNode.class);
  328. if (t != null) {
  329. key = t.key;
  330. }
  331. }
  332. getRpcProxy(TreeServerRpc.class).contextClick(key, details);
  333. WidgetUtil.clearTextSelection();
  334. }
  335. private static Logger getLogger() {
  336. return Logger.getLogger(TreeConnector.class.getName());
  337. }
  338. }