JPopupMenu Menu Not Appearing - java

This is the code for my JPopupMenu and how I added it, it is supposed to respond when I right click the table:
JMenuItem deleteRows = new JMenuItem("Delete Row");
popup.add(deleteRows);
personTable.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if(e.getButton() == MouseEvent.BUTTON3) {
popup.show(personTable, e.getX(), e.getY());
}
}
});
I am not sure why the popup menu is not appearing when I right-click the table in the application. I would appreciate it if somebody told me what I am doing wrong.

The trigger for a popup is different for different OS's, you can't simply use mousePressed and your certainly shouldn't be using e.getButton() == MouseEvent.BUTTON3
From How to use Menus, Bringing up a PopupMenu
The exact gesture that should bring up a popup menu varies by look and feel. In Microsoft Windows, the user by convention brings up a popup menu by releasing the right mouse button while the cursor is over a component that is popup-enabled. In the Java look and feel, the customary trigger is either pressing the right mouse button (for a popup that goes away when the button is released) or clicking it (for a popup that stays up).
Instead, you should be checking for each of the mouse events, pressed, released and clicked. You should also be using MouseEvent#isPopupTrigger to determine if the event is a popup trigger for the OS.
Having said all that, it would be simpler to just us JComponent#setComponentPopupMenu and let it decide instead
personTable.setComponentPopupMenu(popup);
Runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultTableModel model = new DefaultTableModel(10, 10);
JTable table = new JTable(model);
JMenuItem mi = new JMenuItem("I'll be your menu for today");
JPopupMenu popup = new JPopupMenu();
popup.add(mi);
table.setComponentPopupMenu(popup);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Here is the simple way to create a popupMenu without using mouse listener:
JpopupMenu popMenu = new JpopupMenu() ;
JMenuItem item = new JMenuItem("my item") ;
popMenu.add(item);
myTable.setComponentPopupMenu(popMenu);

Related

Action event not firing for JMenuItem accelerator keystroke?

I have a simple java swing application. It's mostly used on Macos, so I'm trying to add a default menu bar to it through Desktop.getDesktop().setDefaultMenuBar(...). I defined a menu bar with a "File" menu and a "New" menu item with an action listener. Using the mouse to click on File->New calls the listener's actionPerformed() event as expected.
I tried to attach the standard accelerator to the menu item (Command-N on a mac). Clicking on the "File" menu now displays "New" with the expected accelerator next to it. However, when I actually type Command-N, the action listener isn't called. The only visible effect of typing Command-N is that the "File" menu item briefly flickers.
Edit: This seems to be related to the fact the menu is being set through Desktop.setDefaultMenuBar(). If I attach the menu to the JFrame, then accelerators work correctly. However, I'm using Desktop.setDefaultMenuBar() to define a menu that appears even when no other windows are open.
import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Scratch {
static class MainMenu extends JMenuBar implements ActionListener {
public MainMenu() {
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic(KeyEvent.VK_F);
int keyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
JMenuItem item = new JMenuItem("New");
item.setMnemonic(KeyEvent.VK_N);
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, keyMask));
item.addActionListener(this);
fileMenu.add(item);
this.add(fileMenu);
}
#Override
public void actionPerformed(ActionEvent e) {
System.err.println("actionPerformed " + e);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Hello Stackoverflow!");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Hello Stackoverflow!");
frame.getContentPane().add(label);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
Desktop.getDesktop().setDefaultMenuBar(new MainMenu());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
The call to Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() is based on this blog entry.
Can anyone see what I'm doing wrong?
I'm testing with Amazon coretto 11 if it matters.

Alt key interrups the GUI

I currently have a very simple game, where you move a black rectangle from side to side using the arrows keys. My problem is that whenever the alt key is being pressed (mostly by accident), the game window loses focus and doesn't reply to the arrow keys anymore. The focus is regained if I press alt again, but if the rectangle is already in motion it will keep going non stop.
I'm using these render and update methods btw.
Is there a way to prevent the alt key from doing its job - focusing on the UI's menu?
Is there a way to prevent the alt key from doing its job - focusing on the UI's menu?
Strictly speaking, no - or more to the point, that's the wrong approach.
What you should be doing is using the Key Bindings API which will overcome the focus related issues of KeyListener, this way, you don't "have" to care
This is a really simple test which binds actions to the ALT and other keys and seems to work for me, but immediately, I'm testing on MacOS. I also found that you can only bind to the "key released" event of the meta keys
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
JMenuBar mb= new JMenuBar();
JMenu menu = new JMenu("Menu");
menu.add("This is test item");
mb.add(menu);
frame.setJMenuBar(mb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel("...");
add(label);
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "A");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "W");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "S");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "D");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, 0, true), "ALT");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0, true), "CTRL");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_META, 0, true), "META");
ActionMap actionMap = getActionMap();
actionMap.put("A", new TextAction("A"));
actionMap.put("W", new TextAction("W"));
actionMap.put("S", new TextAction("S"));
actionMap.put("D", new TextAction("D"));
actionMap.put("ALT", new TextAction("ALT"));
actionMap.put("CTRL", new TextAction("CTRL"));
actionMap.put("META", new TextAction("META"));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected class TextAction extends AbstractAction {
private String text;
public TextAction(String text) {
this.text = text;
}
#Override
public void actionPerformed(ActionEvent e) {
label.setText(text);
}
}
}
}
As an alternative, you could also look at KeyboardFocusManager.addKeyEventDispatcher and/or Toolkit.addAWTEventListener to see if you can determine when the ALT key is pressed and either force focus back to the component OR, better, pause the game - as there might be a legitimate reason the user wants access to the system menu.
To that end, a FocusListener might be a simpler solution all round, as you can simply determine when focus is lost or gained and pause/resume the game appropriately

