org.eclipse.scout.rt.ui.rap.basic.tree.RwtScoutTree.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.scout.rt.ui.rap.basic.tree.RwtScoutTree.java

Source

/*******************************************************************************
 * Copyright (c) 2011 BSI Business Systems Integration AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     BSI Business Systems Integration AG - initial API and implementation
 *******************************************************************************/
package org.eclipse.scout.rt.ui.rap.basic.tree;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.scout.commons.StringUtility;
import org.eclipse.scout.commons.beans.IPropertyObserver;
import org.eclipse.scout.commons.dnd.TransferObject;
import org.eclipse.scout.commons.holders.Holder;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.client.ui.IDNDSupport;
import org.eclipse.scout.rt.client.ui.IEventHistory;
import org.eclipse.scout.rt.client.ui.action.keystroke.IKeyStroke;
import org.eclipse.scout.rt.client.ui.action.menu.IMenu;
import org.eclipse.scout.rt.client.ui.basic.tree.ITree;
import org.eclipse.scout.rt.client.ui.basic.tree.ITreeNode;
import org.eclipse.scout.rt.client.ui.basic.tree.TreeEvent;
import org.eclipse.scout.rt.client.ui.basic.tree.TreeListener;
import org.eclipse.scout.rt.ui.rap.RwtMenuUtility;
import org.eclipse.scout.rt.ui.rap.basic.RwtScoutComposite;
import org.eclipse.scout.rt.ui.rap.ext.MenuAdapterEx;
import org.eclipse.scout.rt.ui.rap.ext.tree.TreeEx;
import org.eclipse.scout.rt.ui.rap.extension.UiDecorationExtensionPoint;
import org.eclipse.scout.rt.ui.rap.form.fields.AbstractRwtScoutDndSupport;
import org.eclipse.scout.rt.ui.rap.keystroke.IRwtKeyStroke;
import org.eclipse.scout.rt.ui.rap.keystroke.RwtKeyStroke;
import org.eclipse.scout.rt.ui.rap.util.RwtUtility;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.widgets.MarkupValidator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;

/**
 * @since 3.8.0
 */
@SuppressWarnings("restriction")
public class RwtScoutTree extends RwtScoutComposite<ITree> implements IRwtScoutTree {
    private static final IScoutLogger LOG = ScoutLogManager.getLogger(RwtScoutTree.class);

    private P_ScoutTreeListener m_scoutTreeListener;

    private TreeViewer m_treeViewer;

    private boolean m_enabledFromScout = true;
    private IRwtKeyStroke[] m_keyStrokes;

    private String m_variant = "";

    public RwtScoutTree() {
    }

    public RwtScoutTree(String variant) {
        m_variant = variant;
    }

    @Override
    protected void initializeUi(Composite parent) {
        TreeViewer viewer = createTreeModel(parent);
        setUiTreeViewer(viewer);
        setUiField(viewer.getTree());

        initNodeHeight();
        initializeTreeModel();

        viewer.getTree().setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
        viewer.getTree().setData(MarkupValidator.MARKUP_VALIDATION_DISABLED, Boolean.TRUE);
        // listeners
        viewer.addSelectionChangedListener(new P_RwtSelectionListener());
        viewer.addTreeListener(new P_RwtExpansionListener());
        viewer.addDoubleClickListener(new P_RwtDoubleClickListener());

        P_RwtTreeListener treeListener = new P_RwtTreeListener();
        viewer.getTree().addListener(SWT.MouseDown, treeListener);
        viewer.getTree().addListener(SWT.MouseUp, treeListener);
        viewer.getTree().addListener(SWT.KeyUp, treeListener);
        viewer.getTree().addListener(SWT.MenuDetect, treeListener);

        getUiEnvironment().addKeyStroke(viewer.getTree(), new P_RwtKeyReturnAvoidDoubleClickListener(), false);
    }

