Adding a scrollable menu to a submenu - java

I'm trying to adapt to submenus the code found at https://coderanch.com/t/343946/java/Scrolling-JMenu in order to have a scrollbar rather than buttons like suggested in the answer of Java: Creating a scrolling submenu. However I am having a some troubles for getting a "standard" behaviour:
The parent menu should not disappear when the mouse is focused/hovering on the scrollable menu (see Font menu).
The scrollable menu should disappear when the mouse is not focused/hovering on it or on its parent submenu (see Font menu).
The scrollable menu should not disappear even if the text of the submenu is long (see Help menu).
Thanks in advance for your help. Here's my code:
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
public class ScrollingMenu extends JFrame {
private final JPopupMenu fontNameMenu = new JPopupMenu();
private final JMenu[] fontMenuArray = new JMenu[] {new JMenu("Font Name"), new JMenu("Font Style")};
private JMenu menu = new JMenu("Font");
private final JMenu[] helpMenuArray = new JMenu[] {new JMenu("Very very very very very very very long Font Name Menu")};
private JMenu hmenu = new JMenu("Help");
public static void main(String[] args) {
JFrame scrollingMenu = new ScrollingMenu();
scrollingMenu.setVisible(true);
}
public ScrollingMenu() {
// Creating a long array
String[] systemFontArray = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
JCheckBoxMenuItem[] fontNameArray = new JCheckBoxMenuItem[systemFontArray.length];
// Creating the scrollable menu
GridBagLayout fontNameLayout=new GridBagLayout();
JPanel fontNamePanel = new JPanel();
fontNamePanel.setLayout(fontNameLayout);
for (int i=0;i<systemFontArray.length;i++) {
fontNameArray[i] = new JCheckBoxMenuItem(systemFontArray[i]);
fontNameLayout.setConstraints(fontNameArray[i],new GridBagConstraints(0,i,1,1,0,0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0));
fontNamePanel.add(fontNameArray[i]);
}
JScrollPane fontScrollPane = new JScrollPane(fontNamePanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.fontNameMenu.add(fontScrollPane);
// Adding the scrollable menu to a submenu
addML(ScrollingMenu.this.fontMenuArray[0], ScrollingMenu.this.fontNameMenu, new Dimension(200, 200));
// Creating the Font Style submenu (for underlining problem 2)
this.fontMenuArray[1].add(new JCheckBoxMenuItem("Bold"));
this.fontMenuArray[1].add(new JCheckBoxMenuItem("Italic"));
this.fontMenuArray[1].add(new JCheckBoxMenuItem("Plain"));
// Adding the submenus to the Font menu
for (int i=0;i<this.fontMenuArray.length;i++) {
this.menu.add(this.fontMenuArray[i]);
}
// Adding the scrollable menu to a long text submenu (problem 3)
addML(ScrollingMenu.this.helpMenuArray[0], ScrollingMenu.this.fontNameMenu, new Dimension(200, 200));
// Adding the submenu to the Help menu
for (int i=0;i<this.helpMenuArray.length;i++) {
this.hmenu.add(this.helpMenuArray[i]);
}
JMenuBar menuBar = new JMenuBar();
menuBar.add(this.menu);
menuBar.add(this.hmenu);
this.setJMenuBar(menuBar);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
}
private static void addML (JMenu subMenu, JPopupMenu popupMenu, Dimension dimension) {
popupMenu.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent focusEvent) {
if(popupMenu.isVisible() && !subMenu.isSelected())
popupMenu.setVisible(false);
}
});
popupMenu.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent mouseEvent) {
if(!subMenu.isSelected())
popupMenu.setVisible(false);
}
});
popupMenu.setInvoker(subMenu);
popupMenu.setPreferredSize(dimension);
subMenu.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
if(!popupMenu.isVisible())
displayFontNameMenu(subMenu, popupMenu);
else
popupMenu.setVisible(false);
}
#Override
public void mouseEntered(MouseEvent mouseEvent) {
if(!popupMenu.isVisible() && subMenu.isSelected())
displayFontNameMenu(subMenu, popupMenu);
}
#Override
public void mouseExited(MouseEvent mouseEvent) {
if(popupMenu.isVisible() && !popupMenu.contains(mouseEvent.getPoint())) {
popupMenu.setVisible(false);
}
}
});
}
private static void displayFontNameMenu(JMenu subMenu, JPopupMenu popupMenu) {
Rectangle rectangle = subMenu.getBounds();
Point point = new Point(rectangle.x +rectangle.width, rectangle.y);
SwingUtilities.convertPointToScreen(point, subMenu.getParent());
popupMenu.setLocation(point.x, point.y);
popupMenu.setVisible(true);
}
}

