Discussion:
Detect when menu is chosen in JMenu
(too old to reply)
wx
2008-09-09 18:11:19 UTC
Permalink
I'd like to implement a menu with JMenu which has submenus. I'd like
to be able to click on an item or choose it with the keyboard with
enter or accelerator. This applies to both leaf menu items (without
submenus), but I'd also like to be able to choose JMenus (which have
submenus) and perform some action in that case. How to detect when a
JMenu is chosen? I tried adding an ActionListener, but that did not
work. I can implement mouse listener and key listener, but is there an
unified way to get "menu chosen" event, regardless of the method the
menu item has been chosen? I.e. is there something similar to
ItemListener, with the difference that the event should be fired when
the menu was actually chosen (e.g. mouse click, keyboard enter), not
only selected (e.g. mouse over, keyboard arrows)?
John B. Matthews
2008-09-09 19:04:08 UTC
Permalink
In article
Post by wx
I'd like to implement a menu with JMenu which has submenus. I'd like
to be able to click on an item or choose it with the keyboard with
enter or accelerator. This applies to both leaf menu items (without
submenus), but I'd also like to be able to choose JMenus (which have
submenus) and perform some action in that case. How to detect when a
JMenu is chosen? I tried adding an ActionListener, but that did not
work. I can implement mouse listener and key listener, but is there an
unified way to get "menu chosen" event, regardless of the method the
menu item has been chosen? I.e. is there something similar to
ItemListener, with the difference that the event should be fired when
the menu was actually chosen (e.g. mouse click, keyboard enter), not
only selected (e.g. mouse over, keyboard arrows)?
Sorry if I'm overlooking something, but the usual tutorial demos seem to
do this in actionPerformed() and itemStateChanged():

<http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html>
<http://www.javabeginner.com/jmenubar.htm>

Can you post an <http://pscode.org/sscce.html> that shows the problem?
--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
i***@gmail.com
2008-09-09 19:37:36 UTC
Permalink
Sure, should have done that at the beginning. Here's what I tried:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;

public class MenuPrb {
public static void main(String[] args) {
JFrame frame = new JFrame();
JMenuBar jmb = new JMenuBar();
frame.add(jmb);
JMenu menu1 = new JMenu("menu1");
jmb.add(menu1);

JMenu menu2 = new JMenu("menu2");
menu1.add(menu2);
menu2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("menu2: actionPerformed");
}
});
menu2.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.out.println("menu2: mouseClicked");
}
});
menu2.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
System.out.println("menu2: keyPressed");
}
public void keyReleased(KeyEvent e) {
System.out.println("menu2: keyReleased");
}
public void keyTyped(KeyEvent e) {
System.out.println("menu2: keyTyped");
}
});

for(int i = 0; i < 10; i++) {
JMenuItem mi = new JMenuItem("menu item " + i);
menu2.add(mi);
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("mi: actionPerformed");
}
});
}

frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

If you click on any of the menu items, it works. If you click on
menu2, it doesn't. That is what I would like to achieve - get clicks
from menu2. It can be done by menu2.addMouseListener(...) as above,
but that won't catch keyboard activation. menu2.addKeyListener doesn't
work also, as you can see - none of the keys pressed over menu2
trigger either action or key event handlers. Enter works on menu items
by triggering action events (in my opinion correct behavior).

