Search code examples
javaswingjtree

JTree and CellEditor not working correctly when using two different objects in the same JTree


I am currently having a problem with the cell editor from the JTree. I have two different objects inside my JTree, Users and Books.

enter image description here

So the user is Bianca and the books are the nodes. The labels inside those nodes (the eye, the paint label and the trash can) are also clickable. However, when I set my cell editor, it gives me a null exception. I think it's because I am working with two different objects so I can't set the cell editor properly. This is the specific method where I think I have the problem:

public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded,
        boolean leaf, int row) {
    Component returnValue = null;
    System.out.println("loo");
    if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
        Object userObject = ((DefaultMutableTreeNode) value)
                .getUserObject();
        System.out.println("b");
        if(userObject instanceof Book){
            Book feature = (Book) userObject;
            title.setText(feature.getTitulo());
            renderer.setEnabled(tree.isEnabled());
            returnValue = renderer;
            System.out.println("hi!!");
        }
    }
    if (returnValue == null) {
        System.out.println("hi1");
        returnValue = defaultEditor.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
        System.out.println(defaultEditor);

    } 
    return returnValue;
}

I don't know how to return the DefaultCellEditor when the type of the node is not Book.

Below is my entire code if someone wants to replicate the problem:

Class Book:

public class Book {
    String titulo;

    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }
    public Book(String titulo){
        this.titulo = titulo;
    }
}

Class User

public class User {
    String nomeUser;

    public String getNomeUser() {
        return nomeUser;
    }

    public void setNomeUser(String nomeUser) {
        this.nomeUser = nomeUser;
    }
    public User(String nome){
        this.nomeUser = nome;
    }
}

Class Render (used to set the cell editor and cell render of the JTree)

import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;
import javax.swing.AbstractCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;


public class Render{
public static class NodeCellRenderer implements TreeCellRenderer{
        private JLabel nomeRadar;
        private JPanel renderer;
        private JLabel btnVisible;
        private JLabel btnPaint;
        private JLabel btnTrash;

        DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
        public NodeCellRenderer() throws IOException{
            renderer = new JPanel(new FlowLayout(5, 5,5));
            nomeRadar = new JLabel();
            renderer.add(nomeRadar);
            btnVisible = new JLabel();
            btnPaint = new JLabel();
            btnTrash = new JLabel();
            InputStream visible = getClass().getResourceAsStream("/eye_visible-16.png");
            InputStream invisible = getClass().getResourceAsStream("/eye_invisible-16.png");
            InputStream paint = getClass().getResourceAsStream("/paint-16.png");
            InputStream trash = getClass().getResourceAsStream("/trash-16.png");
            btnVisible.setIcon(new ImageIcon(ImageIO.read(visible)));
            btnPaint.setIcon(new ImageIcon(ImageIO.read(paint)));
            btnTrash.setIcon(new ImageIcon(ImageIO.read(trash)));
            nomeRadar.addMouseListener(new MouseAdapter(){

                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no nome");
                }
            });
            btnVisible.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no olho");
                }
            });
            btnPaint.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no paint");
                }
            });
            btnTrash.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no trash");
                }
            });
            renderer.add(nomeRadar);
            renderer.add(btnVisible);
            renderer.add(btnPaint);
            renderer.add(btnTrash);

        }
        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf,
                int row, boolean hasFocus) {
            Component returnValue = null;
            // TODO Auto-generated method stub
            if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
                Object userObject = ((DefaultMutableTreeNode) value)
                        .getUserObject();
                if(userObject instanceof Book){
                    Book feature = (Book) userObject;
                    nomeRadar.setText(feature.getTitulo());
                    renderer.setEnabled(tree.isEnabled());
                    returnValue = renderer;
                }
            }
            if (returnValue == null) {
                returnValue = defaultRenderer.getTreeCellRendererComponent(tree,
                        value, selected, expanded, leaf, row, hasFocus);
            }
            return returnValue;
        }

    }
    public static class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor, ActionListener, MouseListener
    {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        TreeCellEditor defaultEditor = JTreeOfObjects.currentStyle;
        JLabel title;
        JPanel renderer;
        JLabel btnVisible;
        JLabel btnPaint;
        JLabel btnTrash;
        private Object value;

        public ButtonCellEditor() throws IOException{
            renderer = new JPanel(new FlowLayout(5, 5,5));
            System.out.println("ulo");
            title = new JLabel();
            renderer.add(title);
            btnVisible = new JLabel();
            btnPaint = new JLabel();
            btnTrash = new JLabel(); 
            InputStream visible = getClass().getResourceAsStream("/eye_visible-16.png");
            InputStream invisible = getClass().getResourceAsStream("/eye_invisible-16.png");
            InputStream paint = getClass().getResourceAsStream("/paint-16.png");
            InputStream trash = getClass().getResourceAsStream("/trash-16.png");
            btnVisible.setIcon(new ImageIcon(ImageIO.read(visible)));
            btnPaint.setIcon(new ImageIcon(ImageIO.read(paint)));
            btnTrash.setIcon(new ImageIcon(ImageIO.read(trash)));
            System.out.println("nossa 1");
            title.addMouseListener(new MouseAdapter(){

                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no nome");
                    stopCellEditing();
                }
            });
            btnVisible.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no olho");
                    stopCellEditing();
                }
            });
            btnPaint.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no paint");
                    stopCellEditing();
                }
            });
            btnTrash.addMouseListener(new MouseAdapter(){
                public void mouseClicked(MouseEvent e){
                    System.out.println("clicou no trash");
                    stopCellEditing();
                }
            });
            renderer.add(title);
            renderer.add(btnVisible);
            renderer.add(btnPaint);
            renderer.add(btnTrash); 
        }


        @Override
        public Object getCellEditorValue() {
            // TODO Auto-generated method stub
            //return value.toString();
            return null;
        }


        @Override
        public void mouseClicked(MouseEvent e) {
            // TODO Auto-generated method stub
            System.out.println("clicou no b");
            stopCellEditing();
        }


        @Override
        public void mouseEntered(MouseEvent e) {
            // TODO Auto-generated method stub

        }


        @Override
        public void mouseExited(MouseEvent e) {
            // TODO Auto-generated method stub

        }


        @Override
        public void mousePressed(MouseEvent e) {
            // TODO Auto-generated method stub
            System.out.println("clicou no d");
            stopCellEditing();
        }


        @Override
        public void mouseReleased(MouseEvent e) {
            // TODO Auto-generated method stub

        }


        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            System.out.println("clicou no a");
            stopCellEditing();

        }


        @Override
        public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded,
                boolean leaf, int row) {
            Component returnValue = null;
            System.out.println("loo");
            if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
                Object userObject = ((DefaultMutableTreeNode) value)
                        .getUserObject();
                System.out.println("b");
                if(userObject instanceof Book){
                    Book feature = (Book) userObject;
                    title.setText(feature.getTitulo());
                    renderer.setEnabled(tree.isEnabled());
                    returnValue = renderer;
                    System.out.println("hi!!");
                }
            }
            if (returnValue == null) {
                System.out.println("hi1");
                returnValue = defaultEditor.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
                System.out.println(defaultEditor);

            } 
            return returnValue;
        }


    }
}

Class JTreeOfObjects

public class JTreeOfObjects {


    public static TreeCellRenderer renderer;
    private JFrame frame;
    public static JTree tree;
    private DefaultTreeModel model;
    private DefaultMutableTreeNode parent;
    JLabel nomeRadar;
    JLabel btnVisible;
    JLabel btnPaint;
    JLabel btnTrash;
    public static TreeCellEditor currentStyle;
    public void initialize() throws IOException{
        frame = new JFrame("Árvore de Navegação");
        parent = new DefaultMutableTreeNode("Library",true);
        //DefaultMutableTreeNode undefined = new DefaultMutableTreeNode("Indefinida");
        //undefined.add(new DefaultMutableTreeNode("!"));
        System.out.println(parent.getUserObject());

        //parent.add(undefined);
        tree = new JTree(parent);
        System.out.println("Current"+tree.getCellEditor());
        //currentStyle = tree.getCellEditor();
        renderer  = new Render.NodeCellRenderer();
        model = (DefaultTreeModel) tree.getModel();

        tree.setEditable(true);
        tree.setCellRenderer(renderer);
        tree.setCellEditor(new Render.ButtonCellEditor());
        //frame.getContentPane().add(tree);
        nomeRadar = new JLabel();
        btnVisible = new JLabel();
        btnPaint = new JLabel();
        btnTrash = new JLabel(); 
        InputStream visible = getClass().getResourceAsStream("/eye_visible-16.png");
        InputStream invisible = getClass().getResourceAsStream("/eye_invisible-16.png");
        InputStream paint = getClass().getResourceAsStream("/paint-16.png");
        InputStream trash = getClass().getResourceAsStream("/trash-16.png");
        btnVisible.setIcon(new ImageIcon(ImageIO.read(visible)));
        btnPaint.setIcon(new ImageIcon(ImageIO.read(paint)));
        btnTrash.setIcon(new ImageIcon(ImageIO.read(trash)));
        frame.getContentPane().add(btnPaint);
        frame.getContentPane().add(btnVisible);
        frame.getContentPane().add(btnTrash);
        frame.getContentPane().add(tree);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setUndecorated(true);
        frame.getRootPane().setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);
        frame.setSize(200, 200);
        frame.setVisible(true);

    }
    public void adicionarNoCamada(String nomePessoa){
        //DefaultTreeModel model = (DefaultTreeModel)tree.getModel();

        DefaultMutableTreeNode noCamada = new DefaultMutableTreeNode(nomePessoa);
        parent.add(noCamada);


    }
    public void adicionarLivro(String nomeCamada,Book feature) throws IOException{
        //tree.get
        DefaultMutableTreeNode node = null;
        Enumeration e = parent.breadthFirstEnumeration();
        while(e.hasMoreElements()){
            node = (DefaultMutableTreeNode) e.nextElement();
            if(nomeCamada.equals(node.getUserObject().toString())){
                node.add(new DefaultMutableTreeNode(feature));
                break;
            }
            else{

            }
        }
        tree.setCellEditor(new Render.ButtonCellEditor());
    }

}

