All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.mdl.tree.TreeUIDelegateClient Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * 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:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

// @(#) $Id: TreeUIDelegateClient.java 6895 2010-11-26 13:14:04Z webok $
package at.spardat.xma.mdl.tree;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

import at.spardat.xma.component.ComponentClient;
import at.spardat.xma.mdl.AttachmentExceptionClient;
import at.spardat.xma.mdl.ModelChangeEvent;
import at.spardat.xma.mdl.UIDelegateClient;
import at.spardat.xma.mdl.WModel;
import at.spardat.xma.mdl.tree.TreeWM.*;
import at.spardat.xma.page.EventAdapter;
import at.spardat.xma.page.PageClient;
import at.spardat.xma.util.Assert;

/**
 * Manages UI related aspects of a TreeWM.
 *
 * @author YSD, 04.05.2003 18:21:00
 */
public class TreeUIDelegateClient extends UIDelegateClient {


    private static final String             TREEITEM_DATA_KEY = "XMATreeItemDataKey";

    /**
     * The widget model this UI delegate belongs to
     */
    private TreeWMClient                    wModel_;

    /**
     * The SWT tree
     */
    private Tree                            tree_;

    /**
     * maps every TreeNode of the tree model to the
     * corresponding SWT TreeItem. The keys in the map
     * are the keys of the TreeNodes, the values are
     * SWT TreeItem objects.
     */
    private HashMap                         nodeItemMap_;

    /**
     * Keep track of whether we are updating the UI.
     */
    private boolean                         updatingUI_ = false;

    /**
     * The editable-property.
     */
    private boolean                         editable_ = true;

    /**
     * Enable-state
     */
    private boolean                         enabled_ = true;

    /**
     * This property is set by handleUIEvent() and is used by postClientEvent().
     * It holds the key of the node being the source of the TreeEvent.
     * It it only set on an expand and collapse event.
     *
     * This property is needed as the items itself can get disposed as the tree is rebuild.
     */
    private String                            eventSourceTreeNodeKey_;

    /**
     * Constructor
     *
     * @param wModel the widget model this UI delegate belongs to
     */
    public TreeUIDelegateClient (TreeWMClient wModel) {
        wModel_ = wModel;
    }

    /**
     * The attached Tree must delegate its SelectionEvent to this method in order
     * to keep track of the selection state of the tree.
     *
     * @see at.spardat.xma.mdl.UIDelegateClient#handleUIEvent(java.lang.Object, int)
     */
    public void handleUIEvent (Object event, int type) {
        if (!isUIAttached()) return;
        if (updatingUI_) {
            return;
        }
        if (Assert.ON) debugInvariant();
        realInvariant();

        updatingUI_ = true;
        try {
            // selection event
            if (event instanceof SelectionEvent && ((SelectionEvent)event).widget == tree_) {
                if(event instanceof TreeEvent){
                    TreeNode node = item2node((TreeItem)((SelectionEvent)event).item);
                    eventSourceTreeNodeKey_ = node.getKey();
                    if(type == SWT.Expand){
                        //if a TreeEvent occurred (expand) then a dummynode has to be deleted.
                        if(node.removeDummyNode()){  //if no dummynode exists then false
//                          a remove node rebuilds the complete tree. Therefore the event.item has to be updated.
                            ((SelectionEvent)event).item = node2item(node);
                            //as the dummynode is removed the lazy flag has to be set to true.
                            node.setLazy(true);
                        }
                    }
                }else{
                    // transmit the new selection state to the model
                    LinkedList          keys = new LinkedList();
                    if (checkedSelection()) {
                        // iterate over all nodes and collect the selected keys
                        Iterator iter = wModel_.nodes_.keySet().iterator();
                        while (iter.hasNext()) {
                            String key = (String)iter.next();
                            TreeItem ti = node2item(wModel_.getNode(key));
                            if (ti.getChecked()) keys.add(key);
                        }
                    } else {
                        TreeItem [] selected = tree_.getSelection();
                        for (int i = 0; i < selected.length; i++) {
                            keys.add(item2node(selected[i]).key_);
                        }
                    }
                    wModel_.handle (wModel_.new NewSelectionEvent (keys, true));
                }
            }

        } finally {
            updatingUI_ = false;
        }
        if (Assert.ON) debugInvariant();
        realInvariant();
    }