Notice that in both links you provided (I've already read the first
one) they never set any listeners on JMenu, only on JMenuItem. Maybe
this is not what they had in mind, but I consider this quite oddly
designed in that case. After all, JMenu is a descendant of
AbstractButton. I'd expect a button to be clickable... Clickable in
the same way as other buttons - by mouse & keyboard, yielding
actionPerformed events.
John B. Matthews
2008-09-09 23:51:38 UTC
Permalink
In article
Post by i***@gmail.com
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class MenuPrb {
public static void main(String[] args) {
JFrame frame = new JFrame();
JMenuBar jmb = new JMenuBar();
frame.add(jmb);
JMenu menu1 = new JMenu("menu1");
jmb.add(menu1);
JMenu menu2 = new JMenu("menu2");
menu1.add(menu2);
menu2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("menu2: actionPerformed");
}
});
menu2.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.out.println("menu2: mouseClicked");
}
});
menu2.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
System.out.println("menu2: keyPressed");
}
public void keyReleased(KeyEvent e) {
System.out.println("menu2: keyReleased");
}
public void keyTyped(KeyEvent e) {
System.out.println("menu2: keyTyped");
}
});
for(int i = 0; i < 10; i++) {
JMenuItem mi = new JMenuItem("menu item " + i);
menu2.add(mi);
mi.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("mi: actionPerformed");
}
});
}
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
If you click on any of the menu items, it works. If you click on
menu2, it doesn't. That is what I would like to achieve - get clicks
from menu2. It can be done by menu2.addMouseListener(...) as above,
but that won't catch keyboard activation. menu2.addKeyListener doesn't
work also, as you can see - none of the keys pressed over menu2
trigger either action or key event handlers. Enter works on menu items
by triggering action events (in my opinion correct behavior).
I see. Normally, activating a menu item containing a submenu serves only
to display the submenu. Indeed, the left and right arrow keys hide and
show the submenu, as expected. You want to see that display event: you
can see the corresponding mouse event in a mouse listener, but the
keyboard event is not getting through. You might see if a FocusListener
will report the event.
Post by i***@gmail.com
Notice that in both links you provided (I've already read the first
one) they never set any listeners on JMenu, only on JMenuItem. Maybe
this is not what they had in mind, but I consider this quite oddly
designed in that case. After all, JMenu is a descendant of
AbstractButton. I'd expect a button to be clickable... Clickable in
the same way as other buttons - by mouse & keyboard, yielding
actionPerformed events.
I'm curious what meaning to attach to activating menu2, other than to
display the submenu.
--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
Knute Johnson
2008-09-09 23:37:21 UTC
Permalink
Post by wx
I'd like to implement a menu with JMenu which has submenus. I'd like
to be able to click on an item or choose it with the keyboard with
enter or accelerator. This applies to both leaf menu items (without
submenus), but I'd also like to be able to choose JMenus (which have
submenus) and perform some action in that case. How to detect when a
JMenu is chosen? I tried adding an ActionListener, but that did not
work. I can implement mouse listener and key listener, but is there an
unified way to get "menu chosen" event, regardless of the method the
menu item has been chosen? I.e. is there something similar to
ItemListener, with the difference that the event should be fired when
the menu was actually chosen (e.g. mouse click, keyboard enter), not
only selected (e.g. mouse over, keyboard arrows)?
Here is one simple way of doing that. There is one side effect of using
mnemonic and that is that if you select the top level menu with a
mnemonic then the next level will also be selected. That may be L&F
specific I don't know but it is the case on Win XP.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class test99 extends JFrame implements ActionListener, MenuListener {
public test99() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JMenuBar mb = new JMenuBar();
setJMenuBar(mb);

JMenu m1 = new JMenu("one");
m1.setMnemonic(KeyEvent.VK_O);
m1.addMenuListener(this);
JMenu s1 = new JMenu("sub one");
s1.setMnemonic(KeyEvent.VK_S);
s1.addMenuListener(this);
JMenuItem i1 = new JMenuItem("item one");
i1.setMnemonic(KeyEvent.VK_I);
i1.addActionListener(this);
JMenuItem i2 = new JMenuItem("item two");
i2.setMnemonic(KeyEvent.VK_T);
i2.addActionListener(this);

mb.add(m1);
m1.add(s1);
s1.add(i1);
s1.add(i2);

setSize(400,300);
setVisible(true);
}

public void actionPerformed(ActionEvent ae) {
System.out.println(ae.getActionCommand());
}

public void menuCanceled(MenuEvent me) {
}

public void menuDeselected(MenuEvent me) {
}

