com.vaadin.v7.client.ui.tree.TreeConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.vaadin.v7.client.ui.tree.TreeConnector.java

Source

/*
 * Copyright 2000-2018 Vaadin Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.vaadin.v7.client.ui.tree;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gwt.aria.client.Roles;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Paintable;
import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.MultiSelectMode;
import com.vaadin.v7.client.ui.AbstractLegacyComponentConnector;
import com.vaadin.v7.client.ui.VTree;
import com.vaadin.v7.client.ui.VTree.TreeNode;
import com.vaadin.v7.shared.ui.tree.TreeConstants;
import com.vaadin.v7.shared.ui.tree.TreeServerRpc;
import com.vaadin.v7.shared.ui.tree.TreeState;
import com.vaadin.v7.ui.Tree;

@Connect(Tree.class)
public class TreeConnector extends AbstractLegacyComponentConnector implements Paintable {

    protected final Map<TreeNode, TooltipInfo> tooltipMap = new HashMap<TreeNode, TooltipInfo>();

    @Override
    protected void init() {
        getWidget().connector = this;
    }

    @Override
    public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
        if (!isRealUpdate(uidl)) {
            return;
        }

        getWidget().rendering = true;

        getWidget().client = client;

        if (uidl.hasAttribute("partialUpdate")) {
            handleUpdate(uidl);

            // IE8 is no longer supported with Vaadin 8

            getWidget().rendering = false;
            return;
        }

        getWidget().paintableId = uidl.getId();

        getWidget().immediate = getState().immediate;

        getWidget().disabled = !isEnabled();
        getWidget().readonly = isReadOnly();

        getWidget().dragMode = uidl.hasAttribute("dragMode") ? uidl.getIntAttribute("dragMode") : 0;

        getWidget().isNullSelectionAllowed = uidl.getBooleanAttribute("nullselect");
        getWidget().isHtmlContentAllowed = uidl.getBooleanAttribute(TreeConstants.ATTRIBUTE_HTML_ALLOWED);

        if (uidl.hasAttribute("alb")) {
            getWidget().bodyActionKeys = uidl.getStringArrayAttribute("alb");
        }

        getWidget().body.clear();
        // clear out any references to nodes that no longer are attached
        getWidget().clearNodeToKeyMap();
        tooltipMap.clear();

        TreeNode childTree = null;
        UIDL childUidl = null;
        for (final Object child : uidl) {
            childUidl = (UIDL) child;
            if ("actions".equals(childUidl.getTag())) {
                updateActionMap(childUidl);
                continue;
            } else if ("-ac".equals(childUidl.getTag())) {
                getWidget().updateDropHandler(childUidl);
                continue;
            }
            childTree = getWidget().new TreeNode();
            getConnection().getVTooltip().connectHandlersToWidget(childTree);
            updateNodeFromUIDL(childTree, childUidl, 1);
            getWidget().body.add(childTree);
            childTree.addStyleDependentName("root");
            childTree.childNodeContainer.addStyleDependentName("root");
        }
        if (childTree != null && childUidl != null) {
            boolean leaf = !childUidl.getTag().equals("node");
            childTree.addStyleDependentName(leaf ? "leaf-last" : "last");
            childTree.childNodeContainer.addStyleDependentName("last");
        }
        final String selectMode = uidl.getStringAttribute("selectmode");
        getWidget().selectable = !"none".equals(selectMode);
        getWidget().isMultiselect = "multi".equals(selectMode);

        if (getWidget().isMultiselect) {
            Roles.getTreeRole().setAriaMultiselectableProperty(getWidget().getElement(), true);

            if (BrowserInfo.get().isTouchDevice()) {
                // Always use the simple mode for touch devices that do not have
                // shift/ctrl keys (#8595)
                getWidget().multiSelectMode = MultiSelectMode.SIMPLE;
            } else {
                getWidget().multiSelectMode = MultiSelectMode.valueOf(uidl.getStringAttribute("multiselectmode"));
            }
        } else {
            Roles.getTreeRole().setAriaMultiselectableProperty(getWidget().getElement(), false);
        }

        getWidget().selectedIds = uidl.getStringArrayVariableAsSet("selected");

        // Update lastSelection and focusedNode to point to *actual* nodes again
        // after the old ones have been cleared from the body. This fixes focus
        // and keyboard navigation issues as described in #7057 and other
        // tickets.
        if (getWidget().lastSelection != null) {
            getWidget().lastSelection = getWidget().getNodeByKey(getWidget().lastSelection.key);
        }

        if (getWidget().focusedNode != null) {

            Set<String> selectedIds = getWidget().selectedIds;

            // If the focused node is not between the selected nodes, we need to
            // refresh the focused node to prevent an undesired scroll. #12618.
            if (!selectedIds.isEmpty() && !selectedIds.contains(getWidget().focusedNode.key)) {
                String keySelectedId = selectedIds.iterator().next();

                TreeNode nodeToSelect = getWidget().getNodeByKey(keySelectedId);

                getWidget().setFocusedNode(nodeToSelect);
            } else {
                getWidget().setFocusedNode(getWidget().getNodeByKey(getWidget().focusedNode.key));
            }
        }

        if (getWidget().lastSelection == null && getWidget().focusedNode == null
                && !getWidget().selectedIds.isEmpty()) {
            getWidget().setFocusedNode(getWidget().getNodeByKey(getWidget().selectedIds.iterator().next()));
            getWidget().focusedNode.setFocused(false);
        }

        // IE8 is no longer supported with Vaadin 8

        getWidget().rendering = false;

    }

    @Override
    public void onStateChanged(StateChangeEvent stateChangeEvent) {
        super.onStateChanged(stateChangeEvent);
        // VTree does not implement Focusable
        getWidget().setTabIndex(getState().tabIndex);
    }

    @Override
    public VTree getWidget() {
        return (VTree) super.getWidget();
    }

    private void handleUpdate(UIDL uidl) {
        final TreeNode rootNode = getWidget().getNodeByKey(uidl.getStringAttribute("rootKey"));
        if (rootNode != null) {
            if (!rootNode.getState()) {
                // expanding node happened server side
                rootNode.setState(true, false);
            }
            String levelPropertyString = Roles.getTreeitemRole().getAriaLevelProperty(rootNode.getElement());
            int levelProperty;
            try {
                levelProperty = Integer.valueOf(levelPropertyString);
            } catch (NumberFormatException e) {
                levelProperty = 1;
                getLogger().log(Level.SEVERE, e.getMessage() == null ? "" : e.getMessage(), e);
            }

            renderChildNodes(rootNode, (Iterator) uidl.iterator(), levelProperty + 1);
        }
    }

    /**
     * Registers action for the root and also for individual nodes
     *
     * @param uidl
     */
    private void updateActionMap(UIDL uidl) {
        for (final Object child : uidl) {
            final UIDL action = (UIDL) child;
            final String key = action.getStringAttribute("key");
            final String caption = action.getStringAttribute(TreeConstants.ATTRIBUTE_ACTION_CAPTION);
            String iconUrl = null;
            if (action.hasAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON)) {
                iconUrl = getConnection()
                        .translateVaadinUri(action.getStringAttribute(TreeConstants.ATTRIBUTE_ACTION_ICON));
            }
            getWidget().registerAction(key, caption, iconUrl);
        }

    }

    public void updateNodeFromUIDL(TreeNode treeNode, UIDL uidl, int level) {
        Roles.getTreeitemRole().setAriaLevelProperty(treeNode.getElement(), level);

        String nodeKey = uidl.getStringAttribute("key");
        String caption = uidl.getStringAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION);
        if (getWidget().isHtmlContentAllowed) {
            treeNode.setHtml(caption);
        } else {
            treeNode.setText(caption);
        }
        treeNode.key = nodeKey;

        getWidget().registerNode(treeNode);

        if (uidl.hasAttribute("al")) {
            treeNode.actionKeys = uidl.getStringArrayAttribute("al");
        }

        if (uidl.getTag().equals("node")) {
            if (uidl.getChildCount() == 0) {
                treeNode.childNodeContainer.setVisible(false);
            } else {
                renderChildNodes(treeNode, (Iterator) uidl.iterator(), level + 1);
                treeNode.childrenLoaded = true;
            }
        } else {
            treeNode.addStyleName(TreeNode.CLASSNAME + "-leaf");
        }
        if (uidl.hasAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE)) {
            treeNode.setNodeStyleName(uidl.getStringAttribute(TreeConstants.ATTRIBUTE_NODE_STYLE));
        }

        String description = uidl.getStringAttribute("descr");
        if (description != null) {
            tooltipMap.put(treeNode, new TooltipInfo(description, null, treeNode));
        }

        if (uidl.getBooleanAttribute("expanded") && !treeNode.getState()) {
            treeNode.setState(true, false);
        }

        if (uidl.getBooleanAttribute("selected")) {
            treeNode.setSelected(true);
            // ensure that identifier is in selectedIds array (this may be a
            // partial update)
            getWidget().selectedIds.add(nodeKey);
        }

        String iconUrl = uidl.getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON);
        String iconAltText = uidl.getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT);
        treeNode.setIcon(iconUrl, iconAltText);
    }

    void renderChildNodes(TreeNode containerNode, Iterator<UIDL> i, int level) {
        containerNode.childNodeContainer.clear();
        containerNode.childNodeContainer.setVisible(true);
        while (i.hasNext()) {
            final UIDL childUidl = i.next();
            // actions are in bit weird place, don't mix them with children,
            // but current node's actions
            if ("actions".equals(childUidl.getTag())) {
                updateActionMap(childUidl);
                continue;
            }
            final TreeNode childTree = getWidget().new TreeNode();
            getConnection().getVTooltip().connectHandlersToWidget(childTree);
            updateNodeFromUIDL(childTree, childUidl, level);
            containerNode.childNodeContainer.add(childTree);
            if (!i.hasNext()) {
                childTree.addStyleDependentName(childTree.isLeaf() ? "leaf-last" : "last");
                childTree.childNodeContainer.addStyleDependentName("last");
            }
        }
        containerNode.childrenLoaded = true;
    }

    @Override
    public boolean isReadOnly() {
        return super.isReadOnly() || getState().propertyReadOnly;
    }

    @Override
    public TreeState getState() {
        return (TreeState) super.getState();
    }

    @Override
    public TooltipInfo getTooltipInfo(Element element) {

        TooltipInfo info = null;

        // Try to find a tooltip for a node
        if (element != getWidget().getElement()) {
            Object node = WidgetUtil.findWidget(element, TreeNode.class);

            if (node != null) {
                TreeNode tnode = (TreeNode) node;
                if (tnode.isCaptionElement(element)) {
                    info = tooltipMap.get(tnode);
                }
            }
        }

        // If no tooltip found for the node or if the target was not a node, use
        // the default tooltip
        if (info == null) {
            info = super.getTooltipInfo(element);
        }

        return info;
    }

    @Override
    public boolean hasTooltip() {
        /*
         * Item tooltips are not processed until updateFromUIDL, so we can't be
         * sure that there are no tooltips during onStateChange when this method
         * is used.
         */
        return true;
    }

    @Override
    protected void sendContextClickEvent(MouseEventDetails details, EventTarget eventTarget) {
        if (!Element.is(eventTarget)) {
            return;
        }

        Element e = Element.as(eventTarget);
        String key = null;

        if (getWidget().body.getElement().isOrHasChild(e)) {
            TreeNode t = WidgetUtil.findWidget(e, TreeNode.class);
            if (t != null) {
                key = t.key;
            }
        }

        getRpcProxy(TreeServerRpc.class).contextClick(key, details);

        WidgetUtil.clearTextSelection();
    }

    private static Logger getLogger() {
        return Logger.getLogger(TreeConnector.class.getName());
    }
}