    protected TreeViewer createTreeModel(Composite parent) {
        int style = isMultiSelect() ? SWT.MULTI : SWT.SINGLE;
        style |= SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL;
        TreeEx tree = getUiEnvironment().getFormToolkit().createTree(parent, style);
        if (StringUtility.hasText(m_variant)) {
            tree.setData(RWT.CUSTOM_VARIANT, m_variant);
        }
        tree.setLayoutDeferred(true);
        // Measure item call ends up in a layout error on windows 2000
        // tree.addListener(SWT.MeasureItem, new Listener(){
        // public void handleEvent(Event event) {
        // // event.height = (int)(event.gc.getFontMetrics().getHeight() *1.5);
        // }
        // });
        TreeViewer viewer = new TreeViewer(tree);
        viewer.setUseHashlookup(true);
        return viewer;
    }

    @Override
    public boolean isUiDisposed() {
        return getUiField() == null || getUiField().isDisposed();
    }

    protected void initializeTreeModel() {
        // model
        RwtScoutTreeModel model = createTreeModel();
        getUiTreeViewer().setContentProvider(model);
        getUiTreeViewer().setLabelProvider(model);
        getUiTreeViewer().setInput(model);
    }

    protected RwtScoutTreeModel createTreeModel() {
        return new RwtScoutTreeModel(getScoutObject(), this, getUiTreeViewer());
    }

    protected boolean isMultiSelect() {
        if (getScoutObject() != null) {
            return getScoutObject().isMultiSelect();
        } else {
            return false;
        }
    }

    protected void setUiTreeViewer(TreeViewer viewer) {
        m_treeViewer = viewer;
    }

    @Override
    public TreeViewer getUiTreeViewer() {
        return m_treeViewer;
    }

    protected ITreeContentProvider getContentProvider() {
        return (ITreeContentProvider) getUiTreeViewer().getContentProvider();
    }

    @Override
    public TreeEx getUiField() {
        return (TreeEx) super.getUiField();
    }

    protected void initNodeHeight() {
        int height = UiDecorationExtensionPoint.getLookAndFeel().getTreeNodeHeight();
        if (height >= 0) {
            getUiField().setData(RWT.CUSTOM_ITEM_HEIGHT, height);
        }
    }

    @Override
    protected void attachScout() {
        super.attachScout();
        if (getScoutObject() == null) {
            return;
        }
        if (m_scoutTreeListener == null) {
            m_scoutTreeListener = new P_ScoutTreeListener();
            getScoutObject().addUITreeListener(m_scoutTreeListener);
        }
        if (getScoutObject().isRootNodeVisible()) {
            setExpansionFromScout(getScoutObject().getRootNode());
        } else {
            for (ITreeNode node : getScoutObject().getRootNode().getFilteredChildNodes()) {
                setExpansionFromScout(node);
            }
        }
        setSelectionFromScout(getScoutObject().getSelectedNodes());
        setKeyStrokeFormScout();
        setNodeHeightFromScout();
        attachDndSupport();
        handleEventsFromRecentHistory();
    }

    protected void attachDndSupport() {
        if (UiDecorationExtensionPoint.getLookAndFeel().isDndSupportEnabled()) {
            new P_DndSupport(getScoutObject(), getScoutObject(), getUiField());
        }
    }

    private void handleEventsFromRecentHistory() {
        //handle events from recent history
        final IEventHistory<TreeEvent> h = getScoutObject().getEventHistory();
        if (h == null) {
            return;
        }

        getUiEnvironment().getDisplay().asyncExec(new Runnable() {
            @Override
            public void run() {
                for (TreeEvent e : h.getRecentEvents()) {
                    handleScoutTreeEventInUi(e);
                }
            }
        });
    }

    @Override
    protected void detachScout() {
        super.detachScout();
        if (getScoutObject() == null) {
            return;
        }
        if (m_scoutTreeListener != null) {
            getScoutObject().removeTreeListener(m_scoutTreeListener);
            m_scoutTreeListener = null;
        }
    }

    @Override
    public void setEnabledFromScout(boolean enabled) {
        m_enabledFromScout = enabled;
        if (getUiField() instanceof TreeEx) {
            (getUiField()).setReadOnly(!enabled);
        } else {
            getUiField().setEnabled(enabled);
        }
    }

    public boolean isEnabledFromScout() {
        return m_enabledFromScout;
    }

    protected void setExpansionFromScout(ITreeNode scoutNode) {
        if (scoutNode != null) {
            setExpansionFromScoutRec(scoutNode);
        }
    }

