views:

7142

answers:

5

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;
 }
}
+1  A: 

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).

Tom Hawtin - tackline
+3  A: 

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

kazanaki
+1  A: 

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.

dw.mackie
+2  A: 

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);
A: 

We still need to handle menu key on windows keyboard. http://en.wikipedia.org/wiki/Menu_key

AlexV