Unable to get hidden JTable column

I use a JTable with two buttons(Active & Inactive Buttons) . When I click on the Inactive button I hide the first column of the table. Also I try to show the first column when I click on the Active button. But the problem is , when I click on the Active button I couldn't display the first column with its preferred size .
This is the code for hide the first column,
expTable.getColumnModel().getColumn(0).setMinWidth(0);
expTable.getColumnModel().getColumn(0).setMaxWidth(0);
expTable.getColumnModel().getColumn(0).setWidth(0);
This is the code for get back fist column,
expTable.getColumnModel().getColumn(0).setMinWidth(0);
expTable.getColumnModel().getColumn(0).setMaxWidth(300);
expTable.getColumnModel().getColumn(0).setWidth(100);
First I click on the Inactive button and then I click on the Active button . But this is not working .
Have any idea to how to do above .
private void inactiveButtonActionPerformed(java.awt.event.ActionEvent evt){
expTable.getColumnModel().getColumn(0).setMinWidth(0);
expTable.getColumnModel().getColumn(0).setMaxWidth(0);
expTable.getColumnModel().getColumn(0).setWidth(0);
}
private void activeButtonActionPerformed(java.awt.event.ActionEvent evt)
{
expTable.getColumnModel().getColumn(0).setMinWidth(0);
expTable.getColumnModel().getColumn(0).setMaxWidth(300);
expTable.getColumnModel().getColumn(0).setWidth(100);
}
Don't attempt to "hide" a TableColumn by playing with the width. The TableColumn is still part of the table so when the user tabs from column to column, focus will go to the hidden column and disappear from the users view which is very confusing.
Instead you should remove the TableColumn from the TableColumnModel. Then you can add the TableColumn back to the TableColumnModel when needed.
Check out the Table Column Manager which manages this concept for you. If you don't use the full functionality of the class you can use the hideColumn(...) and showColumn(...) to do a simple toggle on the first column.
In the past when I've need to do something like, I've simply removed and re-added the TableColumn
This is a pretty crude example of the concept...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel();
model.addColumn("Column 1");
model.addColumn("Column 2");
model.addColumn("Column 3");
model.addColumn("Column 4");
model.setRowCount(100);
JTable table = new JTable(model);
add(new JScrollPane(table));
JToggleButton toggle = new JToggleButton("Toggle");
toggle.addActionListener(new ActionListener() {
private TableColumn column;
#Override
public void actionPerformed(ActionEvent e) {
TableColumnModel columnModel = table.getColumnModel();
if (toggle.isSelected()) {
column = columnModel.getColumn(0);
table.getColumnModel().removeColumn(column);
} else if (column != null) {
columnModel.addColumn(column);
columnModel.moveColumn(columnModel.getColumnCount() - 1, 0);
}
}
});
add(toggle, BorderLayout.SOUTH);
}
}
}
I actually spent sometime putting together a custom TableModel which provide the ability to hide and show columns, which basically wrapped this functionality up in a nice reusable package

How to use SystemTray correctly in Java?