    private void setExpansionFromScoutRec(ITreeNode scoutNode) {
        boolean exp;
        if (scoutNode.getParentNode() == null) {
            exp = true;
        } else {
            exp = scoutNode.isExpanded();
        }
        ITreeNode[] filteredChildNodes = scoutNode.getFilteredChildNodes();
        boolean hasChilds = filteredChildNodes.length > 0;
        if (hasChilds && exp != getUiTreeViewer().getExpandedState(scoutNode)) {
            getUiTreeViewer().setExpandedState(scoutNode, exp);
        }
        if (exp) {
            for (ITreeNode childNode : filteredChildNodes) {
                setExpansionFromScoutRec(childNode);
            }
        }
    }

    protected void setSelectionFromScout(ITreeNode[] scoutNodes) {
        if (getUiField().isDisposed()) {
            return;
        }
        getUiTreeViewer().setSelection(new StructuredSelection(scoutNodes));
        updateScrollToSelectionFromScout();
    }

    protected void setKeyStrokeFormScout() {
        // remove old
        if (m_keyStrokes != null) {
            for (IRwtKeyStroke keyStroke : m_keyStrokes) {
                getUiEnvironment().removeKeyStroke(getUiField(), keyStroke);
            }
        }
        // add new
        ArrayList<IRwtKeyStroke> newKeyStrokes = new ArrayList<IRwtKeyStroke>();
        IKeyStroke[] scoutKeyStrokes = getScoutObject().getKeyStrokes();
        for (IKeyStroke scoutKeyStroke : scoutKeyStrokes) {
            IRwtKeyStroke[] strokes = RwtUtility.getKeyStrokes(scoutKeyStroke, getUiEnvironment());
            for (IRwtKeyStroke stroke : strokes) {
                getUiEnvironment().addKeyStroke(getUiField(), stroke, false);
                newKeyStrokes.add(stroke);
            }
        }
        m_keyStrokes = newKeyStrokes.toArray(new IRwtKeyStroke[newKeyStrokes.size()]);
    }

    protected void setNodeHeightFromScout() {
        int h = getScoutObject().getNodeHeightHint();
        if (h >= 0) {
            getUiField().setData(RWT.CUSTOM_ITEM_HEIGHT, h);
        }
        if (isCreated()) {
            getUiTreeViewer().refresh();
        }
    }

    /**
     * bsi ticket 95090: avoid multiple and excessive tree structure updates
     */
    protected void handleScoutTreeEventBatchInUi(List<TreeEvent> eventList) {
        //phase 1: collect all parent nodes that need to be refreshed and refresh once per node
        HashSet<ITreeNode> processedParentNodes = new HashSet<ITreeNode>();
        for (TreeEvent e : eventList) {
            ITreeNode parentNode = null;
            switch (e.getType()) {
            case TreeEvent.TYPE_NODES_INSERTED:
            case TreeEvent.TYPE_NODES_UPDATED:
            case TreeEvent.TYPE_NODES_DELETED:
            case TreeEvent.TYPE_CHILD_NODE_ORDER_CHANGED: {
                parentNode = e.getCommonParentNode();
                break;
            }
            case TreeEvent.TYPE_NODE_FILTER_CHANGED: {
                parentNode = getScoutObject().getRootNode();
                break;
            }
            }
            if (parentNode != null) {
                if (!processedParentNodes.contains(parentNode)) {
                    processedParentNodes.add(parentNode);
                    updateTreeStructureAndKeepSelection(parentNode);
                    setExpansionFromScout(parentNode);
                }
            }
        }
        //phase 2: apply remaining events
        for (TreeEvent e : eventList) {
            switch (e.getType()) {
            case TreeEvent.TYPE_REQUEST_FOCUS: {
                getUiField().setFocus();
                break;
            }
            case TreeEvent.TYPE_NODE_EXPANDED:
            case TreeEvent.TYPE_NODE_COLLAPSED: {
                setExpansionFromScout(e.getNode());
                break;
            }
            case TreeEvent.TYPE_NODES_SELECTED: {
                setSelectionFromScout(e.getNodes());
                break;
            }
            case TreeEvent.TYPE_SCROLL_TO_SELECTION: {
                scrollToSelection();
                break;
            }
            }
        }
    }

