I\'ve created a tree cell renderer/editor framework that is admittedly a little bit hacky, but it works perfectly on Windows and Linux. The image below illustrates a sample set
I've come up with something that creates my desired behavior and does not use a TreeCellEditor
at all. Instead, the tree is uneditable, and is instantiated with a custom extension of JTree
which has processMouseEvent
overridden. I got this idea here.
It seems to work perfect, but it is still a little bit hacky (it does a loop of calculations to determine where the start of the tree cell is, since that can vary based on indentation). Also I pretty much disabled mouseClicked
and mouseReleased
type events, and am controlling the JTreeMod
only with mousePressed
events. Not sure if this will bite me later or is bad practice, but I didn't like my custom code running 3 times in a row for all the events. I also haven't been able to test on a non-Windows OS just yet.
Here is the console output after clicking, in series (1) the image one (2) the text one (3) the image two (4) the text two. Again, this perfectly implements my desired behavior.
you clicked the image for row 1. this was detected, but no selection will happen!
you clicked the text for row 1. this was detected, and selection WILL happen!
SELECTION CHANGED!
you clicked the image for row 2. this was detected, but no selection will happen!
you clicked the text for row 2. this was detected, and selection WILL happen!
SELECTION CHANGED!
And here is the new SSCCE:
package TreeTest;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
@SuppressWarnings("serial")
public class TreeTest2 extends JComponent {
private JFrame frame;
private DefaultTreeModel treeModel;
private DefaultMutableTreeNode root;
private JTreeMod tree;
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Throwable e) {
e.printStackTrace();
}
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TreeTest2 window = new TreeTest2();
window.frame.setVisible(true);
window.frame.requestFocusInWindow();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TreeTest2() {
initialize();
}
private void initialize() {
frame = new JFrame("Tree Test");
frame.setBounds(400, 400, 250, 150);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
root = new DefaultMutableTreeNode("root");
treeModel = new DefaultTreeModel(root);
tree = new JTreeMod(treeModel);
tree.setEditable(false);
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
tree.setCellRenderer(new TreeRenderer());
tree.putClientProperty("JTree.lineStyle", "None");
tree.setBackground(Color.white);
DefaultMutableTreeNode one = new DefaultMutableTreeNode("one");
DefaultMutableTreeNode two = new DefaultMutableTreeNode("two");
treeModel.insertNodeInto(one, root, 0);
treeModel.insertNodeInto(two, one, 0);
TreeNode[] nodes = treeModel.getPathToRoot(root);
tree.expandPath(new TreePath(nodes));
nodes = treeModel.getPathToRoot(one);
tree.expandPath(new TreePath(nodes));
tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
System.out.println("SELECTION CHANGED!");
}
});
frame.getContentPane().add(tree);
}
public class TreeRenderer implements TreeCellRenderer {
private ImageIcon oneIcon;
private ImageIcon twoIcon;
public TreeRenderer() {
try {
oneIcon = new ImageIcon(ImageIO.read(
new URL("http://i.imgur.com/HtHJkfI.png")));
twoIcon = new ImageIcon(ImageIO.read(
new URL("http://i.imgur.com/w5jAp5c.png")));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
JLabel numIcon = new JLabel();
numIcon.setAlignmentX(JLabel.CENTER_ALIGNMENT);
numIcon.setAlignmentY(JLabel.CENTER_ALIGNMENT);
numIcon.setBorder(new EmptyBorder(0, 0, 0, 4));
JLabel numText = new JLabel();
JPanel comp = new JPanel();
comp.setLayout(new BoxLayout(comp, BoxLayout.X_AXIS));
comp.add(numIcon);
comp.add(numText);
String str = (String) ((DefaultMutableTreeNode) value).getUserObject();
if (str.equals("one")) {
numIcon.setIcon(oneIcon);
numText.setText("one");
} else if (str.equals("two")) {
numIcon.setIcon(twoIcon);
numText.setText("two");
}
numText.setOpaque(true);
if (selected) {
numText.setBackground(new Color(209, 230, 255));
numText.setBorder(new LineBorder(
new Color(132, 172, 221), 1, false));
} else {
numText.setBackground(Color.white);
numText.setBorder(new LineBorder(Color.white, 1, false));
}
comp.setFocusable(false);
comp.setBackground(Color.white);
return comp;
}
}
public class JTreeMod extends JTree {
public JTreeMod(DefaultTreeModel treeModel) {
super(treeModel);
}
@Override
protected void processMouseEvent(MouseEvent e) {
int type = e.getID();
if (type == MouseEvent.MOUSE_CLICKED || type == MouseEvent.MOUSE_RELEASED) {
// do nothing
} else if (type == MouseEvent.MOUSE_PRESSED) {
int x = e.getX();
int y = e.getY();
int row = this.getRowForLocation(x, y);
if (row == -1) {
super.processMouseEvent(e);
return;
}
int xOffset = x;
int row1 = row;
while (row1 == row) {
xOffset--;
row1 = this.getRowForLocation(xOffset, y);
}
xOffset++;
if (x - xOffset <= 16) {
System.out.println("you clicked the image for row " + (row + 1) +
". this was detected, but no selection will happen!");
return;
} else {
System.out.println("you clicked the text for row " + (row + 1) +
". this was detected, and selection WILL happen!");
super.processMouseEvent(e);
}
} else {
super.processMouseEvent(e);
}
}
}
}