    /**
     * This method is to be called after PageClient.clientEventBase((SelectionEvent)event,type)
     * by the XMA framework.
     * It implements logic needed to expand and collapse dynamic trees after the PageClient expand/collapse event processing.
     * (As nodes of dymamic trees do not expand/collapse automatically after every manipulation)
     * Therfore right now this method is only implemented by this class.
     * @param event
     * @param type
     * @since version_number
     * @author s3460
     */
    public void postClientEvent (Object event, int type) {
        if(event instanceof TreeEvent && eventSourceTreeNodeKey_ != null){
            //TreeItem treeItem = (TreeItem)((TreeEvent)event).item;
            TreeItem treeItem = key2item(eventSourceTreeNodeKey_);
            if(type == SWT.Expand){
                treeItem.setExpanded(true);
                //after the first expand the node is not lazy anymore
                item2node(treeItem).setLazy(false);
            }else if(type == SWT.Collapse){
                treeItem.setExpanded(false);
            }
        }
    }

    /**
     * Returns a newly created HashSet which contains the keys of all expanded
     * nodes.
     */
    private HashSet getExpandedKeys () {
        HashSet expanded = new HashSet();
        TreeItem [] items = tree_.getItems();
        for (int i = 0; i < items.length; i++) {
            getExpandedKeysOfSubtree (items[i], expanded);
        }
        return expanded;
    }

    // adds all keys of the expanded nodes in a given subtree to the provided HashSet
    private void getExpandedKeysOfSubtree (TreeItem ti, HashSet expanded) {
        if (ti.getExpanded()) expanded.add(item2node(ti).key_);
        // subtree
        TreeItem [] items = ti.getItems();
        for (int i = 0; i < items.length; i++) {
            getExpandedKeysOfSubtree (items[i], expanded);
        }
    }