public void menuSelected(MenuEvent me) {
System.out.println(((JMenu)(me.getSource())).getText());
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new test99();
}
});
}
}
--
Knute Johnson
email s/nospam/knute2008/

--
Posted via NewsDemon.com - Premium Uncensored Newsgroup Service
------->>>>>>http://www.NewsDemon.com<<<<<<------
Unlimited Access, Anonymous Accounts, Uncensored Broadband Access
John B. Matthews
2008-09-10 01:23:52 UTC
Permalink
Post by Knute Johnson
Post by wx
I'd like to implement a menu with JMenu which has submenus. I'd like
to be able to click on an item or choose it with the keyboard with
enter or accelerator. This applies to both leaf menu items (without
submenus), but I'd also like to be able to choose JMenus (which have
submenus) and perform some action in that case. How to detect when a
JMenu is chosen? I tried adding an ActionListener, but that did not
work. I can implement mouse listener and key listener, but is there an
unified way to get "menu chosen" event, regardless of the method the
menu item has been chosen? I.e. is there something similar to
ItemListener, with the difference that the event should be fired when
the menu was actually chosen (e.g. mouse click, keyboard enter), not
only selected (e.g. mouse over, keyboard arrows)?
Here is one simple way of doing that. There is one side effect of using
mnemonic and that is that if you select the top level menu with a
mnemonic then the next level will also be selected. That may be L&F
specific I don't know but it is the case on Win XP.
[...]

FWIW, this approach works on Mac OS X, too.
--
John B. Matthews
trashgod at gmail dot com
home dot woh dot rr dot com slash jbmatthews
Stanimir Stamenkov
2008-09-10 04:45:54 UTC
Permalink
Post by wx
I'd like to implement a menu with JMenu which has submenus. I'd like
to be able to click on an item or choose it with the keyboard with
enter or accelerator. This applies to both leaf menu items (without
submenus), but I'd also like to be able to choose JMenus (which have
submenus) and perform some action in that case. How to detect when a
JMenu is chosen?
I've seen this come up before. Take a look at the
MenuSelectionManager [1] class. You may google for it to see
examples of its usage.

[1]
http://java.sun.com/javase/6/docs/api/javax/swing/MenuSelectionManager.html
--
Stanimir
wx
2008-09-10 13:17:46 UTC
Permalink
@Knute

Thanks for the suggestion. Unfortunately, this is not usable in my
case - the menu is dynamically generated, with content possibly
changing frequently. I could do some magic and assign different
mnemonics e.g. A, B, C, ... to different levels, but it would be quite
confusing to have different mnemonics.

@ Stanimir

I googled for this, but found nothing useful. All I found is using
this class inside the event handler:
http://www.java2s.com/Tutorial/Java/0240__Swing/UsingMenuSelectionManagertodeterminethecurrentselectionpath.htm

I'll search for some more - if you have some example link, that would
be helpful.

Doesn't it sound strange that the process is so complicated for such a
simple thing? I mean - click on the button (AbstractButton <-
JMenuItem). Thanks again for the help guys!
Stanimir Stamenkov
2008-09-10 13:50:29 UTC
Permalink
Post by wx
I googled for this, but found nothing useful.
[...]

