0

I'm try to create a custom JTree using Checkboxes following the code from this post. But I'm running into three issues:

  1. When I try to change the name of one of the CheckBoxNodes (using the keyReleased handler), it wont update the text even though I am calling model.nodeChanged(node); this seems to have no effect unless I subsequently call tree.updateUI(); which I understand is not the proper way to do things (even though it works).

  2. When I try to insert a CheckBoxNode, it is always adding it to the first NameVector because something is changing the selection when I press the 'a' key as a trigger. I've run through the KeyMappings and tried to consume the event, but it seems to occur before keyReleased is called. How do I disable this behavior?

  3. How do I correctly update the NameVector's text to reflect the current number of selected CheckBoxNodes and total CheckboxNodes that I have in the NamedVector.toString() function after I add/remove/check/uncheck a CheckBoxNode?

Thanks in advance!

public class CheckBoxNodeTreeSample2 
{
    private JTree tree;
    
    public static void main(String... s)
    {
        new CheckBoxNodeTreeSample2();
    }

    public CheckBoxNodeTreeSample2() 
    {
        JFrame frame = new JFrame("CheckBox Tree");
    
        CheckBoxNode accessibilityOptions[] = 
        {
            new CheckBoxNode("Move system caret with focus/selection changes", false),
            new CheckBoxNode("Always expand alt text for images", true) 
        };
        CheckBoxNode browsingOptions[] = 
        {
            new CheckBoxNode("Notify when downloads complete", true),
            new CheckBoxNode("Disable script debugging", true),
            new CheckBoxNode("Use AutoComplete", true),
            new CheckBoxNode("Browse in a new process", false) 
        };
      
        Vector accessVector = new NamedVector("Accessibility",accessibilityOptions);
        Vector browseVector = new NamedVector("Browsing", browsingOptions);

        Object rootNodes[] = { accessVector, browseVector };
        Vector rootVector = new NamedVector("Root", rootNodes);
        tree = new JTree(rootVector);
    
        CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
        tree.setCellRenderer(renderer);
        tree.setCellEditor(new CheckBoxNodeEditor());
        tree.setEditable(true);
        //tree.setRootVisible(true);
           
        //expand the tree on launch
        for(int i=0;i<tree.getRowCount();++i) { tree.expandRow(i); }
        
        // listen for changes in the selection
        tree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(final TreeSelectionEvent e) {
                System.out.println(System.currentTimeMillis() + ": selection changed");
            }
        });

        // listen for changes in the model (including check box toggles)
        tree.getModel().addTreeModelListener(new TreeModelListener() 
        {
            @Override
            public void treeNodesChanged(TreeModelEvent e) {
                System.out.println(System.currentTimeMillis() + ": nodes changed");
            }

            @Override
            public void treeNodesInserted(final TreeModelEvent e) {
                System.out.println(System.currentTimeMillis() + ": nodes inserted");
            }

            @Override
            public void treeNodesRemoved(final TreeModelEvent e) {
                System.out.println(System.currentTimeMillis() + ": nodes removed");
            }

            @Override
            public void treeStructureChanged(final TreeModelEvent e) {
                System.out.println(System.currentTimeMillis() + ": structure changed");
            }
        });
        
        
        JScrollPane scrollPane = new JScrollPane(tree);
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
        frame.setSize(360, 260);
        frame.setVisible(true);
        
        tree.addKeyListener(new KeyListener() 
        {       
            public void keyTyped(KeyEvent e) { }
            public void keyPressed(KeyEvent e) { } 
            public void keyReleased(KeyEvent e) 
            {   
                TreePath treePath = tree.getSelectionPath();    
                DefaultMutableTreeNode node = (DefaultMutableTreeNode)treePath.getLastPathComponent();
                Object userObject = node.getUserObject();
                
                if(e.getKeyChar() == 'a') //add
                {               
                    //only add to NamedVectors
                    if(userObject instanceof NamedVector)
                    {                                       
                        DefaultTreeModel model = (DefaultTreeModel) tree.getModel();                       
                        model.insertNodeInto(new DefaultMutableTreeNode(new CheckBoxNode("ADDED NODE " + System.currentTimeMillis(), true)), node, node.getChildCount());
                    }
                }
                else if(e.getKeyChar() == 'e') //edit
                {               
                    //Only edit CheckBoxNodes
                    if(userObject instanceof CheckBoxNode)
                    {
                        CheckBoxNode checkBoxNode = (CheckBoxNode)userObject;
                        checkBoxNode.setText("Edited at "  + System.currentTimeMillis());
                        
                        DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
                        node.setUserObject(checkBoxNode);
                        model.nodeChanged(node);
                        //tree.updateUI(); 
                    }                   
                }   
                else if(e.getKeyChar() == 'r') //add
                {                           
                    //Only remove CheckBoxNodes
                    if(userObject instanceof CheckBoxNode)
                    {
                   
                        DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) (treePath.getLastPathComponent());
                        MutableTreeNode parent = (MutableTreeNode)(currentNode.getParent());
                        if (parent != null) 
                        {
                            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
                            model.removeNodeFromParent(currentNode);
                        } 
                    }                                
                }   
            } 
        }); 
    }
}

class CheckBoxPanel extends JPanel
{
    public JCheckBox checkBox;
    public JLabel label;

    public CheckBoxPanel() 
    { 
        super();
    
        checkBox = new JCheckBox();
        label = new JLabel();
    
        checkBox.setBorder(new EmptyBorder(0, 0, 0, 0));

        add(checkBox);
        add(label);
        
        //Passes KeyEvents to the tree
        checkBox.addKeyListener(new KeyListener() 
        {       
            public void keyTyped(KeyEvent e) { }
            public void keyPressed(KeyEvent e) { } 
            public void keyReleased(KeyEvent e) 
            { 
                getParent().dispatchEvent(e);
            } 
        });
    }
}

