How to add checkbox to JTree node to manage multiselection?

后端 未结 1 443
忘掉有多难
忘掉有多难 2020-12-01 17:24

I want to build JTree which has nodes that contain check box + icon + data and tree selection algorithm.

相关标签:
1条回答
  • 2020-12-01 17:53

    This is full example that demonstrate how to add checkbox to Jtree node. I used JTree with nodes based on File system content.

    I usesd also AddCheckBoxToTree.CheckTreeManager class to manage selection or semi selection options.

    Use

    public AddCheckBoxToTree.CheckTreeManager getCheckTreeManager() {
        return checkTreeManager;
    }
    

    method to play with selection tree path.

    for example:

    // clear all selected path in order 
        TreePath[] paths=getCheckTreeManager().getSelectionModel().getSelectionPaths();
        if(paths != null){
            for(TreePath tp : paths){
                getCheckTreeManager().getSelectionModel().removeSelectionPath(tp);
            }
        }
    

    here I pasted all code that do that:

    package com.demo.tree.checkbox;
    
    
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Graphics;
    import java.awt.event.MouseEvent;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    import java.awt.event.WindowListener;
    import java.io.File;
    import java.util.Vector;
    
    import javax.swing.Icon;
    import javax.swing.ImageIcon;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.JScrollPane;
    import javax.swing.JTree;
    import javax.swing.SwingUtilities;
    import javax.swing.ToolTipManager;
    import javax.swing.UIManager;
    import javax.swing.event.TreeExpansionEvent;
    import javax.swing.event.TreeExpansionListener;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
    import javax.swing.tree.TreeCellRenderer;
    import javax.swing.tree.TreePath;
    import javax.swing.tree.TreeSelectionModel;
    
    
    
    public class FileTreeViewer  extends JFrame {
    
    private static final long serialVersionUID = 1L;
    public static final ImageIcon ICON_COMPUTER =  new ImageIcon("");
    public static final ImageIcon ICON_DISK =  new ImageIcon("defaults1.png");
    public static final ImageIcon ICON_FOLDER =   new ImageIcon("fol_orig.png");
    public static final ImageIcon ICON_EXPANDEDFOLDER =  new ImageIcon("folder_open.png");
    
    protected JTree  m_tree;
    protected DefaultTreeModel m_model;
    
    AddCheckBoxToTree AddCh = new AddCheckBoxToTree();
    
    private AddCheckBoxToTree.CheckTreeManager checkTreeManager;
    
    
    protected TreePath m_clickedPath;
    
    public FileTreeViewer()
    {
        super("Demo tree check box");
        setSize(400, 300);
    
        DefaultMutableTreeNode top = new DefaultMutableTreeNode(
                new IconData(ICON_COMPUTER, null, "Computer"));
    
        DefaultMutableTreeNode node;
        File[] roots = File.listRoots();
        for (int k=0; k<roots.length; k++)
        {
            node = new DefaultMutableTreeNode(new IconData(ICON_DISK, null, new FileNode(roots[k])));
            top.add(node);
            node.add(new DefaultMutableTreeNode( new Boolean(true) ));
        }
    
        m_model = new DefaultTreeModel(top);
    
        m_tree = new JTree(m_model){
            public String getToolTipText(MouseEvent ev) 
            {
                if(ev == null)
                    return null;
                TreePath path = m_tree.getPathForLocation(ev.getX(), 
                        ev.getY());
                if (path != null)
                {
                    FileNode fnode = getFileNode(getTreeNode(path));
                    if (fnode==null)
                        return null;
                    File f = fnode.getFile();
                    return (f==null ? null : f.getPath());
                }
                return null;
            }
        };
    
        ToolTipManager.sharedInstance().registerComponent(m_tree);
    
        m_tree.putClientProperty("JTree.lineStyle", "Angled");
    
        TreeCellRenderer renderer = new IconCellRenderer();
        m_tree.setCellRenderer(renderer);
    
        m_tree.addTreeExpansionListener(new  DirExpansionListener());
    
        m_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 
        m_tree.setShowsRootHandles(true); 
        m_tree.setEditable(false);
    
    
        checkTreeManager = AddCh.new CheckTreeManager(m_tree, null);
    
    
    
        JScrollPane s = new JScrollPane();
        s.getViewport().add(m_tree);
        getContentPane().add(s, BorderLayout.CENTER);
    
    
        WindowListener wndCloser = new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                System.exit(0);
            }
        };
    
        addWindowListener(wndCloser);
    
        setVisible(true);
    }
    
    DefaultMutableTreeNode getTreeNode(TreePath path)
    {
        return (DefaultMutableTreeNode)(path.getLastPathComponent());
    }
    
    FileNode getFileNode(DefaultMutableTreeNode node)
    {
        if (node == null)
            return null;
        Object obj = node.getUserObject();
        if (obj instanceof IconData)
            obj = ((IconData)obj).getObject();
        if (obj instanceof FileNode)
            return (FileNode)obj;
        else
            return null;
    }
    
    public AddCheckBoxToTree.CheckTreeManager getCheckTreeManager() {
        return checkTreeManager;
    }
    
    // Make sure expansion is threaded and updating the tree model
    // only occurs within the event dispatching thread.
    class DirExpansionListener implements TreeExpansionListener
    {
        public void treeExpanded(TreeExpansionEvent event)
        {
            final DefaultMutableTreeNode node = getTreeNode(
                    event.getPath());
            final FileNode fnode = getFileNode(node);
    
            Thread runner = new Thread() 
            {
                public void run() 
                {
                    if (fnode != null && fnode.expand(node)) 
                    {
                        Runnable runnable = new Runnable() 
                        {
                            public void run() 
                            {
                                m_model.reload(node);
                            }
                        };
                        SwingUtilities.invokeLater(runnable);
                    }
                }
            };
            runner.start();
        }
    
        public void treeCollapsed(TreeExpansionEvent event) {}
    }
    
    
    
    public static void main(String argv[]) 
    {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch (Exception evt) {}
        new FileTreeViewer();
    }
    }
    
    class IconCellRenderer extends JLabel implements TreeCellRenderer{
    protected Color m_textSelectionColor;
    protected Color m_textNonSelectionColor;
    protected Color m_bkSelectionColor;
    protected Color m_bkNonSelectionColor;
    protected Color m_borderSelectionColor;
    
    protected boolean m_selected;
    
    public IconCellRenderer()
    {
        super();
        m_textSelectionColor = UIManager.getColor(
                "Tree.selectionForeground");
        m_textNonSelectionColor = UIManager.getColor(
                "Tree.textForeground");
        m_bkSelectionColor = UIManager.getColor(
                "Tree.selectionBackground");
        m_bkNonSelectionColor = UIManager.getColor(
                "Tree.textBackground");
        m_borderSelectionColor = UIManager.getColor(
                "Tree.selectionBorderColor");
        setOpaque(false);
    }
    
    public Component getTreeCellRendererComponent(JTree tree, 
            Object value, boolean sel, boolean expanded, boolean leaf, 
            int row, boolean hasFocus) 
    
    {
        DefaultMutableTreeNode node = 
                (DefaultMutableTreeNode)value;
        Object obj = node.getUserObject();
        setText(obj.toString());
    
        if (obj instanceof Boolean)
            setText("Retrieving data...");
    
        if (obj instanceof IconData)
        {
            IconData idata = (IconData)obj;
            if (expanded)
                setIcon(idata.getExpandedIcon());
            else
                setIcon(idata.getIcon());
        }
        else
            setIcon(null);
    
        setFont(tree.getFont());
        setForeground(sel ? m_textSelectionColor : 
            m_textNonSelectionColor);
        setBackground(sel ? m_bkSelectionColor : 
            m_bkNonSelectionColor);
        m_selected = sel;
        return this;
    }
    
    public void paintComponent(Graphics g) 
    {
        Color bColor = getBackground();
        Icon icon = getIcon();
    
        g.setColor(bColor);
        int offset = 0;
        if(icon != null && getText() != null) 
            offset = (icon.getIconWidth() + getIconTextGap());
        g.fillRect(offset, 0, getWidth() - 1 - offset,
                getHeight() - 1);
    
        if (m_selected) 
        {
            g.setColor(m_borderSelectionColor);
            g.drawRect(offset, 0, getWidth()-1-offset, getHeight()-1);
        }
    
        super.paintComponent(g);
    }
    }
    
    class IconData {
    protected Icon   m_icon;
    protected Icon   m_expandedIcon;
    protected Object m_data;
    
    public IconData(Icon icon, Object data)
    {
        m_icon = icon;
        m_expandedIcon = null;
        m_data = data;
    }
    
    public IconData(Icon icon, Icon expandedIcon, Object data)
    {
        m_icon = icon;
        m_expandedIcon = expandedIcon;
        m_data = data;
    }
    
    public Icon getIcon() 
    { 
        return m_icon;
    }
    
    public Icon getExpandedIcon() 
    { 
        return m_expandedIcon!=null ? m_expandedIcon : m_icon;
    }
    
    public Object getObject() 
    { 
        return m_data;
    }
    
    public String toString() 
    { 
        return m_data.toString();
    }
    }
    
    class FileNode {
    protected File m_file;
    
    public FileNode(File file)
    {
        m_file = file;
    }
    
    public File getFile() 
    { 
        return m_file;
    }
    
    public String toString() 
    { 
        return m_file.getName().length() > 0 ? m_file.getName() : 
            m_file.getPath();
    }
    
    public boolean expand(DefaultMutableTreeNode parent){
        DefaultMutableTreeNode flag = (DefaultMutableTreeNode)parent.getFirstChild();
        if (flag==null)    // No flag
            return false;
        Object obj = flag.getUserObject();
        if (!(obj instanceof Boolean))
            return false;      // Already expanded
    
        parent.removeAllChildren();  // Remove Flag
    
        File[] files = listFiles();
        if (files == null)
            return true;
    
        Vector<FileNode> v = new Vector<FileNode>();
    
        for (int k=0; k<files.length; k++){
            File f = files[k];
            if (!(f.isDirectory()))
                continue;
    
            FileNode newNode = new FileNode(f);
    
            boolean isAdded = false;
            for (int i=0; i<v.size(); i++)
            {
                FileNode nd = (FileNode)v.elementAt(i);
                if (newNode.compareTo(nd) < 0)
                {
                    v.insertElementAt(newNode, i);
                    isAdded = true;
                    break;
                }
            }
            if (!isAdded)
                v.addElement(newNode);
        }
    
        for (int i=0; i<v.size(); i++){
            FileNode nd = (FileNode)v.elementAt(i);
            IconData idata = new IconData(FileTreeViewer.ICON_FOLDER, FileTreeViewer.ICON_EXPANDEDFOLDER, nd);
            DefaultMutableTreeNode node = new 
                    DefaultMutableTreeNode(idata);
            parent.add(node);
    
            if (nd.hasSubDirs())
                node.add(new DefaultMutableTreeNode( 
                        new Boolean(true) ));
        }
    
        return true;
    }
    
    public boolean hasSubDirs(){
        File[] files = listFiles();
        if (files == null)
            return false;
        for (int k=0; k<files.length; k++)
        {
            if (files[k].isDirectory())
                return true;
        }
        return false;
    }
    
    public int compareTo(FileNode toCompare){ 
        return  m_file.getName().compareToIgnoreCase(
                toCompare.m_file.getName() ); 
    }
    
    protected File[] listFiles(){
        if (!m_file.isDirectory())
            return null;
        try
        {
            return m_file.listFiles();
        }
        catch (Exception ex)
        {
            JOptionPane.showMessageDialog(null, "Error reading directory "+m_file.getAbsolutePath(),"Warning", JOptionPane.WARNING_MESSAGE);
            return null;
        }
    }
    }
    

    Till now all was good and clear. Now i'm going to paste selection Controller, here we go:

    Here we create checkbox element to add after to tree node

    package com.demo.tree.checkbox;
    
    
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.ItemListener;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    
    import javax.swing.AbstractAction;
    import javax.swing.ActionMap;
    import javax.swing.ButtonGroup;
    import javax.swing.ButtonModel;
    import javax.swing.Icon;
    import javax.swing.JCheckBox;
    import javax.swing.SwingUtilities;
    import javax.swing.event.ChangeListener;
    import javax.swing.plaf.ActionMapUIResource;
    
    
    public class TristateCheckBox extends JCheckBox {
    static final  long serialVersionUID =0;
    
    /** This is a type-safe enumerated type */
    public static class State
    {
        private State() {}
    }
    
    public final        State NOT_SELECTED = new State();   
    public final        State SELECTED     = new State();
    public final static State DONT_CARE    = new State();
    
    private final TristateDecorator model;
    
    public TristateCheckBox(String text, Icon icon, State initial){
        super(text, icon);
        // Add a listener for when the mouse is pressed
        super.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                grabFocus();
                model.nextState();
            }
        });
    
        // Reset the keyboard action map
        ActionMap map = new ActionMapUIResource();
        map.put("pressed", new AbstractAction() {
    
            private static final long serialVersionUID = 1L;
    
            public void actionPerformed(ActionEvent e) {
                grabFocus();
                model.nextState();
            }
        });
    
        map.put("released", null);
    
        SwingUtilities.replaceUIActionMap(this, map);
    
        // set the model to the adapted model
        model = new TristateDecorator(getModel());
        setModel(model);
        setState(initial);
    }
    
    // Constractor types:
    public TristateCheckBox(String text, State initial) {
        this(text, null, initial);
    }
    
    public TristateCheckBox(String text) {
        this(text, DONT_CARE);
    }
    
    public TristateCheckBox() {
        this(null);
    }
    
    /** No one may add mouse listeners, not even Swing! */
    public void addMouseListener(MouseListener l) { }
    /**
     * Set the new state to either SELECTED, NOT_SELECTED or
     * DONT_CARE.  If state == null, it is treated as DONT_CARE.
     */
    public void setState(State state) {
        model.setState(state);
    }
    /** Return the current state, which is determined by the
     * selection status of the model. */
    public State getState() { 
        return model.getState(); 
    }
    
    public void setSelected(boolean b) {
        if (b) {
            setState(SELECTED);         
        } else {
            setState(NOT_SELECTED);         
        }
    }
    /**
     * Exactly which Design Pattern is this?  Is it an Adapter,
     * a Proxy or a Decorator?  In this case, my vote lies with the
     * Decorator, because we are extending functionality and
     * "decorating" the original model with a more powerful model.
     */
    private class TristateDecorator implements ButtonModel {
        private final ButtonModel other;
        private TristateDecorator(ButtonModel other) {
            this.other = other;
        }
        private void setState(State state) {
            if (state == NOT_SELECTED) {
                other.setArmed(false);
                setPressed(false);
                setSelected(false);
            } else if (state == SELECTED) {
                other.setArmed(false);
                setPressed(false);
                setSelected(true);
            } else { // either "null" or DONT_CARE
                other.setArmed(true);
                setPressed(true);
                setSelected(false);
            }
        }
        /**
         * The current state is embedded in the selection / armed
         * state of the model.
         *
         * We return the SELECTED state when the checkbox is selected
         * but not armed, DONT_CARE state when the checkbox is
         * selected and armed (grey) and NOT_SELECTED when the
         * checkbox is deselected.
         */
        private State getState() {
            if (isSelected() && !isArmed()) {
                // normal black tick
                return SELECTED;
            } else if (isSelected() && isArmed()) {
                // don't care grey tick
                return DONT_CARE;
            } else {
                // normal deselected
                return NOT_SELECTED;
            }
        }
    
        /** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/
        private void nextState() {
            State current = getState();
            if (current == NOT_SELECTED) {
                setState(SELECTED);
            } else if (current == SELECTED) {
                setState(DONT_CARE);
            } else if (current == DONT_CARE) {
                setState(NOT_SELECTED);
            }
        }
    
        /** Filter: No one may change the armed status except us. */
        public void setArmed(boolean b) {
        }
    
        /** We disable focusing on the component when it is not
         * enabled. */
        public void setEnabled(boolean b) {
            setFocusable(b);
            other.setEnabled(b);
        }
    
        /** All these methods simply delegate to the "other" model
         * that is being decorated. */
        public boolean isArmed()                              {return other.isArmed(); }
        public boolean isSelected()                           {return other.isSelected(); }
        public boolean isEnabled()                            {return other.isEnabled(); }
        public boolean isPressed()                            {return other.isPressed(); }
        public boolean isRollover()                           {return other.isRollover(); }
        public int     getMnemonic()                          {return other.getMnemonic(); }
        public String  getActionCommand()                     {return other.getActionCommand();}
        public Object[]getSelectedObjects()                   {return other.getSelectedObjects();}
        public void    setSelected(boolean b)                 {other.setSelected(b);}
        public void    setPressed(boolean b)                  {other.setPressed(b);}
        public void    setRollover(boolean b)                 {other.setRollover(b);}
        public void    setMnemonic(int key)                   {other.setMnemonic(key);}             
        public void    setActionCommand(String s)             {other.setActionCommand(s);}                      
        public void    setGroup(ButtonGroup group)            {other.setGroup(group);}      
        public void    addActionListener(ActionListener l)    {other.addActionListener(l);}
        public void    removeActionListener(ActionListener l) {other.removeActionListener(l);}      
        public void    addItemListener(ItemListener l)        {other.addItemListener(l);}       
        public void    removeItemListener(ItemListener l)     {other.removeItemListener(l);}    
        public void    addChangeListener(ChangeListener l)    {other.addChangeListener(l);}     
        public void    removeChangeListener(ChangeListener l) {other.removeChangeListener(l);}      
    
    }
    }
    

    After we add CheckTreeManager:

    package com.demo.tree.checkbox;
    
    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.util.ArrayList;
    import java.util.Stack;
    
    import javax.swing.JCheckBox;
    import javax.swing.JPanel;
    import javax.swing.JTree;
    import javax.swing.event.TreeSelectionEvent;
    import javax.swing.event.TreeSelectionListener;
    import javax.swing.tree.DefaultTreeSelectionModel;
    import javax.swing.tree.TreeCellRenderer;
    import javax.swing.tree.TreeModel;
    import javax.swing.tree.TreePath;
    import javax.swing.tree.TreeSelectionModel;
    
    
    public class AddCheckBoxToTree {
    
    
    public class CheckTreeSelectionModel extends DefaultTreeSelectionModel{ 
        static final  long serialVersionUID =0;
        private TreeModel model; 
    
        public CheckTreeSelectionModel(TreeModel model){ 
            this.model = model; 
    
            setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); 
        }       
    
        // tests whether there is any unselected node in the subtree of given path (DONT_CARE)
        public boolean isPartiallySelected(TreePath path){
            if(isPathSelected(path, true)){ 
                return false; 
            }
    
            TreePath[] selectionPaths = getSelectionPaths(); 
    
            if(selectionPaths==null){
                return false; 
            }
    
            for(int j = 0; j<selectionPaths.length; j++){ 
                if(isDescendant(selectionPaths[j], path)){
                    return true;
                }                    
            } 
    
            return false; 
        } 
    
        // tells whether given path is selected. 
        // if dig is true, then a path is assumed to be selected, if 
        // one of its ancestor is selected. 
        public boolean isPathSelected(TreePath path, boolean dig){                      
            if(!dig){
                return super.isPathSelected(path); 
            }
    
            while(path!=null && !super.isPathSelected(path)){
                path = path.getParentPath(); 
            }
    
            return path!=null; 
        } 
    
        // is path1 descendant of path2 
        private boolean isDescendant(TreePath path1, TreePath path2){ 
            Object obj1[] = path1.getPath(); 
            Object obj2[] = path2.getPath(); 
            for(int i = 0; i<obj2.length; i++){ 
                if(obj1[i]!=obj2[i]) 
                    return false; 
            } 
            return true; 
        } 
    
        public void setSelectionPaths(TreePath[] pPaths){ 
            throw new UnsupportedOperationException("not implemented yet!!!"); 
        } 
    
        public void addSelectionPaths(TreePath[] paths){ 
    
            // unselect all descendants of paths[] 
            for(int i = 0; i<paths.length; i++)
            { 
                TreePath path = paths[i]; 
    
                TreePath[] selectionPaths = getSelectionPaths(); 
    
                if(selectionPaths==null){ 
                    break; 
                }
    
                ArrayList<TreePath> toBeRemoved = new ArrayList<TreePath>(); 
    
                for(int j = 0; j<selectionPaths.length; j++)
                { 
                    if(isDescendant(selectionPaths[j], path))
                    {
                        toBeRemoved.add(selectionPaths[j]); 
                    }
                } 
                super.removeSelectionPaths((TreePath[])toBeRemoved.toArray(new TreePath[0])); 
            } 
    
            // if all siblings are selected then unselect them and select parent recursively 
            // otherwize just select that path. 
            for(int i = 0; i<paths.length; i++){ 
                TreePath path = paths[i]; 
    
                TreePath temp = null; 
    
                while(areSiblingsSelected(path)){ 
                    temp = path; 
    
                    if(path.getParentPath()==null)
                    { 
                        break; 
                    }
    
                    path = path.getParentPath(); 
                } 
    
                if(temp!=null){ 
                    if(temp.getParentPath()!=null)
                    { 
                        addSelectionPath(temp.getParentPath());
                    }
                    else
                    { 
                        if(!isSelectionEmpty())
                        { 
                            removeSelectionPaths(getSelectionPaths()); 
                        }
    
                        super.addSelectionPaths(new TreePath[]{temp}); 
                    }                   
                }
                else 
                {   
                    super.addSelectionPaths(new TreePath[]{ path}); 
                }
            } 
        } 
    
        // tells whether all siblings of given path are selected. 
        private boolean areSiblingsSelected(TreePath path){ 
            TreePath parent = path.getParentPath(); 
    
            if(parent==null){ 
                return true; 
            }
    
            Object node = path.getLastPathComponent(); 
    
            Object parentNode = parent.getLastPathComponent(); 
    
            int childCount = model.getChildCount(parentNode); 
    
            Boolean isParameters = false;
            Boolean isDescription = false;
    
            for(int i = 0; i<childCount; i++){ 
    
                Object childNode = model.getChild(parentNode, i); 
    
    
    
                if(childNode==node){ 
                    continue;
                }
    
    // If last Path component equals to "parameters" or "description" - select second child too. 
                if(childCount == 2)
                {
                    if(childNode.toString().equals("parameters") && model.isLeaf(childNode))
                    {
                        isParameters = true;
                    }
                    if(childNode.toString().equals("description") && model.isLeaf(childNode))
                    {
                        isDescription = true;
                    }
                }
    
    
                if(!isPathSelected(parent.pathByAddingChild(childNode)) && !isParameters && !isDescription){ 
                    return false; 
                }
            }
    
            return true; 
        } 
    
        public void removeSelectionPaths(TreePath[] paths){ 
            for(int i = 0; i<paths.length; i++){ 
                TreePath path = paths[i]; 
                if(path.getPathCount()==1) 
                    super.removeSelectionPaths(new TreePath[]{ path}); 
                else 
                    toggleRemoveSelection(path); 
            } 
        } 
    
        /** if any ancestor node of given path is selected then unselect it 
         *  and selection all its descendants except given path and descendants. 
         * otherwise just unselect the given path */
        private void toggleRemoveSelection(TreePath path){ 
    
            Stack<TreePath> stack = new Stack<TreePath>(); 
            TreePath parent = path.getParentPath(); 
    
            Boolean isParameters = false;
            Boolean isDescription = false;
    
            while(parent!=null && !isPathSelected(parent)){ 
                stack.push(parent); 
                parent = parent.getParentPath(); 
            } 
            if(parent!=null) 
                stack.push(parent); 
            else{ 
                super.removeSelectionPaths(new TreePath[]{path}); 
                return; 
            } 
    
            while(!stack.isEmpty()){ 
                TreePath temp = (TreePath)stack.pop(); 
    
                TreePath peekPath = stack.isEmpty() ? path : (TreePath)stack.peek(); 
    
                Object node = temp.getLastPathComponent(); 
                Object peekNode = peekPath.getLastPathComponent(); 
                int childCount = model.getChildCount(node); 
    
    
                for(int i = 0; i<childCount; i++){ 
                    Object childNode = model.getChild(node, i); 
    
                    if(childNode.toString().equals("parameters") && model.isLeaf(childNode))
                    {
                        isParameters = true;
                    }
                    if(childNode.toString().equals("description")  && model.isLeaf(childNode))
                    {
                        isDescription = true;
                    }
    
    
                    if(childNode!=peekNode)
                    {
                        if(!isParameters && !isDescription)
                        super.addSelectionPaths(new TreePath[]{temp.pathByAddingChild(childNode)}); 
                    }
                } 
            } 
    
            super.removeSelectionPaths(new TreePath[]{parent}); 
        }
    
        public TreeModel getModel() {
            return model;
        } 
    }
    
    
    public class CheckTreeCellRenderer extends JPanel implements TreeCellRenderer { 
    
        static final  long serialVersionUID =0;
    
        CheckTreeSelectionModel selectionModel; 
        private TreeCellRenderer delegate; 
        TristateCheckBox checkBox = new TristateCheckBox(); 
    
    
    
    
    
        public CheckTreeCellRenderer(TreeCellRenderer delegate, CheckTreeSelectionModel selectionModel){ 
            this.delegate = delegate; 
            this.selectionModel = selectionModel; 
    
            setLayout(new BorderLayout()); 
            setOpaque(false); 
            checkBox.setOpaque(false);      
    
        } 
    
    
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){ 
            Component renderer = delegate.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); 
    
    
            TreePath path = tree.getPathForRow(row);            
    
    
            if(path!=null)
            {                   
                if(selectionModel.isPathSelected(path, true))
                {                   
                    checkBox.setState(checkBox.SELECTED);   
                    //System.out.println(">>>>>>     selected   " );
                }
                else
                {                       
                    checkBox.setState(checkBox.NOT_SELECTED);   
                    //System.out.println("not selected   ");
                }
    
                if(selectionModel.isPartiallySelected(path))
                {
                    checkBox.setState(checkBox.DONT_CARE);                  
                }
            } 
    
            removeAll();        
    
                add(checkBox, BorderLayout.WEST); 
                add(renderer, BorderLayout.CENTER);     
    
    
            return this; 
        }
    
    
        public TreeCellRenderer getDelegate() {
            return delegate;
        }
    
    
        public void setDelegate(TreeCellRenderer delegate) {
            this.delegate = delegate;
        } 
    } 
    
    
    public class CheckTreeManager extends MouseAdapter implements TreeSelectionListener{ 
        CheckTreeSelectionModel selectionModel; 
        private JTree tree = new JTree(); 
    
        int hotspot = new JCheckBox().getPreferredSize().width; 
    
        public CheckTreeManager(JTree tree, CheckTreeSelectionModel checkTreeSelectionModel){ 
            this.tree = tree;           
    
    
            if(checkTreeSelectionModel != null)
            {
                //selectionModel = new CheckTreeSelectionModel(tree.getModel()); 
                selectionModel =  checkTreeSelectionModel;
    
            }
            else
            {
                selectionModel = new CheckTreeSelectionModel(tree.getModel()); 
                //System.out.println(selectionModel.getSelectionPath());
            }
    
    
    
            tree.setCellRenderer(new CheckTreeCellRenderer(tree.getCellRenderer(), selectionModel)); 
    
            tree.addMouseListener(this); 
            selectionModel.addTreeSelectionListener(this); 
        } 
    
        public void mouseClicked(MouseEvent me){ 
            //System.out.println("start...");
    
            TreePath path = tree.getPathForLocation(me.getX(), me.getY()); 
            //System.out.println(Arrays.asList(path));
    
    
            if(path==null) 
            {
                //System.out.println("path==null");
                return;     
            }
    
            if(me.getX()/1.2>tree.getPathBounds(path).x+hotspot)
            {
                //System.out.println("me.getX()/1.2>tree.getPathBounds(path).x+hotspot");
                return; 
            }
    
    
            boolean selected = selectionModel.isPathSelected(path, true); 
            selectionModel.removeTreeSelectionListener(this); 
    
            try{ 
                if(selected) 
                {
                    //System.out.println("selected");
                    selectionModel.removeSelectionPath(path); 
                }
                else 
                {
                    //System.out.println("not selected");
                    selectionModel.addSelectionPath(path);
                }
            } 
            finally
            { 
                //System.out.println("finally");
                selectionModel.addTreeSelectionListener(this); 
                tree.treeDidChange(); 
            } 
        } 
    
        public CheckTreeSelectionModel getSelectionModel(){ 
            return selectionModel; 
        } 
    
        public void setSelectionModel(CheckTreeSelectionModel selectionModel) {
            this.selectionModel = selectionModel;
        }
    
        public void valueChanged(TreeSelectionEvent e){ 
            tree.treeDidChange(); 
        }       
    }    
    }
    

    The most impotent method is:

    boolean com.demo.tree.checkbox.AddCheckBoxToTree.CheckTreeSelectionModel.isPartiallySelected
    

    that checks whether there is any unselected node in the subtree of given path (DONT_CARE).

    And this is the result of what we did: And this is the result of what we did:


    This answer is based on Santhosh Kumar's Weblog post with minor fixes.

    0 讨论(0)
提交回复
热议问题