at.spardat.xma.mdl.tree.TreeUIDelegateClient Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* 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) {
}
}