class CheckBoxNodeRenderer implements TreeCellRenderer 
{
    private CheckBoxPanel leafRenderer = new CheckBoxPanel();

    private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();

    Color selectionBorderColor, selectionForeground, selectionBackground,
                    textForeground, textBackground;

    protected CheckBoxPanel getLeafRenderer() 
    {
            return leafRenderer;
    }

    public CheckBoxNodeRenderer() {
            Font fontValue;
            fontValue = UIManager.getFont("Tree.font");
            if (fontValue != null) {
                    leafRenderer.checkBox.setFont(fontValue);
                    leafRenderer.label.setFont(fontValue);
            }
            Boolean booleanValue = (Boolean) UIManager
                            .get("Tree.drawsFocusBorderAroundIcon");
            leafRenderer.checkBox.setFocusPainted((booleanValue != null)
                            && (booleanValue.booleanValue()));

            selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
            selectionForeground = UIManager.getColor("Tree.selectionForeground");
            selectionBackground = UIManager.getColor("Tree.selectionBackground");
            textForeground = UIManager.getColor("Tree.textForeground");
            textBackground = UIManager.getColor("Tree.textBackground");
    }

    public Component getTreeCellRendererComponent(JTree tree, Object value,
                    boolean selected, boolean expanded, boolean leaf, int row,
                    boolean hasFocus) {

            Component returnValue;
            if (leaf) {

                    String stringValue = tree.convertValueToText(value, selected,
                                    expanded, leaf, row, false);
                    
                    leafRenderer.checkBox.setSelected(false);
                    leafRenderer.label.setText(stringValue);

                    leafRenderer.setEnabled(tree.isEnabled());

                    if (selected) {
                            leafRenderer.setForeground(selectionForeground);
                            leafRenderer.setBackground(selectionBackground);
                    } else {
                            leafRenderer.setForeground(textForeground);
                            leafRenderer.setBackground(textBackground);
                    }

                    if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
                            Object userObject = ((DefaultMutableTreeNode) value)
                                            .getUserObject();
                            if (userObject instanceof CheckBoxNode) {
                                    CheckBoxNode node = (CheckBoxNode) userObject;                                        
                                    leafRenderer.checkBox.setSelected(node.isSelected());
                                    leafRenderer.label.setText(node.getText());
                            }
                    }
                    returnValue = leafRenderer;
            } else {
                    returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree,
                                    value, selected, expanded, leaf, row, hasFocus);
            }
            return returnValue;
    }
}

class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {

    CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();

    ChangeEvent changeEvent = null;

    public Object getCellEditorValue() 
    {
            CheckBoxPanel checkBoxPanel = renderer.getLeafRenderer();
            CheckBoxNode checkBoxNode = new CheckBoxNode(checkBoxPanel.label.getText(), checkBoxPanel.checkBox.isSelected());
            return checkBoxNode;
    }

    public boolean isCellEditable(EventObject event) {
            boolean returnValue = false;
            if (event instanceof MouseEvent) {
                    MouseEvent mouseEvent = (MouseEvent) event;
                    JTree tree = (JTree)event.getSource();

                    TreePath path = tree.getPathForLocation(mouseEvent.getX(),
                                    mouseEvent.getY());
                    if (path != null) {
                            Object node = path.getLastPathComponent();
                            if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
                                    DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
                                    Object userObject = treeNode.getUserObject();
                                    returnValue = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));   

                            }
                    }
            }
            return returnValue;
    }

    public Component getTreeCellEditorComponent(JTree tree, Object value,
                    boolean selected, boolean expanded, boolean leaf, int row)  {

            Component editor = renderer.getTreeCellRendererComponent(tree, value,
                            true, expanded, leaf, row, true);

            // editor always selected / focused
            ItemListener itemListener = new ItemListener() {
                    public void itemStateChanged(ItemEvent itemEvent) {
                            if (stopCellEditing()) {
                                    fireEditingStopped();
                            }
                    }
            };
            
            if (editor instanceof CheckBoxPanel) 
            {
                ((CheckBoxPanel) editor).checkBox.addItemListener(itemListener);
            }

            return editor;
    }
}

class CheckBoxNode {
    
    String text;
    boolean selected;

    public CheckBoxNode(String text, boolean selected) {
            this.text = text;
            this.selected = selected;
    }

    public boolean isSelected() {
            return selected;
    }

    public void setSelected(boolean newValue) {
            selected = newValue;
    }

    public String getText() {
            return text;
    }

    public void setText(String newValue) {
            text = newValue;
    }

    public String toString() {
            return getClass().getName() + "[" + text + "/" + selected + "]";
    }
}

class NamedVector extends Vector 
{
    String name;

    public NamedVector(String name) {
            this.name = name;
    }

    public NamedVector(String name, Object elements[]) {
            this.name = name;
            for (int i = 0, n = elements.length; i < n; i++) {
                    add(elements[i]);
            }
    }

    public String toString() 
    {
        int selectedCount = 0;
        for(int i = 0; i < size(); i++)
        {                   
            if(get(i) instanceof CheckBoxNode)
            {
                if(((CheckBoxNode)get(i)).isSelected()) { selectedCount++; } 
            }
        }
        return name + " [" + selectedCount + "/" + size() + "]";
    }
}
cdubbs
  • 97
  • 12

0 Answers0