Class Main App

public class MainApp {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        Book b1 = new Book("The Book of Life");
        Book b2 = new Book ("The Book of Death");
        User u1 = new User("Bianca");
        JTreeOfObjects object = new JTreeOfObjects();
        object.initialize();
        object.adicionarNoCamada(u1.getNomeUser());
        object.adicionarLivro(u1.getNomeUser(), b1);
        object.adicionarLivro(u1.getNomeUser(), b2);

    }

}

Any help would be greatly appreciated. It's not a duplicated since I know how to fix a NullPointer Exception (have to instatiate the object). I just don't know what to return in the case of a TreeCellEditor, how to return its default.


Solution

  • You might need to override CellEditor#isCellEditable(...) and CellEditor#getCellEditorValue(), but I'm not sure this will give you what you want.

      @Override public boolean isCellEditable(EventObject e) {
        Object source = e.getSource();
        if (!(source instanceof JTree) || !(e instanceof MouseEvent)) {
          return false;
        }
        JTree tree = (JTree) source;
        Point p = ((MouseEvent) e).getPoint();
        Object node = tree.getPathForLocation(p.x, p.y).getLastPathComponent();
        if (!(node instanceof DefaultMutableTreeNode)) {
          return false;
        }
        Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
        if (!(userObject instanceof Book)) {
          return false;
        }
        return super.isCellEditable(e);
      }
    
      @Override
      public Object getCellEditorValue() {
        return new Book(renderer.nomeRadar.getText());
      }
    

    Here's a quick example:

    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.tree.*;
    
    public class MainApp2 {
      public JComponent makeUI() {
        JTree tree = new JTree(makeModel());
        tree.setEditable(true);
        tree.setCellRenderer(new NodeCellRenderer());
        tree.setCellEditor(new ButtonCellEditor());
    
        JPanel p = new JPanel(new BorderLayout());
        p.add(new JScrollPane(tree));
        return p;
      }
      private static DefaultMutableTreeNode makeModel() {
        Book b1 = new Book("The Book of Life");
        Book b2 = new Book("The Book of Death");
        User u1 = new User("Bianca");
    
        DefaultMutableTreeNode noCamada = new DefaultMutableTreeNode(u1);
        noCamada.add(new DefaultMutableTreeNode(b1));
        noCamada.add(new DefaultMutableTreeNode(b2));
    
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Library", true);
        root.add(noCamada);
        return root;
      }
      public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
          JFrame f = new JFrame("Arvore de Navegacao");
          f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
          f.getContentPane().add(new MainApp2().makeUI());
          f.setSize(320, 240);
          f.setLocationRelativeTo(null);
          f.setVisible(true);
        });
      }
    }
    
    class NodePanel extends JPanel {
      public final JLabel nomeRadar = new JLabel();
      public final JLabel btnVisible = new JLabel(new ColorIcon(Color.RED));
      public final JLabel btnPaint = new JLabel(new ColorIcon(Color.GREEN));
      public final JLabel btnTrash = new JLabel(new ColorIcon(Color.BLUE));
      protected NodePanel() {
        super(new FlowLayout(FlowLayout.LEADING, 5, 5));
        add(nomeRadar);
        add(btnVisible);
        add(btnPaint);
        add(btnTrash);
      }
    }
    
    class NodeCellRenderer implements TreeCellRenderer {
      private final DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
      private final NodePanel renderer = new NodePanel();
    
      @Override public Component getTreeCellRendererComponent(
          JTree tree, Object value, boolean selected, boolean expanded,
          boolean leaf, int row, boolean hasFocus) {
        Component returnValue = null;
        if (value instanceof DefaultMutableTreeNode) {
          Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
          if (userObject instanceof Book) {
            Book feature = (Book) userObject;
            renderer.nomeRadar.setText(feature.getTitulo());
            returnValue = renderer;
          }
        }
        if (returnValue == null) {
          returnValue = defaultRenderer.getTreeCellRendererComponent(
              tree, value, selected, expanded, leaf, row, hasFocus);
        }
        return returnValue;
      }
    }
    
    class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor {
      private final DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
      private final NodePanel renderer = new NodePanel();
    
      public ButtonCellEditor() {
        renderer.setBackground(Color.ORANGE);
        renderer.nomeRadar.addMouseListener(new MouseAdapter() {
          @Override public void mousePressed(MouseEvent e) {
            System.out.println("clicou no nome");
            stopCellEditing();
          }
        });
        renderer.btnVisible.addMouseListener(new MouseAdapter() {
          @Override public void mousePressed(MouseEvent e) {
            System.out.println("clicou no olho");
            stopCellEditing();
          }
        });
        renderer.btnPaint.addMouseListener(new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            System.out.println("clicou no paint");
            stopCellEditing();
          }
        });
        renderer.btnTrash.addMouseListener(new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            System.out.println("clicou no trash");
            stopCellEditing();
          }
        });
        renderer.addMouseListener(new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            System.out.println("renderer");
            stopCellEditing();
          }
        });
      }
    
      @Override public boolean isCellEditable(EventObject e) {
        Object source = e.getSource();
        if (!(source instanceof JTree) || !(e instanceof MouseEvent)) {
          return false;
        }
        JTree tree = (JTree) source;
        Point p = ((MouseEvent) e).getPoint();
        TreePath path = tree.getPathForLocation(p.x, p.y);
        if (Objects.isNull(path)) {
          return false;
        }
        Object node = path.getLastPathComponent();
        if (!(node instanceof DefaultMutableTreeNode)) {
          return false;
        }
        Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
        if (!(userObject instanceof Book)) {
          return false;
        }
        return super.isCellEditable(e);
      }
    
      @Override
      public Object getCellEditorValue() {
        return new Book(renderer.nomeRadar.getText());
      }
    
      @Override
      public Component getTreeCellEditorComponent(
          JTree tree, Object value, boolean isSelected, boolean expanded,
          boolean leaf, int row) {
        Component returnValue = null;
        if (value instanceof DefaultMutableTreeNode) {
          Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
          if (userObject instanceof Book) {
            Book feature = (Book) userObject;
            renderer.nomeRadar.setText(feature.getTitulo());
            returnValue = renderer;
          }
        }
        if (returnValue == null) {
          returnValue = defaultRenderer.getTreeCellRendererComponent(
              tree, value, isSelected, expanded, leaf, row, true);
        }
        return returnValue;
      }
    }
    
    class Book {
      String titulo;
      public String getTitulo() {
        return titulo;
      }
      public void setTitulo(String titulo) {
        this.titulo = titulo;
      }
      public Book(String titulo) {
        this.titulo = titulo;
      }
    }
    
    class User {
      String nomeUser;
      public String getNomeUser() {
        return nomeUser;
      }
      public void setNomeUser(String nomeUser) {
        this.nomeUser = nomeUser;
      }
      public User(String nome) {
        this.nomeUser = nome;
      }
      @Override public String toString() {
        return nomeUser;
      }
    }
    
    class ColorIcon implements Icon {
      private final Color color;
      protected ColorIcon(Color color) {
        this.color = color;
      }
      @Override public void paintIcon(Component c, Graphics g, int x, int y) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.translate(x, y);
        g2.setPaint(color);
        g2.fillRect(0, 0, getIconWidth(), getIconHeight());
        g2.dispose();
      }
      @Override public int getIconWidth() {
        return 16;
      }
      @Override public int getIconHeight() {
        return 16;
      }
    }