Related

How to get/set JRadionButtonMenuItem dynamically?

This example has Radio Buttons located on a Sub Menu as seen here.
What I would like to do is anytime the "Change Radio Button" button is pressed, it will change which button is selected on the menu. This means it has to first retrieve which is currently set then select the other.
Granted for this simple sample the Radio Buttons could be made instance variables to make things easy but image the JMenu and associated sub-menus and radio buttons are generated is some class further down in the bowels of the program. Direct access is not that direct.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
public class RadioButtonsOnMenu
{
public static void main(final String args[])
{
JFrame frame = new JFrame("MenuSample Example");
JButton jButton = new JButton("Change Radio Button");
jButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.out.println("Changing Radion Button");
//How to change the JButton on the menu?
//frame.getMenuBar().......
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel jPanel = new JPanel();
jPanel.add(jButton);
frame.add(jPanel);
frame.setJMenuBar(buildMenu());
frame.setSize(350, 250);
frame.setVisible(true);
}
public static JMenuBar buildMenu()
{
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic(KeyEvent.VK_F);
menuBar.add(fileMenu);
JMenuItem newMenuItem = new JMenuItem("New", KeyEvent.VK_N);
fileMenu.add(newMenuItem);
JMenu findOptionsMenu = new JMenu("Options");
findOptionsMenu.setMnemonic(KeyEvent.VK_O);
fileMenu.add(findOptionsMenu);
ButtonGroup directionGroup = new ButtonGroup();
JRadioButtonMenuItem forwardMenuItem = new JRadioButtonMenuItem("Forward", true);
forwardMenuItem.setMnemonic(KeyEvent.VK_F);
findOptionsMenu.add(forwardMenuItem);
directionGroup.add(forwardMenuItem);
JRadioButtonMenuItem backwardMenuItem = new JRadioButtonMenuItem("Backward");
backwardMenuItem.setMnemonic(KeyEvent.VK_B);
findOptionsMenu.add(backwardMenuItem);
directionGroup.add(backwardMenuItem);
return menuBar;
}
}
It is not clear how best to access the sub-menu and associated radio button settings within the JButton action.
jButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.out.println("Changing Radion Button");
//How to change the JButton on the menu?
//frame.getMenuBar().......
}
});
I could probably get the Menu Bar from the Frame and drill down figure the code can get messy if there Menu Bar has numerous items, sub items and even multiple radio button groups.
Is there a more direct way to find out which Radio Buttons on the menu are selected as well as a more direct way to change their value?
The "trick" is to create an application model to hold the value of the menu radio buttons.
Here's the GUI I created.
I started the Swing application with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame and a JButton JPanel. I separated the creation of the JFrame and JPanel.
I created an application model class to hold a boolean which determines whether forward or backward is selected. The JButton ActionListener switches the state of the boolean. The updateRadioButtonMenu method updates the selected state of the radio button menu items.
Here's the complete runnable code.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.SwingUtilities;
public class RadioButtonsOnMenu implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RadioButtonsOnMenu());
}
private ApplicationModel model;
private JRadioButtonMenuItem backwardMenuItem;
private JRadioButtonMenuItem forwardMenuItem;
public RadioButtonsOnMenu() {
this.model = new ApplicationModel();
}
#Override
public void run() {
JFrame frame = new JFrame("MenuSample Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(createMenuBar());
frame.add(createButtonPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic(KeyEvent.VK_F);
menuBar.add(fileMenu);
JMenuItem newMenuItem = new JMenuItem("New", KeyEvent.VK_N);
fileMenu.add(newMenuItem);
JMenu findOptionsMenu = new JMenu("Options");
findOptionsMenu.setMnemonic(KeyEvent.VK_O);
fileMenu.add(findOptionsMenu);
ButtonGroup directionGroup = new ButtonGroup();
forwardMenuItem = new JRadioButtonMenuItem("Forward", model.isForward());
forwardMenuItem.setMnemonic(KeyEvent.VK_F);
findOptionsMenu.add(forwardMenuItem);
directionGroup.add(forwardMenuItem);
backwardMenuItem = new JRadioButtonMenuItem("Backward", !model.isForward());
backwardMenuItem.setMnemonic(KeyEvent.VK_B);
findOptionsMenu.add(backwardMenuItem);
directionGroup.add(backwardMenuItem);
return menuBar;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(100, 100, 100, 100));
JButton button = new JButton("Change Radio Button");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
model.setForward(!model.isForward());
updateRadioButtonMenu();
}
});
panel.add(button);
return panel;
}
public void updateRadioButtonMenu() {
forwardMenuItem.setSelected(model.isForward());
backwardMenuItem.setSelected(!model.isForward());
}
public class ApplicationModel {
private boolean isForward;
public ApplicationModel() {
this.isForward = true;
}
public boolean isForward() {
return isForward;
}
public void setForward(boolean isForward) {
this.isForward = isForward;
}
}
}
What you could do is to save the state in a boolean. You could add listener to the radio buttons and change the boolean everytime one of them is selected
boolean isForward = true;
so when changed to backward you set the value to false. This way you don't need to get The state of the radiobuttons everytime.
In your button actionlistener you could then do:
jButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
System.out.println("Changing Radion Button");
forwardRadioButton.setState(!isForward);
backwardRadioButton.setState(isForward);
iSForward = !isForward;
}
});
I would say you need to do these two things:
Since you seem to have a lot of items it is best to store them somewhere else
1 Create a class to store all the items and then just pass the class
public class myItemHolder{
//declare all the items here instead of at the main
JButton jButton = new JButton("Change Radio Button");
ButtonGroup directionGroup = new ButtonGroup();
JRadioButtonMenuItem forwardMenuItem = new JRadioButtonMenuItem("Forward", true);
JRadioButtonMenuItem backwardMenuItem = new JRadioButtonMenuItem("Backward");
myListener(myItemHolder items){
directionGroup.add(forwardMenuItem);
}
public ButtonGroup getButtons() {
return directionGroup;
}
public JButton getClick() {
return jButton;
}
}
2 Create your own action listener class like so
public class myListener implements ActionListener{
myItemHolders items;
myListener(myItemHolder items){
this.items=items;
}
#Override
public void actionPerformed(ActionEvent e) {
//get the radiobutton like so and do what you want with it
items.getButtons()
}
}
Now you just need to do this in the main:
public class RadioButtonsOnMenu
{
public static void main(final String args[])
{
myItemHolder items = new myItemHolder();
items.getClick.addActionListener(new myListener(items));
}
}
And there you can access everything easily :)
It is all a matter of where you declare your stuff.
Or if you only want to send the ButtonGroup you can just do so by changing the structure a little so the actionlistener only requests the ButtonGroup and give it items.getButtons() instead of items.
Here's another possible way to go about it using Enum state tracking. I make use of a Enum and a Map to track the Radio Button that should be activated. This lets it scale as large as you want for associated Radio Button items within the same JMenu.
RadioMenu
package tools;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
public class RadioMenu<E extends Enum<E>> extends JMenu {
private static final long serialVersionUID = 1L;
private E currentState;
private JRadioButtonMenuItem selectedRadioButton;
private HashMap<E, JRadioButtonMenuItem> stateMap;
public RadioMenu() {
stateMap = new HashMap<E, JRadioButtonMenuItem>();
}
public RadioMenu(String name) {
super(name);
stateMap = new HashMap<E, JRadioButtonMenuItem>();
}
public void addRadioButton(E associatedState, JRadioButtonMenuItem radioButton) {
//Set default to first added button
if(stateMap.isEmpty()) {
currentState = associatedState;
radioButton.setSelected(true);
selectedRadioButton = radioButton;
}
add(radioButton);
stateMap.put(associatedState, radioButton);
radioButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setState(associatedState);
}
});
}
public void generateButtonsFromEnum(Class<E> enumType) {
for(E enumValue : enumType.getEnumConstants()) {
addRadioButton(enumValue, new JRadioButtonMenuItem(enumValue.toString()));
}
}
public E getState() {
return currentState;
}
public void setState(E newState) {
currentState = newState;
selectedRadioButton.setSelected(false);
selectedRadioButton = stateMap.get(newState);
selectedRadioButton.setSelected(true);
}
}
RadioMenuTest
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import tools.RadioMenu;
public class RadioMenuTest implements Runnable {
public enum RadioOptions {
Forward, Backward, Left, Right
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new RadioMenuTest());
}
private RadioMenu<RadioOptions> optionsMenu;
#Override
public void run() {
JFrame frame = new JFrame("RadioMenu Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(createMenuBar());
frame.getContentPane().add(createButtonPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
optionsMenu = new RadioMenu<RadioOptions>("Options");
optionsMenu.generateButtonsFromEnum(RadioOptions.class);
fileMenu.add(optionsMenu);
return menuBar;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
JButton setBackwardButton = new JButton("Set To Backward");
setBackwardButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
optionsMenu.setState(RadioOptions.Backward);
}
});
panel.add(setBackwardButton);
JButton setRightButton = new JButton("Set To Right");
setRightButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
optionsMenu.setState(RadioOptions.Right);
}
});
panel.add(setRightButton);
return panel;
}
}

