Focus on first jmenubar item on "alt" key press - java

Is there any way to auto-select a jmenu within my jmenubar when the user's pressing the "ALT" key ? (Like windows softwares)
The problem is that the default behavior of my jframe, when the "ALT" key is pressed, is to show up a menu containing the following actions : restore, move, size, reduce, ...
What I want my java application to do, is to select my jmenu first item when "alt" is pressed.
(Like it would do with a mnemonic : "alt + f")

Add the action to the ActionMap and InputMap of your JRootPane. See below:
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class MenuExample {
private void setupMenuKey(final JFrame frame) {
Action menuAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JRootPane rootPane = frame.getRootPane();
JMenuBar jMenuBar = rootPane.getJMenuBar();
JMenu menu = jMenuBar.getMenu(0);
menu.doClick();
}
};
JRootPane rootPane = frame.getRootPane();
ActionMap actionMap = rootPane.getActionMap();
final String MENU_ACTION_KEY = "expand_that_first_menu_please";
actionMap.put(MENU_ACTION_KEY, menuAction);
InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, 0, true), MENU_ACTION_KEY);
}
private JFrame build() {
JFrame frame = new JFrame("Hello");
frame.setSize(300, 300);
JMenuBar bar = new JMenuBar();
List<String> letters = Arrays.asList("A", "B", "C");
for (int i = 0; i < 3; i++) {
JMenu menu = new JMenu("Menu " + i);
for (String string : letters) {
menu.add(new JMenuItem(String.format("Menu %s - %s", i, string)));
}
bar.add(menu);
}
frame.setJMenuBar(bar);
JButton b = new JButton("click");
JPanel p = new JPanel();
p.add(b);
frame.add(p);
setupMenuKey(frame);
return frame;
}
public static void main(String[] args) {
MenuExample menuExample = new MenuExample();
JFrame frame = menuExample.build();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

Use actionmap and inputmap of JComponent to do this job. Since JFrame is not a descendant of JCompoenent i would suggest you adding JPanel to your jframe and then,
Action action = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
jMenu1.doClick();
}
};
jPanel1.getActionMap().put("myAction", action);
jPanel1.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, KeyEvent.ALT_DOWN_MASK), "myAction");

I've found the response very useful, but also with a drawback.
Imagine you're using Cross PlatForm L&F (Metal) and you've defined mnemonics to all the menus; you know that those menus are invoked against pressing . So, if we put the solution provided by black panda the results on a pressing are first we watch the desired menu deployed but immediately after switches to the first menu.
I think that the best way would be on ALT pressing only the first menu should be selected but without showing its contents (Those contents shoud appear on a down key pressing). Is there any way to do this in Cross Platform L&F?.
Alternatively there is another way to proceed without any extra code. If we invoke the System L&F (Windows in my case) the ALT key behaves as desired. See stackoverflow question 13474555 for details.

I have found just by using frame.setJMenuBar(jmenubar); where frame is your JFrame and jmenubar is your JMenuBar, it'll automatically do this. You don't even have to add it to your layout manager.

Related

Making a JButton act like a JMenu

