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