O.k. Try this, google for "MenuSelectionManager" on the Java
Tutorial site (type this into the Google's search box):

MenuSelectionManager site:java.sun.com/docs/books/tutorial/

<http://www.google.com/search?q=MenuSelectionManager%20site%3Ajava.sun.com%2Fdocs%2Fbooks%2Ftutorial%2F>

You may also try searching this (and other comp.lang.java.*) group:

<http://groups.google.com/groups?as_q=MenuSelectionManager&as_ugroup=comp.lang.java.*>

You could get an idea of how to use MenuSelectionManager only by
looking at its API documentation (the API is pretty simple).
Post by wx
Doesn't it sound strange that the process is so complicated for such a
simple thing? I mean - click on the button (AbstractButton <-
JMenuItem). Thanks again for the help guys!
As far as I've got you don't want a button action listener but a
menu selection change listener which is quite different and specific.
--
Stanimir
Stanimir Stamenkov
2008-09-10 17:59:29 UTC
Permalink
Post by wx
Doesn't it sound strange that the process is so complicated for such a
simple thing? I mean - click on the button (AbstractButton <-
JMenuItem). Thanks again for the help guys!
As far as I've got you don't want a button action listener but a menu
selection change listener which is quite different and specific.
Reading it up once again I see I've got it wrong. Now I see you
want a single action listener for all menu items and you want it
called back for any new items there may be added to the container,
right? I'm sorry to say I haven't thought about that.
--
Stanimir
wx
2008-09-10 18:15:47 UTC
Permalink
Post by Stanimir Stamenkov
Reading it up once again I see I've got it wrong. Now I see you
want a single action listener for all menu items and you want it
called back for any new items there may be added to the container,
right? I'm sorry to say I haven't thought about that.
<
I don't know whether I understand this, but let me try to rephrase my
question. Assume you have the menu:
A
+- B
| +- C
| +- D
|
+- E
+- F
+- G

Now, assume you click on A. It would open this:
A
+- B >
+- E >

This is because A is e.g. JMenuBar that is activated by a click, not
by mouse roll over. Now, if you only roll over B and wait, but not
click, you will get the following:

A
+- B
| +- C
| +- D
|
+- E >

This is what is called selection. I can get notifications about this
by adding the following:

menu2.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
System.out.println("menu2: itemStateChanged: " +
e.getStateChange());
}
});

before the for loop in my code sample. This is not what I want.

Assume now, in this state, you actually move your mouse over C and
actually click on C (which is a JMenuItem). It will raise an action
event on the respective MenuItem instance. You also get this if you
type Enter while item C is selected. That is a normal behavior. What
is important is that method is not relevant - whether I click with the
mouse or press Enter key on my keyboard or I have an application
programmatically generate this event - is irrelevant and that is a
good abstraction. I get actionPerformed, which means the item has been
chosen.

Now, assume you move your mouse over B and actually click on B (which
is a JMenu). What I would like is that the action listener I
registered this receives this. The only thing I get is mouse clicked
event, as demonstrated by the example. I cannot get keyboard events,
nor can I get action events. This is logically incorrect from my
perspective:
- JMenu is a descendant of AbstractButton - you, by every logic,
should be able to click on buttons and react, shouldn't you?
- You can receive mouse, but not keyboard events
- You cannot determine that JMenu has been chosen, you can only
determine whether it was (de)selected

