Right-click context menu for Java JTree?
Asked Answered
M

6

30

I'm trying to implement pop-up menus in Java JTree. I've sub-classed DefaultTreeCellRenderer (to change node appearance) and DefaultTreeCellEditor (to create Components to attach event listeners to, since apparently the Components that DefaultTreeCellRenderer.getTreeCellRendererComponent() returns can't do it?). I don't really want to "edit" the nodes, just be able to pop-up a menu when a node gets right-clicked, but this is the only way I can think of doing it right now...

Below is the code that I have so far-- I'm just trying to figure out how to capture MouseEvents. It sort of works, but badly. What's a better way to accomplish what I'm trying to do here?

private class My_TreeCellRenderer extends DefaultTreeCellRenderer { 
    My_TreeCellRenderer() {
        super ();
    }   

    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

        // set label text and tool tips
        setText(((My_Object)value).getTreeLabel());
        setToolTipText(((My_Object)value).getTreeToolTip());

        return this;
    }
}

private class My_TreeCellEditor extends DefaultTreeCellEditor { 
    private MouseAdapter ma;

    My_TreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
        super (tree, renderer);
        ma = new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    System.out.println("My Popup");
                }
            }
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    System.out.println("My Popup");
                }
            }
        };
    }

    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
        String src_filename = null;

        // return non-editing component
        Component c = renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, true);

        // add mouse listener if it's not listening already
        MouseListener mouseListeners[] = c.getMouseListeners();
        int i;
        for (i=0; i < mouseListeners.length && mouseListeners[i] != ma; i++);
        if (i >= mouseListeners.length)
            c.addMouseListener(ma);

        return c;
    }

    protected boolean canEditImmediately(EventObject event) {
        if (event instanceof MouseEvent && ((MouseEvent)event).getClickCount() == 1)
            return true;
        else
            return false;
    }
}
Moniz answered 5/2, 2009 at 20:13 Comment(0)
C
34

This task is simple to accomplish, the following is all you need:

//create a class which implements the MouseListener interface and
//implement the following in your overridden mouseClicked method

@Override
public void mouseClicked(MouseEvent e) {

    if (SwingUtilities.isRightMouseButton(e)) {

        int row = tree.getClosestRowForLocation(e.getX(), e.getY());
        tree.setSelectionRow(row);
        popupMenu.show(e.getComponent(), e.getX(), e.getY());
    }
}

You can then add this custom listener to your desired tree(s).

Connotative answered 2/6, 2012 at 3:35 Comment(0)
N
20

Taken right out of the JTree API

 // If you are interested in detecting either double-click events or when a user clicks on a node, regardless of whether or not it was selected, we recommend you do the following:

 final JTree tree = ...;

 MouseListener ml = new MouseAdapter() {
     public void mousePressed(MouseEvent e) {
         int selRow = tree.getRowForLocation(e.getX(), e.getY());
         TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
         if(selRow != -1) {
             if(e.getClickCount() == 1) {
                 mySingleClick(selRow, selPath);
             }
             else if(e.getClickCount() == 2) {
                 myDoubleClick(selRow, selPath);
             }
         }
     }
 };
 tree.addMouseListener(ml);

Of course you need to modify it a bit for right click instead of left click

Nonna answered 5/2, 2009 at 23:45 Comment(0)
M
13

Thanks everyone. I knew something was wrong when I was spending that much effort on implementing a simple popup.

I dismissed this line of thought at first because it felt weird to resort to x- and y- coordinates to find the node I'm looking for, but I guess this is the way to do it.

    // add MouseListener to tree
    MouseAdapter ma = new MouseAdapter() {
        private void myPopupEvent(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();
            JTree tree = (JTree)e.getSource();
            TreePath path = tree.getPathForLocation(x, y);
            if (path == null)
                return; 

            tree.setSelectionPath(path);

            My_Obj obj = (My_Obj)path.getLastPathComponent();

            String label = "popup: " + obj.getTreeLabel();
            JPopupMenu popup = new JPopupMenu();
            popup.add(new JMenuItem(label));
            popup.show(tree, x, y);
        }
        public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger()) myPopupEvent(e);
        }
        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger()) myPopupEvent(e);
        }
    };

    (...)

    JTree tree = new JTree();
    tree.addMouseListener(ma);
Moniz answered 6/2, 2009 at 2:20 Comment(2)
You are creating a JPopupMenu every time. Move creation to someplace else where it is done only once the modify it on the eventLm
This is a good example of premature optimization (en.wikiquote.org/wiki/Donald_Knuth). There is no point in creating a long-lived object until there is a reason to do so (slow resource initialization, for example).Fraya
R
2

I think you're making things way harder than they need to be.
JTree has several "add_foo_Listener" methods on it. Implement one of those (TreeSelectionListener looks about right), and then you've got the currently selected node.
Implement a MouseListener so that you can detect the right-click event (and add it to the JTree, since JTree's a Component), and then you should have everything you need to post a context-sensitive menu.
Check out this tutorial for more details.

Riplex answered 5/2, 2009 at 23:56 Comment(1)
I don't see a TreeSelectionEvent carrying position information like a MouseEvent in e.g. arkon's solutionHinz
D
1

The Renderer is only a transient "rubber stamp", so adding an input listener on that wont be particularly helpful. The Editor, as you point out, is only there once you have gestured to edit. So you want to add a listener to the JTree (assuming it isn't implemented as a composite component).

Discriminatory answered 5/2, 2009 at 20:20 Comment(0)
C
0

Call addRightClickListener() to add the right-click context menu listener to your JTree. Both overrides are for proper cross-platform functionality (Windows and Linux differ here).

private void addRightClickListener()
{
    MouseListener mouseListener = new MouseAdapter()
    {
        @Override
        public void mousePressed(MouseEvent mouseEvent)
        {
            handleContextMenu(mouseEvent);
        }

        @Override
        public void mouseReleased(MouseEvent mouseEvent)
        {
            handleContextMenu(mouseEvent);
        }
    };

    tree.addMouseListener(mouseListener);
}

private void handleContextMenu(MouseEvent mouseEvent)
{
    if (mouseEvent.isPopupTrigger())
    {
        MyContextMenu contextMenu = new MyContextMenu();

        contextMenu.show(mouseEvent.getComponent(),
                mouseEvent.getX(),
                mouseEvent.getY());
    }
}
Coyotillo answered 30/3, 2017 at 19:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.