JMenuItem reuse

Here is the minimal working application :
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class JMenuItemReuse {
public static void main(String[] args) {
SwingUtilities.invokeLater(TestFrame::new);
}
}
class TestFrame extends JFrame {
public TestFrame() {
super();
JPanel panel = new JPanel();
JPopupMenu menu1 = new JPopupMenu();
JPopupMenu menu2 = new JPopupMenu();
JMenuItem item1 = new JMenuItem("reused");
JMenuItem item2 = new JMenuItem("not reused");
menu1.add(item1);
menu2.add(item1); // works if this line is commented
menu2.add(item2);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (SwingUtilities.isRightMouseButton(e)) {
menu1.show(panel, e.getX(), e.getY());
} else {
menu2.show(panel, e.getX(), e.getY());
}
}
});
panel.add(new JLabel("popup-test"));
add(panel);
setPreferredSize(new Dimension(400, 400));
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
The problem is that the popup menus do not work as expected when at least one menu item is reused. It is not a big issue, can be easily avoided by duplicating the reused menu item, but I am still wondering why does it happen. Any ideas?
A JMenuItem belongs to one, and only one, JPopupMenu (or any other menu). You cannot add a Swing component to more than one container; if you do, then the component will automatically be removed from the previous container.
Actaully if you want, you can create Actions. Actions can be shared and added to multiple components (JMenuItems, JButtons etc). You can even enable/disable the Action which will enable/disable all the components at the same time.