I have the following code for a JMenuBar (This code has been taken from a free java program call JGuiD and edited for personal purposes)
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.awt.Dimension;
import java.awt.Color;
public class GuiDMenuBar extends JMenuBar
{
JMenu m_file,m_edit,m_help;
JMenuItem mi_f_new,mi_f_open,mi_f_save,mi_f_saveas,mi_f_exit;
JMenuItem mi_e_cut,mi_e_copy,mi_e_paste,mi_e_delete;
JMenuItem mi_v_motif,mi_v_java,mi_v_windows,mi_v_nimbus;
JMenuItem mi_h_help,mi_h_about;
JButton m_code;
public GuiDMenuBar()
{
setBorderPainted(true);
makeFileMenu();
makeEditMenu();
makeCodeButton();
makeHelpMenu();
}
void makeFileMenu()
{
m_file = new JMenu("File");
m_file.setMnemonic('F');
mi_f_new = new JMenuItem("New",new ImageIcon("icons/new_project.png"));
mi_f_new.setMnemonic('N');
mi_f_open = new JMenuItem("Open",new ImageIcon("icons/open_project.png"));
mi_f_open.setMnemonic('O');
mi_f_save = new JMenuItem("Save",new ImageIcon("icons/save.png"));
mi_f_save.setMnemonic('S');
mi_f_saveas = new JMenuItem("Save Java File",new ImageIcon("icons/saveas.png"));
mi_f_saveas.setMnemonic('J');
mi_f_exit = new JMenuItem("Exit",new ImageIcon("icons/exit.png"));
mi_f_exit.setMnemonic('X');
mi_f_new.setAccelerator(KeyStroke.getKeyStroke("control N"));
mi_f_open.setAccelerator(KeyStroke.getKeyStroke("control O"));
mi_f_save.setAccelerator(KeyStroke.getKeyStroke("control S"));
mi_f_saveas.setAccelerator(KeyStroke.getKeyStroke("control J"));
mi_f_exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,InputEvent.ALT_MASK));
m_file.add(mi_f_new);
m_file.add(mi_f_open);
m_file.addSeparator();
m_file.add(mi_f_save);
m_file.add(mi_f_saveas);
m_file.addSeparator();
m_file.add(mi_f_exit);
add(m_file);
}
void makeEditMenu()
{
m_edit = new JMenu("Edit");
m_edit.setMnemonic('E');
mi_e_cut = new JMenuItem("Cut",new ImageIcon("icons/cut.png"));
mi_e_cut.setMnemonic('X');
mi_e_copy = new JMenuItem("Copy",new ImageIcon("icons/copy.png"));
mi_e_copy.setMnemonic('C');
mi_e_paste = new JMenuItem("Paste",new ImageIcon("icons/paste.png"));
mi_e_paste.setMnemonic('P');
mi_e_delete = new JMenuItem("Delete",new ImageIcon("icons/delete.png"));
mi_e_delete.setMnemonic('D');
mi_e_cut.setAccelerator(KeyStroke.getKeyStroke("control X"));
mi_e_copy.setAccelerator(KeyStroke.getKeyStroke("control C"));
mi_e_paste.setAccelerator(KeyStroke.getKeyStroke("control V"));
mi_e_delete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0));
m_edit.add(mi_e_cut);
m_edit.add(mi_e_copy);
m_edit.add(mi_e_paste);
m_edit.add(mi_e_delete);
add(m_edit);
}
void makeHelpMenu()
{
m_help = new JMenu("Help");
m_help.setMnemonic('H');
mi_h_help = new JMenuItem("Help",new ImageIcon("icons/help.png"));
mi_h_help.setMnemonic('H');
mi_h_about = new JMenuItem("About");
mi_h_about.setMnemonic('A');
mi_h_help.setAccelerator(KeyStroke.getKeyStroke("F1"));
mi_h_about.setAccelerator(KeyStroke.getKeyStroke("control A"));
m_help.add(mi_h_help);
m_help.addSeparator();
m_help.add(mi_h_about);
add(m_help);
}
void makeCodeButton()
{
m_code = new JButton();
m_code.setOpaque(false);
m_code.setContentAreaFilled(false);
m_code.setBorder(null);
m_code.setFocusable(false);
m_code.setText("Code Shift C");
Dimension dBt = new Dimension(75,25);
m_code.setMinimumSize(dBt);
m_code.setPreferredSize(dBt);
m_code.setMaximumSize(dBt);
m_code.getModel().addChangeListener(new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e)
{
ButtonModel model = (ButtonModel) e.getSource();
if(model.isRollover())
{
m_code.setBackground(Color.RED);
m_code.setOpaque(true);
}
else
{
m_code.setBackground(null);
m_code.setOpaque(false);
m_code.setContentAreaFilled(false);
}
}
});
m_code.setMnemonic('C');
add(m_code);
}
public void addListeners(ActionListener al)
{
mi_f_new.addActionListener(al);
mi_f_open.addActionListener(al);
mi_f_save.addActionListener(al);
mi_f_saveas.addActionListener(al);
mi_f_exit.addActionListener(al);
mi_e_cut.addActionListener(al);
mi_e_copy.addActionListener(al);
mi_e_paste.addActionListener(al);
mi_e_delete.addActionListener(al);
mi_h_help.addActionListener(al);
mi_h_about.addActionListener(al);
m_code.addActionListener(al);
}
}
My aim is to make the JButton to appear like it is a JMenu. This would entail that the button only changes colour when I am interacting with the rest of the JMenuBar not just when I hover over it. For example if I had clicked on the JMenu m_file and then hovered over the JButton the background would change, however if I was not previously interacting with the JMenuBar it should not change the background when I hover over the JButton. The next thing required would be the JMenuBar treats it as a JMenu. I mean this in the sense that when F10 is clicked and the first JMenu is selected. After which you can then click on the right arrow on the arrow key pad on your keyboard, this will select the next JMenu. However using this method of navigation skips the JButton and does not allow you to interact with the JButton in anyway. I also mean this in the sense that if you interact with a JMenu and then hover over the JButton the JMenuBar shows you are still hovering over the JMenu as well (See Image).
So I suppose I have three questions.
How do you get the JButton to only change colour if the JMenuBar is already being interacted with?
How do you get the JMenuBar to treat the JButton as a JMenu in the senses that I described?
Is anyone aware of the exact colour of the background of the JMenu when you hover over it? Since I would prefer to change my JButton's background to the same colour of the JMenu's background instead of it just being red.
Thanks,
Dan