I tried to implement a small (test) programm using the SystemTray in java. I got problems and wrote a (M)WE and pubushed it with http://pastebin.com/nYBkaLSy (sorry for not doing any safty checks).
What happens (under Ubuntu/KDE with openJDK):
The program starts and the button is clickable. The SystemTray is registered (all right so far)
If I click (single left mouse) on the image in the tray the button is no more clickable. Instead it seems to be disabled. A right-click on the tray icon OR the frame content (!!!) opens the popup. After closing this (clicking anywhere) the button active again. (this bahavior I cannot explain. My code does not implie this as far as I see)
If I click on the minimize button of the window manager the frame disappears (correct)
On choosing show from the popup of the tray icon restores the frame and the frame content is clickable (ok so far).
Unfortunately the popup is disabled after that. I cannot open it again and if I minimize the window again I cannot bring the window up again. (also I do not see the point in the code)
Now I am unsure if I understood the principles wrongly or if there is some other bug around. SO please help me to clear my questions.
EDIT:
I inserted the code (a bit modifie) here.
Please note that the created image is just dummy code. In my implementation I load an external image (see comments) which produces the same result. So the iconToImage() hint in the comments seems not to be a problem.
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
public class TestTray {
public static void main(String[] args) {
// Create tmp image
Image image = new BufferedImage(32, 32, BufferedImage.TYPE_INT_RGB);
image.getGraphics().drawOval(2, 2, 30, 30);
// Alternative: Load PNG image
//URL imUrl = TestTray.class.getResource("clock.png");
//ImageIcon icon = new ImageIcon(imUrl);
//image = icon.getImage();
TrayIcon ti = new TrayIcon(image);
ti.setImageAutoSize(true);
try {
SystemTray.getSystemTray().add(ti);
} catch (AWTException e) {
e.printStackTrace();
}
// Create JFrame and set default things
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JButton("Test-Button"));
frame.pack();
frame.setVisible(true);
// Add listener to hide window in case of minimization
frame.addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent ev) {
if(ev.getNewState() == JFrame.ICONIFIED)
frame.setVisible(false);
}
});
// Create popup for System tray and register it
PopupMenu popup = new PopupMenu();
MenuItem menuItem;
menuItem = new MenuItem("Show");
menuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frame.setVisible(true);
frame.setExtendedState(JFrame.NORMAL);
}
});
popup.add(menuItem);
menuItem = new MenuItem("Exit");
menuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
popup.add(menuItem);
ti.setPopupMenu(popup);
}
}

Java Swing - ActionListener much slower than KeyListener

For a program, I was using a KeyListener to add something to an ArrayList when pressing the button '1'. Objects in this list are being visualised constantly. With the KeyListener, this worked fluently, even when keeping the button pressed.
Later, I added a JMenuBar to the GUI. Adding something to the ArrayList now has an own JMenuItem with its accelerator set to the KeyStroke '1' and an ActionListener which performs the same stuff than the KeyListener before. However, the performance now is very bad. Keeping '1' pressed is going to lag extremely, it's very slow compared to the KeyListener.
Why is it so slow? Am I doing something wrong? Is there a better way?
...
AL al = new AL();
menu.add(createMenuItem("Add", KeyEvent.VK_1, al));
}
private JMenuItem createMenuItem(String text, int key, ActionListener al){
JMenuItem menuItem = new JMenuItem(text);
menuItem.setAccelerator(KeyStroke.getKeyStroke(key, 0));
menuItem.addActionListener(al);
return menuItem;
}
private class AL implements ActionListener{
public void actionPerformed(ActionEvent e){
int keycode = ((JMenuItem)e.getSource()).getAccelerator().getKeyCode();
bla(keycode);
}
}
It looks like the slowdown is how the menu accelerators are handled. It might be L&F or even OS since when I profile it, there is no hotspot in the Java code (WindowsXP) dependent. A workaround could be to add the key binding to the root pane instead of using an menu accelerator.
Press '1' to trigger KeyListener on button (fast)
Press '2' to trigger menu accelerator (slow)
Press '3' to trigger KeyBinding on button (fast)
Press '4' to trigger KeyBinding on root pane (fast)
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
public class TestKeySpeed {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final JTextArea area = new JTextArea(20, 40);
area.setEditable(false);
JButton button = new JButton("Just something that has focus");
button.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_1) {
area.append("1");
}
}
});
AbstractAction action = new AbstractAction("Add") {
{
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke('2'));
}
#Override
public void actionPerformed(ActionEvent e) {
area.append("2");
}
};
button.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke('3'), "add3");
button.getActionMap().put("add3", action);
JMenu menu = new JMenu("File");
menu.add(action);
JMenuBar bar = new JMenuBar();
bar.add(menu);
JFrame frame = new JFrame("Test");
frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
KeyStroke.getKeyStroke('4'), "add4");
frame.getRootPane().getActionMap().put("add4", action);
frame.setJMenuBar(bar);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(area));
frame.getContentPane().add(button, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
button.requestFocusInWindow();
}
});
}
}
Something else is slowing your application. This example remains responsive with over a dozen Key Bindings. One useful approach is to let menu items and other components share the same actions, as shown here and here.
Addendum: Instead of implementing ActionListener, implement Action by extending AbstractAction, which will make it easier to manage the accelerator key.

Categories

Resources