How to hide Swing accelerators by a text pane?

I have a Swing application with multiple panes. Some of the panes are text ones (JTextPane), some are dialog-like (with buttons and sliders) and some are graphical (custom painted ). I have a few actions defined in the main menu with simple accelerators like K, P or O.
I would like those accelerators to be processed by the menu actions only if the currently focused pane is not processing them. Specifically, I do not want them to be processed by the menu when the user is just typing in a text pane.
I am creating actions and menu items using:
action = new javax.swing.AbstractAction
new MenuItem(action)
I am registering accelerators with:
action.putValue(javax.swing.Action.ACCELERATOR_KEY, keyStroke)
Is it possible to "eat" (suppress) the key press event for the keys which are processed in the text panes so that they are not passed to the main menu for the global processing?
If not, are there some alternatives to do something similar, like to register the accelerators I know should not be processed when in a text pane for some panes only?
I am adding a code based on an answer to make the question clearer (and to make developing alternate solutions easier):
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;
public class TestMenuBindings {
public static void main(String[] args) {
JMenuBar menuBar = new JMenuBar();
final JMenu menu = new JMenu("Print");
final Action oAction = new PrintAction("O",KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
menu.add(oAction);
menuBar.add(menu);
JFrame frm = new JFrame("Frame");
frm.setJMenuBar(menuBar);
JTextArea area = new JTextArea("Here I want no accelerators", 10, 40);
frm.add(new JScrollPane(area));
frm.add(new JTextField("Here I want accelerators working"), BorderLayout.SOUTH);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.pack();
frm.setVisible(true);
}
private static class PrintAction extends AbstractAction {
private String str;
public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
super("Print: " + aPrintStr);
str = aPrintStr;
putValue(Action.ACCELERATOR_KEY, aMnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(str);
}
}
}
Here is the example. In text area no key bindings working. In text field work all key bindings. Also all the menu items are accessible (enabled) from the menu.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;
public class TestMenuBindings {
public static void main(String[] args) {
JMenuBar menuBar = new JMenuBar();
final JMenu menu = new JMenu("Print");
menu.add(new PrintAction("O", KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)));
menu.add(new PrintAction("K", KeyStroke.getKeyStroke(KeyEvent.VK_K, 0)));
menu.add(new PrintAction("P", KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)));
menuBar.add(menu);
JFrame frm = new JFrame("Frame");
frm.setJMenuBar(menuBar);
JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
area.addFocusListener(new FocusListener() {
#Override
public void focusLost(FocusEvent e) {
setItemStatus(menu, true);
}
#Override
public void focusGained(FocusEvent e) {
setItemStatus(menu, false);
}
});
frm.add(new JScrollPane(area));
frm.add(new JTextField("Here working accelerators"), BorderLayout.SOUTH);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.pack();
frm.setVisible(true);
}
private static void setItemStatus(JMenu aMenu, boolean aStatus) {
for (Component item : aMenu.getMenuComponents()) {
((JMenuItem) item).getAction().setEnabled(aStatus);
}
}
private static class PrintAction extends AbstractAction {
private String str;
public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
super("Print: " + aPrintStr);
str = aPrintStr;
putValue(Action.ACCELERATOR_KEY, aMnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(str);
}
}
}
Here is a solution using KeyBindinds, as suggested by camickr. It is shorter than the one provided by Sergiy Medvynskyy, and I find it more straightforward, but it has a drawback the shortcut is not displayed in the menu, which is a result of the shortcut not being defined in the action itself.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.WindowConstants;
public class TestMenuBindings {
public static void main(String[] args) {
JMenuBar menuBar = new JMenuBar();
final JMenu menu = new JMenu("Print");
final Action oAction = new PrintAction("O");
menu.add(oAction);
menuBar.add(menu);
JFrame frm = new JFrame("Frame");
frm.setJMenuBar(menuBar);
JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
frm.add(new JScrollPane(area));
frm.add(new JTextField("Here working accelerators") {
{
getInputMap(WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0), "command_O");
getActionMap().put("command_O", oAction);
}
}, BorderLayout.SOUTH);
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.pack();
frm.setVisible(true);
}
private static class PrintAction extends AbstractAction {
private String str;
public PrintAction(String aPrintStr) {
super("Print: " + aPrintStr);
str = aPrintStr;
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(str);
}
}
}
It is possible to use KeyEventDispatcher to filter key events.
(Credit: I have adapted the code from answer to Application wide keyboard shortcut)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class TestMenuBindings {
public static void main(String[] args) {
JMenuBar menuBar = new JMenuBar();
final JMenu menu = new JMenu("Print");
final Action oAction = new PrintAction("O",KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
menu.add(oAction);
menuBar.add(menu);
JFrame frm = new JFrame("Frame");
frm.setJMenuBar(menuBar);
final JTextArea area = new JTextArea("Here working no accelerators", 10, 40);
frm.add(new JScrollPane(area));
frm.add(new JTextField("Here working accelerators"), BorderLayout.SOUTH);
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
kfm.addKeyEventDispatcher( new KeyEventDispatcher() {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
// pass only KEY_TYPED for letters with no modifiers in the editing area, suppress KEY_PRESSED, KEY_RELEASED
return area.isFocusOwner() && keyStroke.getModifiers()==0 && e.getID()!=KeyEvent.KEY_TYPED && Character.isLetter(e.getKeyChar());
}
});
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.pack();
frm.setVisible(true);
}
private static class PrintAction extends AbstractAction {
private String str;
public PrintAction(String aPrintStr, KeyStroke aMnemonic) {
super("Print: " + aPrintStr);
str = aPrintStr;
putValue(Action.ACCELERATOR_KEY, aMnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(str);
}
}
}

Why doesn't click in JContextMenu invoked in JDialog fire an ActionEvent?

I have a JContextMenu that I open in a JDialog by calling (the MouseListener is registered to the JDialog):
menu.show(e.getComponent(), e.getX(), e.getY());
The menu itself has an ActionListener registered on the menuitems. It opens fine and the actions are performed as expected, until I open another menu (for example the application's main menu or a context menu on another component) that overlaps something else. For example if a menu in the main menu bar overlaps a dialog or if a context menu is opened that reaches outside the window. Then the clicks on the context menu of the JDialog just close the context menu, as if you had clicked beside the menu to close it (the other menus in the application are still fine though).
It's as if the click goes through the menu. If I select a menu item with the arrow keys and press enter, it does the action just fine.
If I specify e.g. the main window of the application as invoker in the menu.show(), then it seems to always work (but of course it opens on the wrong location). What seems to work is registering the mouse listener on the content pane of the JDialog instead.
Can anyone explain what can cause this behaviour? Should a JDialog not be used as invoker of a context menu for some reason?
I also tried using setComponentPopupMenu(), but that only seems to exist for JComponent and not JDialog or it's content pane. Using it on a component inside the JDialog works, but so does the other method of using a mouse listener and menu.show().
Absent a complete example, I'm not sure I can explain your result, but setComponentPopupMenu() works on a JPanel added to a dialog's content pane. The code below is based on this example.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
/**
* #see https://stackoverflow.com/a/22100940/230513
* #see https://stackoverflow.com/questions/5129294
*/
public class Test {
private void display() {
JDialog d = new JDialog();
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
final ImagePanel imagePanel = new ImagePanel();
d.add(new JScrollPane(imagePanel));
d.setJMenuBar(imagePanel.menuBar);
d.pack();
d.setLocationRelativeTo(null);
d.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
private static class ImagePanel extends JPanel {
private static final int MASK
= Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
private JFileChooser chooser = new JFileChooser();
private Action openAction = new ImageOpenAction("Open");
private Action clearAction = new ClearAction("Clear");
private JMenuBar menuBar = new JMenuBar();
private JPopupMenu popup = new JPopupMenu();
private BufferedImage image;
public ImagePanel() {
this.setComponentPopupMenu(popup);
popup.add("Popup Menu");
popup.add(new JMenuItem(openAction));
popup.add(new JMenuItem(clearAction));
JMenu menu = new JMenu("File");
menu.setMnemonic('F');
menu.add(new JMenuItem(openAction));
menu.add(new JMenuItem(clearAction));
menuBar.add(menu);
}
#Override
public Dimension getPreferredSize() {
if (image == null) {
return new Dimension(320, 240);
} else {
return new Dimension(image.getWidth(), image.getHeight());
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
this.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_C);
this.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_C, MASK));
}
#Override
public void actionPerformed(ActionEvent e) {
image = null;
revalidate();
repaint();
}
}
private class ImageOpenAction extends AbstractAction {
public ImageOpenAction(String name) {
super(name);
this.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_O);
this.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_O, MASK));
}
#Override
public void actionPerformed(ActionEvent e) {
int returnVal = chooser.showOpenDialog(chooser);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File f = chooser.getSelectedFile();
try {
image = ImageIO.read(f);
revalidate();
repaint();
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
}
}
}
}
}

