001// License: GPL. For details, see Readme.txt file.
002package org.openstreetmap.gui.jmapviewer.checkBoxTree;
003
004import java.awt.BorderLayout;
005import java.awt.event.MouseAdapter;
006
007import javax.swing.JFrame;
008import javax.swing.JScrollPane;
009import javax.swing.JTree;
010import javax.swing.event.TreeModelEvent;
011import javax.swing.event.TreeModelListener;
012import javax.swing.event.TreeSelectionEvent;
013import javax.swing.event.TreeSelectionListener;
014import javax.swing.tree.DefaultMutableTreeNode;
015import javax.swing.tree.DefaultTreeModel;
016
017import org.openstreetmap.gui.jmapviewer.AbstractLayer;
018import org.openstreetmap.gui.jmapviewer.Layer;
019import org.openstreetmap.gui.jmapviewer.LayerGroup;
020
021/**
022 * JTree for checkBox Tree Layers
023 * 
024 * @author galo
025 */
026public class CheckBoxTree extends JTree{
027    /** Serial Version UID */
028    private static final long serialVersionUID = 6943401106938034256L;
029    
030    private final CheckBoxNodeEditor editor;
031
032    public CheckBoxTree(AbstractLayer layer){
033        this(new CheckBoxNodeData(layer));
034    }
035    public CheckBoxTree(String rootName){
036        this(new CheckBoxNodeData(rootName));
037    }
038    public CheckBoxTree(CheckBoxNodeData root ){
039        this(new DefaultMutableTreeNode(root));
040    }
041    public CheckBoxTree(DefaultMutableTreeNode node){
042        super(new DefaultTreeModel(node));
043
044        final CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
045        setCellRenderer(renderer);
046
047        editor = new CheckBoxNodeEditor(this);
048        setCellEditor(editor);
049        setEditable(true);
050        
051        // listen for changes in the model (including check box toggles)
052        getModel().addTreeModelListener(new TreeModelListener() {
053            @Override
054            public void treeNodesChanged(final TreeModelEvent e) {
055                DefaultTreeModel model = (DefaultTreeModel)e.getSource();
056                Object[] nodes = e.getChildren();
057                DefaultMutableTreeNode node;
058                if(nodes==null||nodes.length==0){
059                    node = node(model.getRoot());
060                }else{
061                    node = node(nodes[0]);
062                }
063                nodeChanged(node);
064                repaint();
065            }
066
067            @Override
068            public void treeNodesInserted(final TreeModelEvent e) {
069                //System.out.println("nodes inserted");
070            }
071
072            @Override
073            public void treeNodesRemoved(final TreeModelEvent e) {
074                //System.out.println("nodes removed");
075            }
076
077            @Override
078            public void treeStructureChanged(final TreeModelEvent e) {
079                //System.out.println("structure changed");
080            }
081        });
082    }
083    public void addNodeListener(MouseAdapter listener){
084        editor.addNodeListener(listener);
085    }
086    public static void main(final String args[]) {
087        final DefaultMutableTreeNode root = new DefaultMutableTreeNode(new CheckBoxNodeData("Root",true));
088
089        final DefaultMutableTreeNode accessibility =
090            add(root, "Accessibility", true);
091        add(accessibility, "Move system caret with focus/selection changes", false);
092        add(accessibility, "Always expand alt text for images", true);
093        root.add(accessibility);
094
095        final DefaultMutableTreeNode browsing =
096            new DefaultMutableTreeNode(new CheckBoxNodeData("Browsing", null));
097        add(browsing, "Notify when downloads complete", true);
098        add(browsing, "Disable script debugging", true);
099        add(browsing, "Use AutoComplete", true);
100        add(browsing, "Browse in a new process", false);
101        root.add(browsing);
102
103        final CheckBoxTree tree = new CheckBoxTree(root);
104        ((DefaultMutableTreeNode)tree.getModel().getRoot()).add(new DefaultMutableTreeNode(new CheckBoxNodeData("gggg", null)));
105        ((DefaultTreeModel)tree.getModel()).reload();
106        // listen for changes in the selection
107        tree.addTreeSelectionListener(new TreeSelectionListener() {
108            @Override
109            public void valueChanged(final TreeSelectionEvent e) {
110                //System.out.println("selection changed");
111            }
112        });
113        // show the tree on screen
114        final JFrame frame = new JFrame("CheckBox Tree");
115        final JScrollPane scrollPane = new JScrollPane(tree);
116        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
117        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
118        frame.setSize(300, 150);
119        frame.setVisible(true);
120    }
121    private static Boolean childStatus(DefaultMutableTreeNode node){
122        Boolean status = data(node.getChildAt(0)).isSelected();
123        for(int i=1; i<node.getChildCount()&&status!=null; i++){
124            if(status != data(node.getChildAt(i)).isSelected()) return null;
125        }
126        return status;
127    }
128    private static void changeParents(DefaultMutableTreeNode node){
129        if(node!=null){
130            DefaultMutableTreeNode parent = node(node.getParent());
131            if(parent!=null){
132                CheckBoxNodeData dataParent = data(parent);
133                Boolean childStatus = childStatus(parent);
134                if(dataParent.isSelected()!=childStatus){
135                    dataParent.setSelected(childStatus);
136                    changeParents(parent);
137                }
138            }
139        }
140    }
141    private static void nodeChanged(DefaultMutableTreeNode node){
142        if(node!=null){
143            changeParents(node);
144            setChildrens(node, data(node).isSelected());
145        }
146    }
147    private static void setChildrens(DefaultMutableTreeNode node, Boolean value){
148        for(int i=0; i<node.getChildCount(); i++){
149            DefaultMutableTreeNode childNode = node(node.getChildAt(i));
150            if (data(childNode).isSelected() !=data(node).isSelected()){
151                data(childNode).setSelected(data(node).isSelected());
152                setChildrens(childNode, value);
153            }
154        }
155    }
156    public DefaultMutableTreeNode rootNode(){
157        return node(getModel().getRoot());
158    }
159    public LayerGroup rootLayer(){
160        return (LayerGroup)rootData().getAbstractLayer();
161    }
162    public CheckBoxNodeData rootData(){
163        return data(rootNode());
164    }
165    private static DefaultMutableTreeNode node(Object node){
166        return (DefaultMutableTreeNode)node;
167    }
168    public static CheckBoxNodeData data(DefaultMutableTreeNode node){
169        return node==null?null:(CheckBoxNodeData)node.getUserObject();
170    }
171    private static CheckBoxNodeData data(Object node){
172        return data(node(node));
173    }
174    private static DefaultMutableTreeNode add(final DefaultMutableTreeNode parent, final String text, final boolean checked){
175        final CheckBoxNodeData data = new CheckBoxNodeData(text, checked);
176        final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data);
177        parent.add(node);
178        return node;
179    }
180    public static CheckBoxNodeData createNodeData(AbstractLayer layer){
181        return new CheckBoxNodeData(layer);
182    }
183    public static DefaultMutableTreeNode createNode(AbstractLayer layer){
184        return new DefaultMutableTreeNode(createNodeData(layer));
185    }
186    /*public DefaultMutableTreeNode addLayerGroup(LayerGroup group){
187        if(group!=null){
188            if(group.getParent()==null){
189                return add(rootNode(), group);
190            }else{
191                DefaultMutableTreeNode parentGroup = searchNode(group.getParent());
192                if(parentGroup==null) parentGroup = addLayerGroup(group.getParent());
193                DefaultMutableTreeNode node = add(parentGroup, group);
194                return node;
195            }
196        }else return null;
197    }*/
198    public Layer addLayer(String name){
199        Layer layer = new Layer(name);
200        addLayer(layer);
201        return layer;
202    }
203    public DefaultMutableTreeNode addLayer(AbstractLayer layer){
204        if (layer!=null){
205            DefaultMutableTreeNode parent;
206            if(layer.getParent()==null){
207                rootLayer().add(layer);
208                parent = rootNode();
209            }else{
210                parent = searchNode(layer.getParent());
211                if(parent==null) parent=addLayer(layer.getParent());
212            }
213            return add(parent, layer);
214        }else return null;
215    }
216    public DefaultMutableTreeNode add(DefaultMutableTreeNode parent, final AbstractLayer layer){
217        layer.setVisible(data(parent).isSelected());
218        DefaultMutableTreeNode node = createNode(layer); 
219        parent.add(node);
220        ((DefaultTreeModel)getModel()).reload();
221        //System.out.println("Created node "+layer+" upper of "+data(parent));
222        return node;
223    }
224    public DefaultMutableTreeNode searchNode(AbstractLayer layer){
225        return searchNode(rootNode(), layer);
226    }
227    public DefaultMutableTreeNode searchNode(DefaultMutableTreeNode node, AbstractLayer layer){
228        CheckBoxNodeData data = CheckBoxTree.data(node);
229        if(data.getAbstractLayer() == layer) return node;
230        else{
231            DefaultMutableTreeNode found = null;
232            for(int i=0; i<node.getChildCount() && found==null; i++){
233                found = searchNode((DefaultMutableTreeNode)node.getChildAt(i), layer);
234            }
235            return found;
236        }
237    }
238}