    private void updateTreeStructureAndKeepSelection(ITreeNode node) {
        if (getUiTreeViewer() != null && getUiTreeViewer().getTree() != null
                && !getUiTreeViewer().getTree().isDisposed()) {
            if (node == getScoutObject().getRootNode()) {
                getUiTreeViewer().refresh();
            } else {
                getUiTreeViewer().refresh(node);
            }
        }
    }

    protected void setSelectionFromRwt(final ITreeNode[] nodes) {
        if (getUpdateUiFromScoutLock().isAcquired()) {
            return;
        }
        if (getScoutObject() != null) {
            // notify Scout
            Runnable t = new Runnable() {
                @Override
                public void run() {
                    try {
                        addIgnoredScoutEvent(TreeEvent.class, "" + TreeEvent.TYPE_NODES_SELECTED);
                        getScoutObject().getUIFacade().setNodesSelectedFromUI(nodes);
                    } finally {
                        removeIgnoredScoutEvent(TreeEvent.class, "" + TreeEvent.TYPE_NODES_SELECTED);
                    }
                }
            };
            getUiEnvironment().invokeScoutLater(t, 0);
            // end notify
        }
    }

    protected void updateScrollToSelectionFromScout() {
        if (getScoutObject().isScrollToSelection()) {
            scrollToSelection();
        }
    }

    /**
     * @rn imo, 05.03.2009, tickets #73324, #73707, #74018
     * @rn imo, 18.11.2009, ticket #83255
     */
    protected void scrollToSelection() {
        if (getUiField() != null && !getUiField().isDisposed()) {
            getUiField().showSelection();
        }
    }

    @Override
    protected void handleScoutPropertyChange(String name, Object newValue) {
        if (name.equals(ITree.PROP_KEY_STROKES)) {
            setKeyStrokeFormScout();
        } else if (name.equals(ITree.PROP_SCROLL_TO_SELECTION)) {
            updateScrollToSelectionFromScout();
        } else if (name.equals(ITree.PROP_NODE_HEIGHT_HINT)) {
            setNodeHeightFromScout();
        }
        super.handleScoutPropertyChange(name, newValue);
    }

    protected void setExpansionFromUi(final ITreeNode node, final boolean expanded) {
        if (getUpdateUiFromScoutLock().isAcquired()) {
            return;
        }
        if (getScoutObject() != null) {
            // notify Scout
            Runnable t = new Runnable() {
                @Override
                public void run() {
                    if (node.isExpanded() != expanded) {
                        getScoutObject().getUIFacade().setNodeExpandedFromUI(node, expanded);
                    }
                }
            };
            getUiEnvironment().invokeScoutLater(t, 0);
            // end notify
        }
    }

    /**
     * model thread: scout table observer
     */
    protected boolean isHandleScoutTreeEvent(TreeEvent[] a) {
        for (TreeEvent element : a) {
            switch (element.getType()) {
            case TreeEvent.TYPE_REQUEST_FOCUS:
            case TreeEvent.TYPE_NODE_EXPANDED:
            case TreeEvent.TYPE_NODE_COLLAPSED:
            case TreeEvent.TYPE_NODES_INSERTED:
            case TreeEvent.TYPE_NODES_UPDATED:
            case TreeEvent.TYPE_NODES_DELETED:
            case TreeEvent.TYPE_NODE_FILTER_CHANGED:
            case TreeEvent.TYPE_NODES_SELECTED:
            case TreeEvent.TYPE_CHILD_NODE_ORDER_CHANGED:
            case TreeEvent.TYPE_SCROLL_TO_SELECTION: {
                return true;
            }
            }
        }
        return false;
    }

