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