Search code examples
javaswingawtjmenu

Implementing a JMenu with actionPerformed using TextAction


I have a Java Swing interface with multiple JTextArea's and I am implementing an "Edit" menu with various different functions like "Find", "Copy", "Paste", etc. When I click on the JMenuItem I need to know which JTextArea had the focus which is achievable through a TextAction (I haven't gone down the route of a FocusListener and keeping track of what last had the focus):

JMenuItem miFind = new JMenuItem(new EditHandler("Find"));

class EditHandler extends TextAction {
  private String s = null;

  public EditHandler(String vs) {
    super(vs);
    s = vs;
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    JTextComponent c = getFocusedComponent();
    if (s.equals("Find")) {
      showFindDialog(c);
    }
  }
}

This works well and good but I want to be able to disable the "Find" JMenuItem under certain contexts (i.e. if the specific JTextArea is disabled or is empty. I can implement an ActionListener on a JMenu but I can't use getFocusedComponent() to identify what JTextArea has the focus.

According to the Java docs the JMenu constructor takes an Action (like a JMenuItem) and I have tried the following:

mEdit = new JMenu(new EditHandler("Edit"));

However, although the constructor fires, the actionPerformed() event isn't firing within my EditHandler for the JMenu. If I can get it to fire then I was planning to either enable or disable my "Find" JMenuItem.


Solution

  • The best way for you is using of actions map of the text component to place the corresponding action. In this case you can disable it for some text components.

    @Override
    public void actionPerformed(ActionEvent e) {
      JTextComponent c = getFocusedComponent();
      if (s.equals("Find")) {
        Action a = c.getActionMap().get("Find");
        if (a.isEnabled()) {
          // generate new event to modify the source (menu item -> text component)
          ActionEvent ae = new ActionEvent(c, e.getID(), e.getCommand());
          a.actionPerformed(ae);
        }
      }
    }
    

    For each your text component you must provide an action and register it using the action map of the component.

    public class UniversalFindAction extends AbstractAction {
      public void actionPerformed(ActionEvent ae) {
        JTextComponent c = (JTextComponent) ae.getSource();
        showFindDialog(c);
      }
    }
    
    // registering of action
    JTextComponent comp = new JTextArea();
    comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK), "Find");
    comp.getActionMap().put("Find", new UniversalFindAction());