Search code examples
javaswingactionjtreekey-bindings

When adding keybindings to a JTree, Why am I forced to make each Action it's own class?


Question: When adding keybindings to a JTree, Why am I forced to make each Action it's own class? Why can't I have have each action use a single Action class? To understand my question/issue, let me start off by explaining my Short Self Contained Example of the Problem below.

addKeyBindings(JTree tree) has the following 2 lines commented out. 
//Action addsiblingnodeaction = new RightClickNodeAction("Add SiblingNode");
//Action addchildnodeaction = new RightClickNodeAction("Add ChildNode");
/*These use 1 action class, to do 2 different actions. (I want this because I 
find the code to be less verbose)
(They're commented out because they don't work for keybindings)
(The reason I ask question, is because they DO work for JPopupMenu)*/

The Real Question is Why does the above //commented out code// not work for keybindings? (In my example I included a JPopupMenu to show that the above commented out code works for JPopupMenu, and in my mind should also work for keybindings) (The only way I was able to get the keybindings to work was to make a AbstractAction class for each Action, which you can see in the example code below)

import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.KeyStroke;

public class StackExchangeQuestion2 {
     public static void main(String[] args){
     StackExchangeQuestion2 workaround = new StackExchangeQuestion2();
     //workaround = compiler didn't like me throwing constructor code in main
     }//end main

StackExchangeQuestion2(){
     JTree tree = new JTree();   
     initRightClickMenu(tree);//purpose of existance is to show Action Code is accurate
     addKeyBindings(tree);

     JFrame window = new JFrame();
     window.getRootPane().setContentPane(tree);
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     window.setTitle("Stack Exchange Question");
     window.setSize(400,500);//variable parameters would be best
     window.setVisible(true);           
     }//constructor

private void initRightClickMenu(JTree tree){
     JPopupMenu rightclickmenu = new JPopupMenu();   
     Action addsiblingnodeaction = new RightClickNodeAction("Add SiblingNode");
     Action addchildnodeaction = new RightClickNodeAction("Add ChildNode");
     rightclickmenu.add(addsiblingnodeaction);
     rightclickmenu.add(addchildnodeaction);
     tree.setComponentPopupMenu(rightclickmenu);        
}

private void addSiblingNode(){
System.out.println("sibling node added");}
private void addChildNode(){
System.out.println("child node added");}     


private void addKeyBindings(JTree tree){
 //Action addsiblingnodeaction = new RightClickNodeAction("Add SiblingNode");
 Action addsiblingnodeaction = new AddSiblingNodeAction("Add SiblingNode");
    tree.getActionMap().put("Add SiblingNode", addsiblingnodeaction);
    tree.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "Add SiblingNode");    
 //Action addchildnodeaction = new RightClickNodeAction("Add ChildNode");
 Action addchildnodeaction = new AddChildNodeAction("Add ChildNode");    
    tree.getActionMap().put("Add ChildNode", addchildnodeaction);
    tree.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "Add ChildNode");
}//end addKeyBindins    

private class AddSiblingNodeAction extends AbstractAction{      
    AddSiblingNodeAction(String name){super(name);}
    public void actionPerformed(ActionEvent ae) {    addSiblingNode();   }}

private class AddChildNodeAction extends AbstractAction{      
    AddChildNodeAction(String name){super(name);}
    public void actionPerformed(ActionEvent ae) {    addChildNode();     }}    

private class RightClickNodeAction extends AbstractAction{      
    RightClickNodeAction(String name){super(name);}
    public void actionPerformed(ActionEvent ae) {
    if(ae.getActionCommand().equals("Add SiblingNode"))addSiblingNode();
    if(ae.getActionCommand().equals("Add ChildNode"))addChildNode();     }}
}//end class

Solution

  • Answer: Because I'm doing it wrong, I'm not forced to do it one way.

    private class RightClickNodeAction extends AbstractAction{      
        RightClickNodeAction(String name){super(name);
        putValue(ACTION_COMMAND_KEY, name); 
        }//end constructor
    public void actionPerformed(ActionEvent ae) {
       //System.out.println("Action Command is: "+ae.getActionCommand());
       //the above comment verified it worked as expected + testing
    if(ae.getActionCommand().equals("Add SiblingNode"))addSiblingNode();
    if(ae.getActionCommand().equals("Add ChildNode"))addChildNode();     }}