invisible JMenuBar, Accelerator not working

My Program has a JMenuBar with JMenuItems.
They have a ActionListener, and I set a Shortcut with setAccelerator.
Now I am hiding the menu bar when the window become unfocused, to get more space for a displayed image.
But after the first hiding of the menubar, the hotkeys just stop working.
How can I fix that?
I created a little example code to illustrate that strange behavior:
import javax.swing.*;
import java.awt.event.*;
class Example extends JFrame{
public static void main(String[] args) {
new Example(); //main is static
}
static JMenuBar menubar; //be accessable for the ActionListener
Example() {
//JPanel
this.setSize(50,50);
this.setVisible(true);
//Menubar, static
menubar = new JMenuBar();
this.setJMenuBar(menubar);
//Menu
JMenu filemenu = new JMenu("File");
menubar.add(filemenu);
//Item
JMenuItem menuitem = new JMenuItem("Do Something...");
filemenu.add(menuitem);
menuitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.SHIFT_DOWN_MASK)); // Shift + D
menuitem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Action!");
}
});
JButton button = new JButton("Show/Hide menubar");
this.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Toggle Bar!");
menubar.setVisible(!menubar.isVisible()); //Toggle
}
});
}
}
For reference:
I'm using Java 1.7.0_60-ea (Java 7) on a Mac.
But this error occurs independent of using the Mac native menu bar or the normal java menu bar inside the JFrame.
You could try to add global keybindings. How to add keybindings is explained here.
Here is an example of what you could do:
//Any component that is always visible in the window (like the image)
JComponent c;
//Get input and action map
InputMap imap = c.getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap amap = c.getActionMap();
//Put keybinding and action
imap.put(KeyStroke.getKeyStroke("shift D"), "doSomething");
amap.put("doSomething", anAction);
Note that it only works in the focused window. But should work regardless of the menubar being visible or not.

Add accelerator description to JMenuItem in GUI, without actually adding an accelerator

