chrixm
chrixm

Reputation: 1024

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.

Upvotes: 1

Views: 129

Answers (2)

chrixm
chrixm

Reputation: 1024

Thanks to @sergiy-medvynskyy I have implemented a Global Focus Listener to keep track of the last JTextArea to be focused:

KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
  @Override
  public void propertyChange(final PropertyChangeEvent e) {
    if (e.getNewValue() instanceof JTextArea) {
      SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
          tFocused = (JTextArea)e.getNewValue();
        }
      });
    }
  }
});

I then check the tFocused object using a MenuListener on my JMenu to verify what JTextArea currently has the focus. I can then call setEnabled() on my respective JMenuItem's depending on the context.

Upvotes: 1

Sergiy Medvynskyy
Sergiy Medvynskyy

Reputation: 11327

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());

Upvotes: 1

Related Questions