How to add MouseListener to JList model

I have simple sample of code. How can I to add Mouse Listener for displaying simple menu(when i clicked on right mouse button) for selected JList item? In addition: how can I to add Mouse Listener for left mouse button?
Like this:
Here is my code:
package test;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.AbstractListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class Test implements ActionListener {
private JList list;
private MyJListModel model;
public Test() {
JFrame frame = new JFrame("Test");
frame.setMinimumSize(new Dimension(400,200));
JMenuBar menubar = new JMenuBar();
JMenu filemenu = new JMenu("File");
JMenuItem addItem = new JMenuItem("Add");
filemenu.add(addItem);
menubar.add(filemenu);
addItem.addActionListener(this);
model = new MyJListModel();
list = new JList(model);
JScrollPane scrollpane = new JScrollPane(list);
frame.add(scrollpane);
frame.setJMenuBar(menubar);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
String pushedItem = event.getActionCommand();
if(pushedItem.equals("Add")) {
model.addElement("Item");
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Test();
}
});
}
}
class MyJListModel extends AbstractListModel {
private ArrayList<String> arraylist = new ArrayList<String>();
public MyJListModel() {
super();
}
#Override
public int getSize() {
return arraylist.size();
}
#Override
public Object getElementAt(int i) {
return arraylist.get(i);
}
public void addElement(String string) {
arraylist.add(string);
this.fireContentsChanged(this, 0, arraylist.size() - 1);
}
}
Heres a great article tutorial i found that helped me to accomplish what your trying to accomplish.
http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html#popup
i also found that link from this stack overflow article
How do I create a right click context menu in Java Swing?
Hope this helps
You add the mouselistener to the table, not the model.
Table.addMouseListener(new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
if(e.isPopupTrigger())
{
onShowPopup(e);
}
}
public void mousePressed(MouseEvent e)
{
if(e.isPopupTrigger())
{
onShowPopup(e);
}
}
});

Categories

Resources