Hope this explains the problem and the solution I am searching for in
a better way.
Stanimir Stamenkov
2008-09-10 18:24:24 UTC
Permalink
Post by wx
Now, assume you move your mouse over B and actually click on B (which
is a JMenu). What I would like is that the action listener I
registered this receives this. The only thing I get is mouse clicked
event, as demonstrated by the example. I cannot get keyboard events,
nor can I get action events. This is logically incorrect from my
- JMenu is a descendant of AbstractButton - you, by every logic,
should be able to click on buttons and react, shouldn't you?
- You can receive mouse, but not keyboard events
- You cannot determine that JMenu has been chosen, you can only
determine whether it was (de)selected
Hope this explains the problem and the solution I am searching for in
a better way.
So you want action events happening for menu items which are menu
containers also, correct? I don't know whether this is possible as
the L&F most probably has attached an action listener to these to
control the UI, already. I guess you could override it with a proxy
to notify you and then invoke the original one, but it would be
clumsy in face of dynamic L&F changes.
--
Stanimir
wx
2008-09-10 19:07:32 UTC
Permalink
Post by Stanimir Stamenkov
So you want action events happening for menu items which are menu
containers also, correct?  
Exactly.
Post by Stanimir Stamenkov
I don't know whether this is possible as
the L&F most probably has attached an action listener to these to
control the UI, already.  
Yes, but I thought that why method isn't called setActionListener, but
addActionListeners - it should allow any number of listeners to be
attached. This is the case with normal buttons, right? At the end,
even if this was changed, there should be a good reason for this. Is
there one I don't see? Why would somebody make the code so it doesn't
allow me to listen to events?
Stanimir Stamenkov
2008-09-10 20:44:16 UTC
Permalink
Post by wx
Yes, but I thought that why method isn't called setActionListener, but
addActionListeners - it should allow any number of listeners to be
attached. This is the case with normal buttons, right?
You're right, it appears like that with normal buttons. But I seems
to remember something about adding an action listener caused the old
one to be removed/replaced with the new one.
Post by wx
At the end,
even if this was changed, there should be a good reason for this. Is
there one I don't see? Why would somebody make the code so it doesn't
allow me to listen to events?
I guess the menu UI (in contrast with the menu item UI) somehow
ignores the action listener as I guess it is not generally expected
to trigger an action, but just generates the events reported through
MenuListener. Sorry I could not help any better.
--
Stanimir
Knute Johnson
2008-09-10 17:21:30 UTC
Permalink
Post by wx
@Knute
Thanks for the suggestion. Unfortunately, this is not usable in my
case - the menu is dynamically generated, with content possibly
changing frequently. I could do some magic and assign different
mnemonics e.g. A, B, C, ... to different levels, but it would be quite
confusing to have different mnemonics.
Well your original post did not mention dynamically created menus. If
you aren't willing to accurately describe your problem when posting a
question, don't be surprised if you don't get the answer you want.

I answered your question because I was curious why you would want to
know when the JMenu was selected. This is not a normal requirement as
you can see from the method I used to determine the information.

I'm now curious to know why you think this solution would not work just
as well for dynamically created menus. You might consider fully
describing what you are trying to do and posting an SSCCE.
--
Knute Johnson
email s/nospam/knute2008/

--
Posted via NewsDemon.com - Premium Uncensored Newsgroup Service
------->>>>>>http://www.NewsDemon.com<<<<<<------
Unlimited Access, Anonymous Accounts, Uncensored Broadband Access
wx
2008-09-10 17:56:06 UTC
Permalink
@Stanimir

I found this and tried it - MenuSelectionManagerDemo. It doesn't do
anything when I click on the submenu. I'll take a look at the API,
maybe I can find something there.

@Knute
Post by Knute Johnson
Well your original post did not mention dynamically created menus. If
you aren't willing to accurately describe your problem when posting a
question, don't be surprised if you don't get the answer you want.
<
I consider the fact menus are being dynamically created or not
unrelevant in this case. Even if it was statically made, think about
the menu with 3 levels 10 items each. I'd like not to remember all the
mnemonics - I'd like to always use enter (or whatever is the default
for opening MenuItems) to choose the option.
Post by Knute Johnson
I answered your question because I was curious why you would want to
know when the JMenu was selected. This is not a normal requirement as
you can see from the method I used to determine the information.

I'm now curious to know why you think this solution would not work
just
as well for dynamically created menus. You might consider fully
describing what you are trying to do and posting an SSCCE.
<
I think you misunderstood me. I did not want to know when the menu was
selected (e.g. mouse rolled over submenu), but when it was chosen
(i.e. clicked at or the enter was pressed). If you are on WinXP or
similar, open your Start Menu/All Programs and double click on some
folder (e.g. Accessories). It will open that folder in Explorer. This
is similar to what I want to achieve - just replace "double click"
with "click". I can do that with mouse (& mouse listeners), but (as is
the case with Start Menu) I cannot do that with keyboard, which is
what I would like - and in some generic way - not when "key was
pressed", but when "menu was chosen", if I am able to describe the
logical difference between the two. E.g. you might consider that you
use Enter, but on some OS you might have Space activate the menu or
whatever. Hope this is a good explanation of my problem.

Thanks again guys!
Loading...