    /**
     * Expands all tree items whose keys are in the provided set of keys.
     */
    private void expand (Collection keys) {
        Iterator iter = keys.iterator();
        while (iter.hasNext()) {
            String key = (String) iter.next();
            TreeNode tn = wModel_.getNode(key);
            if (tn != null && !tn.isLazy()) {
                node2item(tn).setExpanded(true);
            }
        }
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#handleModelChangeEvent(at.spardat.xma.mdl.ModelChangeEvent)
     */
    public void handleModelChangeEvent (ModelChangeEvent event) {
        // if the UI is not attached, do nothing
        if (!isUIAttached()) return;

        updatingUI_ = true;
        try {
            if (event instanceof SelectionChangedEvent) {
                // selection changed
                selection2UI();
            } else if (event instanceof TreeChangedEvent || event instanceof RemoveNodeEvent) {
                // tree changed somehow of subtree has been deleted
                // rebuild the tree completely

                // first, we calculate the key of the top item; it may happen
                // that the node the item points to is already dead. But its key is still here.
                TreeItem topItem = null;
                String topKey = null;
                if (tree_.getItemCount() > 0) topItem = tree_.getTopItem();
                if (topItem != null) {
                    topKey = item2node(tree_.getTopItem()).key_;
                }

                // save the list of expanded nodes
                HashSet expandedKeys = getExpandedKeys();

                // rebuild the tree
                nodes2UI();
                selection2UI();

                // try to expand all that have been expanded before
                expand (expandedKeys);

                // reset position
                if (topKey != null) {
                    if (wModel_.containsKey(topKey)) {
                        tree_.setTopItem(node2item(wModel_.getNode(topKey)));
                    }
                }
            } else if (event instanceof ChangeNodeEvent) {
                // a particular node changed
                ChangeNodeEvent ev = (ChangeNodeEvent) event;
                TreeNode n = wModel_.getNode(ev.getKey());
                setItemFromNode(n, node2item(n));
            } else if (event instanceof AddNodeEvent) {
                // a node has been added to the tree
                AddNodeEvent            ev = (AddNodeEvent) event;
                TreeNode                newNode = wModel_.getNode(ev.getKey());
                int                     index = ev.getIndex();
                TreeItem                newTi = null;
                if (newNode.isRoot()) newTi = new TreeItem (tree_, SWT.NULL, index);
                else                  newTi = new TreeItem (node2item(newNode.getParent()), SWT.NULL, index);
                setItemFromNode(newNode, newTi);
                link(newNode, newTi);
            } else if (event instanceof ClearEvent) {
                // reset the tree
                tree_.removeAll();
                nodeItemMap_.clear();
            }
        } finally {
            updatingUI_ = false;
        }
        if (Assert.ON) debugInvariant();
        realInvariant();
    }


    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#getWModel()
     */
    public WModel getWModel() {
        return wModel_;
    }

    // see at.spardat.xma.mdl.UIDelegateClient.createControl()
    public Object createControl(Object parent) {
        if(!(parent instanceof Composite)) throw new IllegalArgumentException("parent must be a composite");
        Composite parentComp = (Composite)parent;
        if(wModel_.isMultiSelect()) {
            return new Tree(parentComp, SWT.BORDER|SWT.MULTI);
        } else {
            return new Tree(parentComp, SWT.BORDER|SWT.SINGLE);
        }
    }

    // see at.spardat.xma.mdl.UIDelegateClient.addListeners()
    public void addListeners(Object control, EventAdapter adapter) {
        if(control instanceof Tree) {
            ((Tree)control).addSelectionListener(adapter);
            ((Tree)control).addTreeListener(adapter);
        } else throw new IllegalArgumentException("unsupported control type: "+control.getClass().getName());
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#isUIAttached()
     */
    public boolean isUIAttached() {
        return tree_ != null;
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#attachUI(java.lang.Object, java.lang.Object)
     */
    public void attachUI(Object control, Object label) throws AttachmentExceptionClient {
        if (isUIAttached()) throw new IllegalStateException();
        boolean             attach = false;
        if (control instanceof Tree) {
            Tree            tree = (Tree)control;
            int             style = tree.getStyle();
            boolean         treeIsMulti = (style & SWT.CHECK) != 0 || (style & SWT.MULTI) != 0;
            if (treeIsMulti != wModel_.isMultiSelect())
                throw new AttachmentExceptionClient ("SWT tree and model selection cardinality does not match.");
            tree_ = tree;
            attach = true;
        }
        if (!attach) {
            throw new AttachmentExceptionClient ("TreeWM must be attached to a SWT-tree");
        }

        updatingUI_ = true;
        try {
            nodes2UI();
            selection2UI();
            //if the control is already disabled, set this at enabled_ - otherwise do not change enabled_
            //as in the Gen class the GUI designer disable property is set directly at the widget (only for some models)
            enabled_ = tree_.getEnabled()?enabled_:false;
            setEnabled(enabled_); //set the model's enabled state at the control
        } finally {
            updatingUI_ = false;
        }

        if (Assert.ON) debugInvariant();
        realInvariant();

    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#getUIControl()
     */
    public Object getUIControl() {
        if (!isUIAttached()) throw new IllegalStateException ();
        return tree_;
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#getUILabel()
     */
    public Object getUILabel() {
        if (!isUIAttached()) throw new IllegalStateException ();
        return null;
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#detachUI()
     */
    public void detachUI() {
        if (!isUIAttached()) return;
        updatingUI_ = true;
        try {
            enabled_ = tree_.getEnabled();
            tree_ = null;
            nodeItemMap_ = null;
        } finally {
            updatingUI_ = false;
        }
    }

    /**
     * This method first removes all items from the SWT tree. It then
     * rebuilds the complete SWT tree.
     */
    private void nodes2UI () {
        tree_.removeAll();
        nodeItemMap_ = new HashMap();
        TreeNode [] roots = wModel_.getRoots();
        for (int i=0; in and makes the SWT
     * root the last child of parent. If parent
     * is null, n is considered to be a root node.
     */
    private void addSubtree (TreeNode n, TreeItem parent) {
        TreeItem    ti;
        if (parent == null) ti = new TreeItem(tree_, SWT.NULL);
        else                ti = new TreeItem(parent, SWT.NULL);
        setItemFromNode (n, ti);
        link (n, ti);
        // recursively add childs
        if (n.childs_ != null) {
            for (int i=0, size=n.getChildCount(); iti from
     * TreeNode tn.
     */
    private void setItemFromNode (TreeNode tn, TreeItem ti) {
        ti.setText (tn.text_);
        // set image
        short imageId = tn.getImageId();
        if (imageId > 0) {
            Image image = ((PageClient)wModel_.getPage()).getComponent().getImage(imageId);
            if (image != null) ti.setImage(image);
        } else if(imageId==0&&ti.getImage()!=null) {
            ti.setImage((Image)null);
        }
        // colors
        NodeColor       fColor = tn.getForegroundColor();
        if (fColor != null) ti.setForeground(nodeColor2SwtColor(fColor));
        fColor = tn.getBackgroundColor();
        if (fColor != null) ti.setBackground(nodeColor2SwtColor(fColor));
    }

    /**
     * Transforms a NodeColor to a SWT-color
     */
    private Color nodeColor2SwtColor (NodeColor c) {
        ComponentClient     comp = ((PageClient)wModel_.getPage()).getComponent();
        return comp.getColor(c.getRed(), c.getGreen(), c.getBlue());
    }

    /**
     * Associates TreeNode and TreeItem so that we can navigate.
     */
    private void link (TreeNode tn, TreeItem ti) {
        nodeItemMap_.put(tn.key_, ti);
        ti.setData (TREEITEM_DATA_KEY, tn);
    }

    /**
     * Grabs the selection information from the model and updates the UI.
     */
    private void selection2UI () {
        if (checkedSelection()) {
            Iterator iter = wModel_.nodes_.keySet().iterator();
            while (iter.hasNext()) {
                String key = (String) iter.next();
                TreeNode tn = wModel_.getNode(key);
                TreeItem ti = node2item(tn);
                ti.setChecked(tn.isSelected());
            }
        } else {
            int numSelected = wModel_.getSelectionCount();
            if (numSelected == 0) tree_.deselectAll();
            else {
                TreeItem [] selectedItems = new TreeItem[numSelected];
                String []   keys  = wModel_.getSelection();
                for (int i=0; in is to be expanded, false if it collapsed.
     * @param fromUI true if this event emenates from the UI, false if
     *                is programmer initiated and the UI should be updated.
     */
    void setExpanded (TreeNode n, boolean expanded, boolean fromUI) {
        if (!fromUI) {
            // if this has not been triggered from the UI but from the programmer,
            // reflect it on the UI
            TreeItem ti = node2item(n);
            ti.setExpanded(expanded);
        }
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#isEnabled()
     */
    public boolean isEnabled () {
        if (isUIAttached()) {
            //always take the state from the widget (if it was set directly on it) as this is the old behavior
            enabled_ = tree_.isEnabled();
        }
        return enabled_;
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#setEnabled(boolean)
     */
    public void setEnabled (boolean what) {
        enabled_ = what;
        if (isUIAttached()) tree_.setEnabled(what);
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#isEditable()
     */
    public boolean isEditable () {
        return editable_;
    }

    /**
     * @see at.spardat.xma.mdl.UIDelegateClient#setEditable(boolean)
     */
    public void setEditable (boolean what) {
        editable_ = what;
    }

    /**
     * Not implemented - does nothing
     */
    public void setErrorColor (boolean inError) {
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy