Search code examples
javaswingjtree

Detect mouseover icon


I can detect when the mouse is over the text part of my JTree with the code below. I want to to also detect when the user mouses over the icon to the left of the JTree text that is used to expand/collapse things and change the cursor accordingly. How would I do that?

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

public class Testing {

    public static void main(String[] args) throws Exception {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JTree tree = new JTree(buildTree());
        tree.addMouseMotionListener(new MouseMotionListener() {

            @Override
            public void mouseDragged(MouseEvent e) {
            }

            @Override
            public void mouseMoved(MouseEvent e) {
                TreePath tp = ((JTree) e.getSource()).getPathForLocation(e.getX(), e.getY());
                if (tp != null) {
                    ((JTree) e.getSource()).setCursor(new Cursor(Cursor.HAND_CURSOR));
                } else {
                    ((JTree) e.getSource()).setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
            }
        });
        frame.add(panel);
        panel.add(tree);
        frame.setSize(new Dimension(200, 200));
        frame.setVisible(true);
    }

    public static DefaultMutableTreeNode buildTree() {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
        DefaultMutableTreeNode subOne = new DefaultMutableTreeNode("Test1");
        DefaultMutableTreeNode leafOne = new DefaultMutableTreeNode("Leaf");
        DefaultMutableTreeNode subTwo = new DefaultMutableTreeNode("Test2");
        subOne.add(leafOne); //Drop down button should detect hover over
        root.add(subOne);
        root.add(subTwo);
        return root;
    }
}

Solution

  • Here is mine solution for you. We need reflection to check whether the mouse over the icon because the required method is declared as protected.

    import java.awt.Cursor;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseMotionListener;
    import java.lang.reflect.Method;
    
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTree;
    import javax.swing.SwingUtilities;
    import javax.swing.WindowConstants;
    import javax.swing.plaf.basic.BasicTreeUI;
    import javax.swing.tree.TreePath;
    
    
    public class TreeTest implements Runnable {
    
        @Override
        public void run() {
            JTree tree = new JTree();
            tree.addMouseMotionListener(new MouseMotionListener() {
    
                @Override
                public void mouseDragged(MouseEvent e) {
                }
    
                @Override
                public void mouseMoved(MouseEvent e) {
                    JTree t = (JTree) e.getSource();
                    TreePath tp = t.getPathForLocation(e.getX(), e.getY());
                    if (tp != null) {
                        t.setCursor(new Cursor(Cursor.HAND_CURSOR));
                    } else {
                        boolean overIcon = false;
                        tp = t.getClosestPathForLocation(e.getX(), e.getY());
                        if (tp != null && t.getUI() instanceof BasicTreeUI) {
                            BasicTreeUI ui = (BasicTreeUI) tree.getUI();
                            if (isInExpandControl(ui, tp, e)) {
                                overIcon = true;
                                t.setCursor(new Cursor(Cursor.MOVE_CURSOR)); // set another cursor
                            }
                        }
                        if (!overIcon) {
                            t.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                        }
                    }
                }
            });
            JFrame frm = new JFrame("Tree test");
            frm.add(new JScrollPane(tree));
            frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frm.pack();
            frm.setLocationRelativeTo(null);
            frm.setVisible(true);
        }
    
        private boolean isInExpandControl(BasicTreeUI ui, TreePath tp, MouseEvent evt) {
            try {
                Method m = BasicTreeUI.class.getDeclaredMethod("isLocationInExpandControl", TreePath.class, int.class, int.class);
                m.setAccessible(true);
                return Boolean.TRUE.equals(m.invoke(ui, tp, evt.getX(), evt.getY()));
            } catch (Exception e) {
                return false;
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new TreeTest());
        }
    }