Java Swing How to - Create JTree custom Cell Editor for a custom component








Question

We would like to know how to create JTree custom Cell Editor for a custom component.

Answer

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.Enumeration;
import java.util.EventObject;
/* w w w  .ja v  a 2 s . c o m*/
import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

public final class Main {
  public static JComponent makeUI() {
    JTree tree = new JTree();
    TreeModel model = tree.getModel();
    DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
    Enumeration e = root.breadthFirstEnumeration();
    while (e.hasMoreElements()) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
      Object o = node.getUserObject();
      if (o instanceof String) {
        node.setUserObject(new CheckBoxNode((String) o, false));
      }
    }
    tree.setEditable(true);
    tree.setCellRenderer(new CheckBoxNodeRenderer());
    tree.setCellEditor(new CheckBoxNodeEditor());
    tree.expandRow(0);
    return new JScrollPane(tree);
  }

  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new Main().makeUI());
    f.setSize(320, 240);
    f.setVisible(true);
  }
}

class CheckBoxNode {
  public final String text;
  public final boolean selected;

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

  @Override
  public String toString() {
    return text;
  }
}

class CheckBoxNodeRenderer implements TreeCellRenderer {
  DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
  JCheckBox check = new JCheckBox();
  JPanel p = new JPanel(new BorderLayout());

  public CheckBoxNodeRenderer() {
    p.setFocusable(false);

    p.setOpaque(false);
    p.add(check, BorderLayout.WEST);
    check.setOpaque(false);
  }

  @Override
  public Component getTreeCellRendererComponent(JTree tree, Object value,
      boolean selected, boolean expanded, boolean leaf, int row,
      boolean hasFocus) {
    JLabel l = (JLabel) renderer.getTreeCellRendererComponent(tree, value,
        selected, expanded, leaf, row, hasFocus);
    if (value instanceof DefaultMutableTreeNode) {
      check.setEnabled(tree.isEnabled());
      check.setFont(tree.getFont());
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode) userObject;
        l.setText(node.text);
        check.setSelected(node.selected);
      }
      p.add(l);
      return p;
    }
    return l;
  }
}

class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
  DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
  JCheckBox check = new JCheckBox();
  JPanel p = new JPanel(new BorderLayout());
  String str = null;

  public CheckBoxNodeEditor() {
    super();
    check.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        stopCellEditing();
      }
    });
    p.setFocusable(false);
    p.setRequestFocusEnabled(false);
    p.setOpaque(false);
    p.add(check, BorderLayout.WEST);
    check.setOpaque(false);
  }

  @Override
  public Component getTreeCellEditorComponent(JTree tree, Object value,
      boolean isSelected, boolean expanded, boolean leaf, int row) {
    JLabel l = (JLabel) renderer.getTreeCellRendererComponent(tree, value,
        true, expanded, leaf, row, true);
    if (value instanceof DefaultMutableTreeNode) {
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode) userObject;
        l.setText(node.text);
        check.setSelected(node.selected);
        str = node.text;
      }
      p.add(l);
      return p;
    }
    return l;
  }

  @Override
  public Object getCellEditorValue() {
    return new CheckBoxNode(str, check.isSelected());
  }

  @Override
  public boolean isCellEditable(EventObject e) {
    if (e instanceof MouseEvent && e.getSource() instanceof JTree) {
      MouseEvent me = (MouseEvent) e;
      JTree tree = (JTree) e.getSource();
      TreePath path = tree.getPathForLocation(me.getX(), me.getY());
      Rectangle r = tree.getPathBounds(path);
      if (r == null) {
        return false;
      }
      Dimension d = check.getPreferredSize();
      r.setSize(new Dimension(d.width, r.height));
      if (r.contains(me.getX(), me.getY())) {
        check.setBounds(new Rectangle(0, 0, d.width, r.height));
        return true;
      }
    }
    return false;
  }
}