    protected void handleScoutTreeEventInUi(TreeEvent e) {
        if (isUiDisposed()) {
            return;
        }
        switch (e.getType()) {
        case TreeEvent.TYPE_NODES_INSERTED:
        case TreeEvent.TYPE_NODES_DELETED:
        case TreeEvent.TYPE_CHILD_NODE_ORDER_CHANGED: {
            updateTreeStructureAndKeepSelection(e.getCommonParentNode());
            setExpansionFromScout(e.getCommonParentNode());
            break;
        }
        case TreeEvent.TYPE_NODES_UPDATED: {
            //in case a virtual node was resolved, check if selection still valid
            ISelection oldSelection = getUiTreeViewer().getSelection();
            ISelection newSelection = new StructuredSelection(getScoutObject().getSelectedNodes());
            updateTreeStructureAndKeepSelection(e.getCommonParentNode());
            if (!newSelection.equals(oldSelection)) {
                getUiTreeViewer().setSelection(newSelection);
            }
            setExpansionFromScout(e.getCommonParentNode());
            break;
        }
        case TreeEvent.TYPE_NODE_FILTER_CHANGED: {
            updateTreeStructureAndKeepSelection(getScoutObject().getRootNode());
            setExpansionFromScout(getScoutObject().getRootNode());
            break;
        }
        case TreeEvent.TYPE_REQUEST_FOCUS: {
            getUiField().setFocus();
            break;
        }
        case TreeEvent.TYPE_NODE_EXPANDED:
        case TreeEvent.TYPE_NODE_COLLAPSED: {
            setExpansionFromScout(e.getNode());
            break;
        }
        case TreeEvent.TYPE_NODES_SELECTED: {
            setSelectionFromScout(e.getNodes());
            break;
        }
        case TreeEvent.TYPE_SCROLL_TO_SELECTION: {
            scrollToSelection();
            break;
        }
        }
    }

    protected void handleUiNodeClick(final ITreeNode node) {
        if (getScoutObject() != null) {
            if (node != null) {
                // notify Scout
                Runnable t = new Runnable() {
                    @Override
                    public void run() {
                        getScoutObject().getUIFacade().fireNodeClickFromUI(node);
                    }
                };
                getUiEnvironment().invokeScoutLater(t, 0);
                // end notify
            }
        }
    }

    protected void handleUiNodeAction(final ITreeNode node) {
        if (getScoutObject() != null) {
            // notify Scout
            Runnable t = new Runnable() {
                @Override
                public void run() {
                    getScoutObject().getUIFacade().fireNodeActionFromUI(node);
                }
            };
            getUiEnvironment().invokeScoutLater(t, 400);
            // end notify
        }
    }

    /**
     * TODO not used yet; attach to rwt tree with styled or html cells
     */
    protected void handleUiHyperlinkAction(final ITreeNode node, final URL url) {
        if (getScoutObject() != null && node != null) {
            // notify Scout
            Runnable t = new Runnable() {
                @Override
                public void run() {
                    getScoutObject().getUIFacade().fireHyperlinkActionFromUI(node, url);
                }
            };
            getUiEnvironment().invokeScoutLater(t, 0);
            // end notify
        }
    }

    // static convenience helpers
    public static TreePath scoutNodeToTreePath(ITreeNode scoutNode) {
        if (scoutNode == null) {
            return null;
        }
        Object[] path = getPathToRoot(scoutNode, 0);
        return new TreePath(path);
    }

    public static TreePath[] scoutNodesToTreePaths(ITreeNode[] scoutNodes) {
        if (scoutNodes == null) {
            return new TreePath[0];
        }
        TreePath[] paths = new TreePath[scoutNodes.length];
        for (int i = 0; i < scoutNodes.length; i++) {
            paths[i] = scoutNodeToTreePath(scoutNodes[i]);
        }
        return paths;
    }

    public static ITreeNode[] getPathToRoot(ITreeNode scoutNode, int depth) {
        ITreeNode[] retNodes;
        if (scoutNode == null) {
            if (depth == 0) {
                return null;
            } else {
                retNodes = new ITreeNode[depth];
            }
        } else {
            depth++;
            if (scoutNode.getParentNode() == null) {
                retNodes = new ITreeNode[depth];
            } else {
                retNodes = getPathToRoot(scoutNode.getParentNode(), depth);
            }
            retNodes[retNodes.length - depth] = scoutNode;
        }
        return retNodes;
    }

