diff java/src/uk/ac/qmul/eecs/ccmi/checkboxtree/CheckBoxTree.java @ 3:9e67171477bc

PHANTOM Omni Heptic device release
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Wed, 25 Apr 2012 17:09:09 +0100
parents
children d66dd5880081
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/checkboxtree/CheckBoxTree.java	Wed Apr 25 17:09:09 2012 +0100
@@ -0,0 +1,158 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ Copyright (C) 2011  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package uk.ac.qmul.eecs.ccmi.checkboxtree;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreePath;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXException;
+
+
+/**
+ * A JTree containing {@code CheckBoxTreeNode} nodes. The tree is built according to an XML file
+ * passed as argument to the constructor.
+ * The XML file hierarchical structure reflects the structure of the tree. XML tag name can be 
+ * either {@code selectable} or {@code unselectable}. The former representing tree nodes with a check
+ * box associated to it, and the latter a normal tree node, much as a {@link DefaultMutableTreeNode}. 
+ * Either tags must have an attribute {@code value}, representing the name of the node which will be displayed
+ * in the tree. Here is an example of a simple XML file, representing a tree with the tree root (non selectable) having
+ * three children, the first of which has, in turn, a child. all the descendants of the root are selectable, but the 
+ * first child.   
+ *    
+ * <pre>
+ * {@code
+ * 	<?xml version="1.0" encoding="utf-8"?>
+ *  <unselectable value="root">
+ *   <unselectable value="first child"/>
+ *   <selectable value="second child"/>
+ *   <selectable value "third child">
+ *     <selectable value="grand child"/>
+ *   </selectable>
+ *  </unselectable>
+ * }
+ * </pre>   
+ * 
+ * @see CheckBoxTreeNode 
+ */
+@SuppressWarnings("serial")
+public class CheckBoxTree extends JTree {
+	public CheckBoxTree(InputStream stream, SetProperties values){
+		super(new DefaultTreeModel(new DefaultMutableTreeNode()));
+		this.properties = values;
+		getAccessibleContext().setAccessibleName("tree");
+		treeModel =  (DefaultTreeModel)getModel();
+		setCellRenderer(new CheckBoxTreeCellRenderer());
+		buildTree(stream);
+		/* mouse listener to toggle the selected tree node */
+		addMouseListener(new MouseAdapter(){
+			@Override
+			public void mousePressed(MouseEvent e){
+				TreePath path = getPathForLocation(e.getX(),e.getY());
+				if(path == null)
+					return;
+				CheckBoxTreeNode treeNode = (CheckBoxTreeNode)path.getLastPathComponent();
+				toggleSelection(treeNode);
+			}
+		});
+	}
+	
+	/**
+	 * Builds a CheckBoxTree out of an xml file passed as argument. All the nodes 
+	 * of the tree are marked unchecked. 
+	 * 
+	 * @param stream an input stream to an xml file
+	 */
+	public CheckBoxTree(InputStream stream){
+		this(stream,null);
+	}
+	
+	/* use a sax parser to build the tree, if an error occurs during the parsing it just stops */
+	private void buildTree(InputStream stream){
+		SAXParserFactory factory = SAXParserFactory.newInstance();
+		try {
+			SAXParser saxParser = factory.newSAXParser();
+			XMLHandler handler = new XMLHandler((DefaultTreeModel)getModel(),properties);
+			saxParser.parse(stream, handler);
+		} catch (IOException e) {
+			e.printStackTrace();
+			return;
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+			return;
+		} catch (SAXException e) {
+			e.printStackTrace();
+			return;
+		}
+	}
+	
+	/**
+	 * Returns a reference to the properties holding which nodes of the tree are currently checked.  
+	 * @return the properties or {@value null} if the constructor with no properties was used. 
+	 */
+	public SetProperties getProperties(){
+		return properties;
+	}
+	
+	/**
+	 * Toggle the check box of the tree node passed as argument. If the tree node 
+	 * is not a leaf, then all its descendants will get the new value it. That is, 
+	 * if the tree node becomes selected as a result of the call, then all the descendants 
+	 * will become in turn selected (regardless of their previous state). If it becomes
+	 * unselected, the descendants will become unselected as well.   
+	 * 
+	 * @param treeNode the tree node to toggle
+	 */
+	public void toggleSelection(CheckBoxTreeNode treeNode){
+		if(treeNode.isSelectable()){
+			boolean selection = !treeNode.isSelected();
+			treeNode.setSelected(selection);
+			if(selection)
+				properties.add(treeNode.getPathAsString());
+			else
+				properties.remove(treeNode.getPathAsString());
+			treeModel.nodeChanged(treeNode);
+			if(!treeNode.isLeaf()){
+				for( @SuppressWarnings("unchecked")
+					Enumeration<CheckBoxTreeNode> enumeration = treeNode.depthFirstEnumeration(); enumeration.hasMoreElements();){
+					CheckBoxTreeNode t = enumeration.nextElement();
+					t.setSelected(selection);
+					if(selection)
+						properties.add(t.getPathAsString());
+					else
+						properties.remove(t.getPathAsString());
+					treeModel.nodeChanged(t);
+				}
+			}
+		}
+	}
+	
+	private SetProperties properties;
+	private DefaultTreeModel treeModel; 
+}