I'm trying to set some text to be displayed where the accelerator binding is usually displayed, for a JMenuItem.
The demarcated Ctrl+Z text in the following image is an example of what I'm trying to set, for another JMenuItem.
I don't actually want to set an accelerator for this JMenuItem, though.
I've poked around the source for several classes, like JMenuItem and BasicMenuItemUI, to no avail.
What's the simplest way to achieve this?
Thanks in advance :)
I assume the reason you want this is so you can prevent the menu from triggering the undo action a second time, when the key combination is already bound on a component on the frame, but this shouldn't be necessary. If the component consumes the key event, the menu won't detect it.
Here's an example with a JTextArea to see what I mean:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JMenuBar menu = new JMenuBar();
frame.setJMenuBar(menu);
JMenu menuEdit = new JMenu("Edit");
menu.add(menuEdit);
JMenuItem menuEditUndo = new JMenuItem("Undo");
menuEdit.add(menuEditUndo);
menuEditUndo.setAccelerator(KeyStroke.getKeyStroke("control Z"));
menuEditUndo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("menu");
}
});
JTextArea textArea = new JTextArea(20, 40);
textArea.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "undo");
textArea.getActionMap().put("undo", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("text");
}
});
frame.add(new JScrollPane(textArea));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Both the text area and the menu item have bound to the same key combo, but pressing Ctrl+Z while the text area has focus prints only "text" and never "menu". I.e., the action does not happen twice. Although this uses a JTextArea, it should be true of any component.

JMenu consumes focuslost event in Windows7 LAF Java7

If a popup menu is still open when another component is clicked, then the component does not get the event, because it's probably consumed by the popup. This happens for all JPopupmenus in general.
This happens only in Java 7 with windows LAF (Windows7). Is there a workaround? Is it a known bug?
import javax.swing.*;
import java.awt.event.*;
public class Test
{
public static void main(String[] s)
throws Exception
{
String lookAnfFeelClassName = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(lookAnfFeelClassName);
JMenu menu = new JMenu("TEST Menu");
JMenuItem menuItem = new JMenuItem("Menu Item 1");
JMenuBar menuBar = new JMenuBar();
menu.add(menuItem);
menuBar.add(menu);
final JButton b = new JButton("Test");
b.setBounds(5, 50, 60, 20);
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//If the Menu is open when I press the button, the putton is not pressed
//so I have to press it again.
JOptionPane.showMessageDialog(b, "Button Pressed");
}
}
);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(150, 150);
frame.setJMenuBar(menuBar);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(b);
frame.setVisible(true);
}
}
Here is the magic line that fixes the problem:
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
I found this after looking into the source code for the BasicPopupMenuUI class. Apparently this behaviour is a deliberate design choice according to the following comments in the code, but it sure feels like a bug to me.
// Ask UIManager about should we consume event that closes
// popup. This made to match native apps behaviour.
By the way, it happens in Java 5 and 6 too.

Java JMenuItem ActionListener