    /*
     * private inner classes
     */
    private class P_ScoutTreeListener implements TreeListener {
        @Override
        public void treeChanged(final TreeEvent e) {
            if (isHandleScoutTreeEvent(new TreeEvent[] { e })) {
                if (isIgnoredScoutEvent(TreeEvent.class, "" + e.getType())) {
                    return;
                }
                Runnable t = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            getUpdateUiFromScoutLock().acquire();
                            //
                            handleScoutTreeEventInUi(e);
                        } finally {
                            getUpdateUiFromScoutLock().release();
                        }
                    }
                };
                getUiEnvironment().invokeUiLater(t);
            }
        }

        @Override
        public void treeChangedBatch(final TreeEvent[] a) {
            if (isHandleScoutTreeEvent(a)) {
                final ArrayList<TreeEvent> filteredList = new ArrayList<TreeEvent>();
                for (TreeEvent element : a) {
                    if (!isIgnoredScoutEvent(TreeEvent.class, "" + element.getType())) {
                        filteredList.add(element);
                    }
                }
                if (filteredList.size() == 0) {
                    return;
                }
                Runnable t = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            getUpdateUiFromScoutLock().acquire();
                            if (!getUiField().isDisposed()) {
                                getUiField().setRedraw(false);
                            }
                            //
                            handleScoutTreeEventBatchInUi(filteredList);
                        } finally {
                            getUpdateUiFromScoutLock().release();
                            if (!getUiField().isDisposed()) {
                                getUiField().setRedraw(true);
                            }
                        }
                    }
                };
                getUiEnvironment().invokeUiLater(t);
            }
        }
    }// end private class

    private class P_RwtSelectionListener implements ISelectionChangedListener {
        @Override
        @SuppressWarnings("unchecked")
        public void selectionChanged(SelectionChangedEvent event) {
            if (isEnabledFromScout()) {
                StructuredSelection sel = (StructuredSelection) event.getSelection();
                ITreeNode[] nodes = (ITreeNode[]) sel.toList().toArray(new ITreeNode[sel.size()]);
                setSelectionFromRwt(nodes);
            }
        }
    } // end class P_RwtSelectionListener

    private Menu createMenu() {
        if (getUiField().getMenu() != null) {
            getUiField().getMenu().dispose();
            getUiField().setMenu(null);
        }

        Menu contextMenu = new Menu(getUiField().getShell(), SWT.POP_UP);
        contextMenu.addMenuListener(new P_ContextMenuListener());
        getUiField().setMenu(contextMenu);

        return contextMenu;
    }

    private void createAndShowMenu(Point location) {
        Menu menu = createMenu();
        showMenu(menu, location);
    }

    private void showMenu(Menu menu, Point location) {
        menu.setLocation(location);
        menu.setVisible(true);
    }

    /**
     * @param event
     */
    private void handleUiDoubleClick(StructuredSelection sel) {
        @SuppressWarnings("unchecked")
        ITreeNode[] nodes = (ITreeNode[]) sel.toList().toArray(new ITreeNode[sel.size()]);
        if (nodes != null && nodes.length == 1) {
            // if not leaf expand collapse
            if (!nodes[0].isLeaf()) {
                // invert expansion
                setExpansionFromUi(nodes[0], !getUiTreeViewer().getExpandedState(nodes[0]));
            } else {
                handleUiNodeAction(nodes[0]);
                if (getScoutObject().isCheckable()) {
                    handleUiNodeClick(nodes[0]);
                }
            }
        }
    }

    private class P_RwtTreeListener implements Listener {
        private static final long serialVersionUID = 1L;

        @Override
        public void handleEvent(Event event) {
            switch (event.type) {
            case SWT.MouseUp: {
                ViewerCell cell = getUiTreeViewer().getCell(new Point(event.x, event.y));
                if (cell != null && cell.getElement() instanceof ITreeNode) {
                    ITreeNode nodeToClick = (ITreeNode) cell.getElement();
                    handleUiNodeClick(nodeToClick);
                }
                break;
            }
            case SWT.KeyUp: {
                if (getScoutObject().isCheckable()) {
                    if (event.stateMask == 0) {
                        switch (event.keyCode) {
                        case ' ':
                            StructuredSelection sel = (StructuredSelection) getUiTreeViewer().getSelection();
                            @SuppressWarnings("unchecked")
                            ITreeNode[] nodes = (ITreeNode[]) sel.toList().toArray(new ITreeNode[sel.size()]);
                            if (nodes != null && nodes.length > 0) {
                                handleUiNodeClick(nodes[0]);
                            }
                            event.doit = false;
                            break;
                        }
                    }
                }
                break;
            }
            case SWT.Resize: {
                updateScrollToSelectionFromScout();
                break;
            }
            case SWT.MenuDetect: {
                Point eventPosition = new Point(event.x, event.y);
                createAndShowMenu(eventPosition);
                break;
            }
            }
        }
    }

    private class P_RwtExpansionListener implements ITreeViewerListener {
        @Override
        public void treeCollapsed(TreeExpansionEvent event) {
            setExpansionFromUi((ITreeNode) event.getElement(), false);
        }

        @Override
        public void treeExpanded(TreeExpansionEvent event) {
            setExpansionFromUi((ITreeNode) event.getElement(), true);
        }
    } // end class P_RwtExpansionListener

    /**
     * @rn sle, 03.12.2010, ticket #97056
     */
    private class P_RwtKeyReturnAvoidDoubleClickListener extends RwtKeyStroke {
        private static final long serialVersionUID = 1L;

        public P_RwtKeyReturnAvoidDoubleClickListener() {
            super(SWT.CR);
        }

        @Override
        public void handleUiAction(Event e) {
            //to avoid the postEvent(DoubleClickEvent) from Tree.WM_CHAR(...) set e.doit to false
            e.doit = false;
        }
    } // end class P_RwtKeyReturnAvoidDoubleClickListener

    private class P_RwtDoubleClickListener implements IDoubleClickListener {
        @Override
        public void doubleClick(DoubleClickEvent event) {
            if (event.getSelection() instanceof StructuredSelection) {
                StructuredSelection sel = (StructuredSelection) event.getSelection();
                handleUiDoubleClick(sel);
            }
        }
    }

    private class P_ContextMenuListener extends MenuAdapterEx {
        private static final long serialVersionUID = 1L;

        public P_ContextMenuListener() {
            super(RwtScoutTree.this.getUiTreeViewer().getTree(),
                    RwtScoutTree.this.getUiTreeViewer().getTree().getParent());
        }

        @Override
        public void menuShown(MenuEvent e) {
            super.menuShown(e);

            if (getScoutObject() == null || !isEnabledFromScout()) {
                return;
            }

            final boolean emptySpace = (getUiField().getContextItem() == null);
            IMenu[] menus = RwtMenuUtility.collectMenus(getScoutObject(), emptySpace, !emptySpace,
                    getUiEnvironment());

            Menu menu = ((Menu) e.getSource());
            RwtMenuUtility.fillContextMenu(menus, getUiEnvironment(), menu);
        }

    } // end class P_ContextMenuListener

    private class P_DndSupport extends AbstractRwtScoutDndSupport {
        public P_DndSupport(IPropertyObserver scoutObject, IDNDSupport scoutDndSupportable, Control control) {
            super(scoutObject, scoutDndSupportable, control, RwtScoutTree.this.getUiEnvironment());
        }

        @Override
        protected TransferObject handleUiDragRequest() {
            final Holder<TransferObject> result = new Holder<TransferObject>(TransferObject.class, null);
            Runnable t = new Runnable() {
                @Override
                public void run() {
                    TransferObject scoutTransferable = getScoutObject().getUIFacade().fireNodesDragRequestFromUI();
                    result.setValue(scoutTransferable);
                }
            };
            try {
                getUiEnvironment().invokeScoutLater(t, 20000).join(20000);
            } catch (InterruptedException e) {
                //nop
            }
            return result.getValue();
        }

        @Override
        protected void handleUiDropAction(DropTargetEvent event, final TransferObject scoutTransferObject) {
            Object dropTarget = event.item != null ? event.item.getData() : null;
            final ITreeNode node = dropTarget instanceof ITreeNode ? (ITreeNode) dropTarget : null;
            Runnable job = new Runnable() {
                @Override
                public void run() {
                    getScoutObject().getUIFacade().fireNodeDropActionFromUI(node, scoutTransferObject);
                }
            };
            getUiEnvironment().invokeScoutLater(job, 200);
        }
    }// end class P_DndSupport

}