I need some help with my ActionListener on my JMenuBar.
There's no error; however every time I click on a JMenuItem, it triggers all the action associated with the JMenuItem. My question is: am I doing it right in my ActionListener code? I'm not too sure about my ActionListener class. I'm trying to separate my ActionListener from my Button logic.
If anyone has any ideas on what I may be doing wrong, please point it out.
Here is my code:
package MenuBar;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class SimpleMenuBar{
private static final LayoutManager grid = new GridLayout(0, 1);
public JMenuBar MenuBar;
public JMenu MenuFile, MenuEdit, MenuOption;
public JMenuItem ItemNew, ItemOpen, ItemSave, ItemExit, ItemCopy, ItemCut, ItemPaste;
ButtonGroup direction;
JRadioButtonMenuItem forwardradio, backwardradio;
JCheckBoxMenuItem CheckCase;
String input = null;
public void Design()
{
JFrame frame = new JFrame("Simple Menubar");
frame.setSize(320, 320);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
MenuBar = new JMenuBar();
frame.setJMenuBar(MenuBar);
MenuBar.setLayout(grid);
//Menus
MenuFile = new JMenu("File");
MenuFile.setMnemonic(KeyEvent.VK_F);
MenuBar.add(MenuFile);
//sub Menus
ItemNew = new JMenuItem("New", KeyEvent.VK_N);
MenuFile.add(ItemNew);
ItemNew.setActionCommand("New");
ItemNew.addActionListener(new MenuBarMethod());
ItemOpen = new JMenuItem("Open", KeyEvent.VK_O);
MenuFile.add(ItemOpen);
ItemOpen.setActionCommand("Open");
ItemNew.addActionListener(new MenuBarMethod());
MenuFile.addSeparator();
ItemSave = new JMenuItem("Save", KeyEvent.VK_S);
MenuFile.add(ItemSave);
MenuFile.addSeparator();
ItemExit = new JMenuItem("Exit", KeyEvent.VK_X);
MenuFile.add(ItemExit);
MenuEdit = new JMenu("Edit");
MenuFile.setMnemonic(KeyEvent.VK_E);
MenuBar.add(MenuEdit);
ItemCopy = new JMenuItem("Copy", KeyEvent.VK_C);
KeyStroke ctrlCKeyStroke = KeyStroke.getKeyStroke("control C");
ItemCopy.setAccelerator(ctrlCKeyStroke);
MenuEdit.add(ItemCopy);
ItemCut = new JMenuItem("Cut", KeyEvent.VK_V);
KeyStroke ctrlVKeyStroke = KeyStroke.getKeyStroke("control V");
ItemCut.setAccelerator(ctrlVKeyStroke);
MenuEdit.add(ItemCut);
ItemPaste = new JMenuItem("Paste", KeyEvent.VK_Y);
KeyStroke ctrlYKeyStroke = KeyStroke.getKeyStroke("control Y");
ItemPaste.setAccelerator(ctrlYKeyStroke);
ItemPaste.setEnabled(false);
MenuEdit.add(ItemPaste);
MenuEdit.addSeparator();
MenuOption = new JMenu("Option");
Icon atIcon = new ImageIcon("option.png");
MenuOption.setIcon(atIcon);
MenuOption.setMnemonic(KeyEvent.VK_O);
direction = new ButtonGroup();
forwardradio = new JRadioButtonMenuItem("Forward Me", true);
forwardradio.setMnemonic(KeyEvent.VK_F);
MenuOption.add(forwardradio);
direction.add(forwardradio);
MenuEdit.add(MenuOption);
backwardradio = new JRadioButtonMenuItem("Backward Me");
backwardradio.setMnemonic(KeyEvent.VK_B);
MenuOption.add(backwardradio);
direction.add(backwardradio);
MenuOption.addSeparator();
CheckCase = new JCheckBoxMenuItem("Case Sensitive");
MenuOption.add(CheckCase);
direction.add(CheckCase);
MenuEdit.add(MenuOption);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
SimpleMenuBar MyMenu = new SimpleMenuBar();
MyMenu.Design();
}
});
}
}
It's an awful lot of code for just a menu bar. I haven't fully implemented the ActionListener. For testing purposes, I've only implemented it for two items: ItemNew and ItemOpen. Unfortunately, every time I click on the ItemNew menu item, it triggers the actions of both ItemNew and ItemOpen.
Here are is code for my action listener:
package MenuBar;
import java.awt.event.*;
import javax.swing.JOptionPane;
public class MenuBarMethod implements ActionListener{
public void actionPerformed(ActionEvent e)
{
if("New".equals(e.getActionCommand())){
JOptionPane.showMessageDialog(null, "Selected Item: " + e.getActionCommand());
}
if("Open".equals(e.getActionCommand())){
JOptionPane.showMessageDialog(null, "Selected Item: " + e.getActionCommand());
}
}
}
As a matter of personal preference, I prefer to use the Action API
The main reasons are
It centralise logic associated with a particular action, including name, key stroke, short cuts, icons etc
It's reusable. It's much easier to create an Action Object then it's to have recreate the menu item & the tool bar item & the popup menu item
It's well supported by other Swing components. Most Swing components that support the ActionListener API generally provide the means to supply an Action as well (JMenu.add(Action), JMenu(Action) etc)
It makes your code easier to read, much easier to read a single ActionPerformed method of an Action class then try and skim through multiple if statements
Take a look at How to Use Actions
IMHO
I would use the getSource method of ActionEvent instead of getActionCommand.
public class MenuBarMethod implements ActionListener{
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == ItemNew){
JOptionPane.showMessageDialog(null, "Selected Item: " + e.getActionCommand());
}
if(e.getSource() == ItemOpen){
JOptionPane.showMessageDialog(null, "Selected Item: " + e.getActionCommand());
}
}
}

Categories

Resources