Adding a keylistener to a JMenuBar - java

I have the following code for a JMenuBar. How would I add my key listener CustomKeyListener to the entire JMenuBar?
package UI;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.*;
import java.awt.Point;
import java.awt.font.*;
import UI.*;
public class GuiDMenuBar extends JMenuBar
{
JMenu m_file,m_code;
JMenuItem mi_f_new,mi_f_open;
public GuiDMenuBar()
{
setBorderPainted(true);
makeFileMenu();
makeCodeButton();
}
void makeFileMenu()
{
m_file = new JMenu("File");
m_file.setMnemonic('F');
mi_f_new = new JMenuItem("New");
mi_f_new.setMnemonic('N');
mi_f_open = new JMenuItem("Open");
mi_f_open.setMnemonic('O');
mi_f_new.setAccelerator(KeyStroke.getKeyStroke("control N"));
mi_f_open.setAccelerator(KeyStroke.getKeyStroke("control O"));
m_file.add(mi_f_new);
m_file.add(mi_f_open);
add(m_file);
}
void makeCodeButton()
{
m_code = new JMenu("Code");
m_code.setMnemonic('C');
add(m_code);
}
public void addListeners(ActionListener al)
{
mi_f_new.addActionListener(al);
mi_f_open.addActionListener(al);
}
class CustomKeyListener implements KeyListener
{
#Override
public void keyTyped(KeyEvent e)
{
System.out.println("Type");
}
#Override
public void keyPressed(KeyEvent e)
{
System.out.println("Press");
}
#Override
public void keyReleased(KeyEvent e)
{
System.out.println("Release");
}
}
}
I have tried adding
m_code.setFocusable(true);
m_code.addKeyListener(new CustomKeyListener());
to the void makeCodeButton however that did not pick up when anything was typed for that one JMenu. This is why I want it added to the whole JMenuBar instead.
Edit to show full CustomKeyListener
class CustomKeyListener implements KeyListener
{
Robot Roby = null;
#Override
public void keyTyped(KeyEvent e)
{
char c = e.getKeyChar();
if(c==KeyEvent.VK_ENTER)
{
if(m_code.isSelected())
{
try
{
Roby = new Robot();
}
catch(AWTException awte)
{
awte.printStackTrace();
}
clcikComponent(m_file);
}
}
System.out.println("Type");
}
#Override
public void keyPressed(KeyEvent e)
{
System.out.println("Press");
}
#Override
public void keyReleased(KeyEvent e)
{
System.out.println("Release");
}
public void clcikComponent(Component comp)
{
Point p = comp.getLocationOnScreen();
System.out.println(p);
Roby.mouseMove(p.x,p.y);
Roby.mousePress(MouseEvent.BUTTON1_MASK);
Roby.mouseRelease(MouseEvent.BUTTON1_MASK);
}
}

I still think this is a bad idea. As a user I would find it very confusing to find out that using the Enter key on one menu invokes an Action, while it doesn't do anything on other menus. The suggestion I gave in my comment is a far better solution.
However, I was curious why it didn't work and I found out that focus is actually on the JRootPane when the menu is displayed (and events are only dispatched to the component with focus). So you could add the KeyListener to the root pane.
However, there is an easier approach. You can use the JMenu.addMenuKeyListener(...) method. The interface is the same as a KeyListener except all methods include "menu" in the name.

Related

Neither Key Listener nor Document Listener fires for ComboBox

Found a simple JComboBox sample on java2 and expanded it to include both a DocumentListener and KeyListener
hoping to capture keystrokes done within the JComboBox.
Eventually those keystrokes will be captured to display the data which matches the keys entered.
For example, as the user types APP, all records beginning with A is return then AP is return and then all data beginning with APP. Basically doing a Filter on "APP*".
But for now, I am unable to get either a KeyListener or DocumentListener working.
Actually, it works sporadically and can't narrow down why. It seems to only work for the Enter key but would like for it to capture for all keystrokes.
Here is the code.
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ComboBoxEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.metal.MetalFileChooserUI;
public class JComboBoxFilter extends JPanel implements ItemListener {
public JComboBoxFilter () {
JComboBox jc = new JComboBox();
jc.addItem("A");
jc.addItem("AA");
jc.addItem("AAA");
jc.addItem("C");
jc.addItem("CC");
jc.addItem("CCC");
jc.addItem("B");
jc.addItem("BB");
jc.addItem("BBB");
jc.addItemListener(this);
add(jc);
ComboBoxEditor editor = jc.getEditor();
JTextField textField = (JTextField)editor.getEditorComponent();
textField.addKeyListener(new KeyListener()
{
#Override
public void keyPressed(KeyEvent arg0) {
runThisKeyListener();
}
#Override
public void keyReleased(KeyEvent arg0) {
runThisKeyListener();
}
#Override
public void keyTyped(KeyEvent arg0) {
runThisKeyListener();
}
private void runThisKeyListener()
{
System.out.println("Inside runThisKeyListener() : " + textField.getText());
}
});
DocumentListener textFieldDL = new DocumentListener()
{
#Override
public void insertUpdate(DocumentEvent e)
{
runThis();
}
#Override
public void removeUpdate(DocumentEvent e)
{
runThis();
}
#Override
public void changedUpdate(DocumentEvent e)
{
runThis();
}
private void runThis()
{
System.out.println("Inside runThis() : " + textField.getText());
}
};
textField.getDocument().addDocumentListener(textFieldDL);
}
public void itemStateChanged(ItemEvent ie) {
String s = (String)ie.getItem();
System.out.println(s);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new JComboBoxFilter ());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setVisible(true);
}
}
Is there anything wrong with the code or is there a Java rule or restriction not allowing this type of functionality?

How to get rid of repeating code in function?

I want to get rid of the repeating code from an application. I tried to do it in several ways, but when I did it, the application did not work as I expected. Only putting the same code in a function had an effect.
In a nutshell, in the application I can delete records in 2 ways, either by pressing a button or by pointing to a record and deleting it with the right mouse button. Button deletion works, but I do not know how to make the mouse deletion have the same effect.
Button that deletes a record in a table.
deleteButton.addActionListener(event -> {
String name;
name = Name.getText();
try {
removeSelectedRow(table1);
pst = con.prepareStatement("delete from recipe where recipe_name = ?");
pst.setString(1, name);
pst.executeUpdate();
JOptionPane.showMessageDialog(null, "Record deleted");
Name.setText("");
Time.setText("");
Difficulty.setSelectedItem("");
Name.requestFocus();
} catch (SQLException e) {
e.printStackTrace();
}
});
here is a function that is supposed to delete with the right mouse button, as you can see it works but the code is almost identical to the previous example.
public void setDeleteButton(ActionEvent event) {
JMenuItem menu = (JMenuItem) event.getSource();
if (menu == menuItemRemove) {
removeSelectedRow(table1);
}
String name;
name = Name.getText();
try {
removeSelectedRow(table1);
pst = con.prepareStatement("delete from recipe where recipe_name = ?");
pst.setString(1, name);
pst.executeUpdate();
JOptionPane.showMessageDialog(null, "Record deleted");
Name.setText("");
Time.setText("");
Difficulty.setSelectedItem("");
Name.requestFocus();
} catch (SQLException e) {
e.printStackTrace();
}
}
function that points to a specific record
public void removeSelectedRow(JTable table) {
DefaultTableModel model = (DefaultTableModel) table1.getModel();
if (table.getSelectedRow() != -1) {
model.removeRow(table.getSelectedRow());
}
}
Okay, so this is going to require a slight shift in mindset. To make this truely flexible, you're going to need to support concepts such as "dependency inject" and "delegation".
The reason for this is, you "operation" needs a lot of information, but, we should be driving towards having a low level of cohesion or coupling between our classes. Your "operation", for example, shouldn't care "how" the row is deleted, only that when requested, it should be done.
So, lets start with some basic delegation...
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
Now, obviously, I'm overly simplifying this for more my needs, but here I've provided a "basic" level delegate and more focused delegate. Why? Because what happens if you want to provide a "insert" action? Why should it have "delete" functionality? Instead, we deliberately isolate the functionality we want to expose.
Next, we need to design our action...
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
#Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
Okay, nothing really special, which is kind of the point. It monitors the selection state so we can enable/disable the action and when triggered, we call our delegate to do the actual work. This decouples the action from the implementation, as the action doesn't need to know what type of TableModel is in use or what type of data source it might be using, it just wants to tell the delegate that it should carry out some kind of operation.
Also note, we've set up a keyboard short cut, which can be used by the JMenuItem and mnemonic support (hold down the Alt or Option key)
Okay, but that's really doing a lot for us, or is it...
Let's have a look at what a delete action might look like...
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
#Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
#Override
public void removeSelectedRow() {
DefaultTableModel model = (DefaultTableModel) table.getModel();
int visibleRowIndex = table.getSelectedRow();
if (visibleRowIndex == -1) {
return;
}
int modelIndex = table.convertRowIndexToModel(visibleRowIndex);
// I'm guessing here, but if you're deleting a row, you should
// use the row data
String recordId = (String) model.getValueAt(modelIndex, 0);
try (PreparedStatement pst = getConnection().prepareStatement("delete from recipe where recipe_name = ?")) {
pst.setString(1, recordId);
// You could check the number of rows effected by this change
pst.executeUpdate();
JOptionPane.showMessageDialog(TestPane.this, "Record deleted", "Success", JOptionPane.INFORMATION_MESSAGE);
model.removeRow(modelIndex);
} catch (SQLException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(TestPane.this, "Failed to delete row from database", "Error", JOptionPane.ERROR_MESSAGE);
}
}
#Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
#Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
Now, this is just an example, but the basic idea is, we've provide implementation for both the MutableTableSupportable and TableRowDeletable interfaces (but the DeleteRowAction doesn't care about the "how") and we've implemented the removeSelectedRow functionality to delete the row from the TableModel and database.
Again, DeleteRowAction doesn't care how this is implemented, it's just delegating that responsibility, so you could have multiple DeleteRowActions which work with different TableModels and data sources all at the same time 😱
Delegation 💪
Okay, but how would all that work together? Well, actually, really easily in fact
Runnable example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import java.sql.*;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
public TestPane(JMenuBar menuBar) {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
table = new JTable(model);
add(new JScrollPane(table));
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
#Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
#Override
public void removeSelectedRow() {
JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
}
#Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
#Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
}
}
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
#Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
}
nb: This example removes the database support, as I don't have one and instead displays a message
Okay, lets take a quick look at some of the interesting things here...
Firstly...
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
We inject the menu bar into the panel. This is done so that the panel can configure the menu bar as it needs. We could use a type of factory or another delegate here, but I'll leave that up to you to figure out.
Next...
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
We build the JMenu and add our delete row action and create a JButton, using the same Action ... for five lines of code, we've actually done a lot. We've been able to set up the text displayed by each component, the tooltip text, the accelerator key and mnemonic ... try doing that manually, and then need to change something down the track 🙄 (want to support localisation - need to make changes in one location)
But wait, we can do more!! 😱
If we add...
InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);
to the end of the constructor, we can can provide a key binding to the user, so that when the JTable has keyboard focus and the hit they Delete/Backspace key, it will trigger the action as well!!!
Now we have four ways to trigger the action:
Press the button
Open and trigger the menu items
Use the menus keyboard "accelerator" key binding
Hit the Delete key
Actions 💪
Key bindings runnable example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
public TestPane(JMenuBar menuBar) {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
table = new JTable(model);
add(new JScrollPane(table));
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
#Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
#Override
public void removeSelectedRow() {
JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
}
#Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
#Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);
}
}
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
#Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
}
But that's not all! We could also add a button to a JToolBar, because, why not!?

Java why is the key listener not working?

I do not know why this doesn't work. I have already read many posts, and added setFocusable but it just does not work.
public class Spiel {
public static void main(String[] args) {
Playground pg = new Playground();
pg.setLocation(0,0);
pg.setSize(1000,1000);
pg.setVisible(true);
pg.setFocusable(true);
}
}
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class Playground extends JFrame implements KeyListener {
Playground(){
}
#Override
public void keyTyped(KeyEvent e) {
System.exit(0);
}
#Override
public void keyPressed(KeyEvent e) {
System.exit(0);
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
You only implemented the KeyListener but if you want it to actually work you still need to register it to your frame.
Playground(){
addKeyListener(this); // should do the trick
}
Otherwise your frame wouldn't know that it actually has to listen and call the methods when a key is pressed.

Java easiest keylistener

I know there was milions of questions about that, but I can't understand most of them. I've seen that people make something like that:
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode()== KeyEvent.VK_Q)
//do something
}
but keyPressed must override methos of some class to work or be runned in other thread. I really don't know how to do that. Can someone give me code of the easiest keylistener for java.
It should work even when program is not focused (it's just console program).
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.*;
import javax.swing.JFrame;
public class SquatCounter {
class MyKeyListener extends KeyAdapter{
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode()== KeyEvent.VK_Q)
System.out.println("Key Q pressed!");
}
}
JFrame jf = new JFrame("title");
public SquatCounter() {
jf.addKeyListener(new MyKeyListener());
}
public static void main(String[] args) {
Key1 key = new Key1 ();
SquatCounter test = new SquatCounter();
}
}
When you setup the JFrame, add a KeyListener like this:
JFrame jf = new JFrame("title");
jf.addKeyListener(new MyKeyListener());
jf.setVisible(false);
(The jf.setVisible(false); stops the program window from appearing (only command line)
Then create a new class called MyKeyListener that extends KeyAdapter.
class MyKeyListener extends KeyAdapter{
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode()== KeyEvent.VK_Q)
System.out.println("Key Q pressed!");
}
}
Now let me explain things a bit.
First, when you create a JFrame, it has no default KeyListener attached. Therefore, we have to create a class MyKeyListener to do that.
Secondly, we extended KeyAdapter instead of implementing KeyListener because there are a lot more methods than what you need in there. You only need to override the keypressed() method when you extends KeyAdapter but you have to implement all (I think it's 3) the other methods that you don't need for your purposes.
Lastly, if you want to do other methods like keyreleased(), just add it in to the MyKeylistener class and it will work.
Hope this helps!
EDIT: Per OP's request, it should be like this:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = "";
while (line.equalsIgnoreCase("q") == false) {
line = in.read();
System.out.println("Q is pressed!");
}
in.close();
Just add a KeyListener.
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.*;
import javax.swing.JFrame;
public class SquatCounter {
JFrame jf = new JFrame("title") {{
setVisible(true);
setDefaultCloseOperation(3); // EXIT_ON_CLOSE
}};
public SquatCounter() {
jf.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent event) {
if (e.getKeyCode()== KeyEvent.VK_Q)
//do something
}
#Override
public void keyReleased(KeyEvent event) {
// different stuff
}
#Override
public void keyTyped(KeyEvent event) {
// more stuff
}
});
}
public static void main(String[] args) {
SquatCounter test = new SquatCounter();
}
}
Doing something out of focus has an answer here: Stackoverflow: Listening for input without focus in Java

Detect CTRL+V in Swing App but keep original function

Wrting a chat application, I want the user to be able to send images out of his/her clipboard. For this, I would like to catch any CTRL+Vkeyboard input. Since pasting text should be possible as by default, the original ctrl+v-function (pasting text) must not be overridden.
I see can two approaches, of which none works for me:
1st: Taken from the official Java documentation: KEY LISTENER
editorPane.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
e.getKeyChar()
// when I press ctrl+v, ^ this is falsely a white square character, looks like (U+25A1). Plain v without ctrl does work.
e.getKeyCode()
// ^ this is falsely 0
// (e.getModifiersEx() correctly returns InputEvent.CTRL_DOWN_MASK)
}
2nd: KEY BINDING
InputMap iMap = editorPane.getInputMap(condition);
ActionMap aMap = editorPane.getActionMap();
iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "ctrlV");
aMap.put("ctrlV", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// works, but overrides natural ctrl+v function!
}
});
Any ideas?
Note: I am using a "foreign" keyboard layout (German). But I can't see why this should make any difference - I would pretty much like to have my application work internationally.
Cheers
edit. Alt+SomeKey however is correctly recognized by the KeyListener
edit2. after changing keyboard layout to US, problem persists.
Stick to Keybindings: KeyListener is a low-level API, while Keybindings will provide you consistent, predictable and robust behaviour.
The solution here is quite easy. You can simply combine the actions yourself by adding a CombinedAction class that will execute the "original" action bound to CTRL+V and the "custom" action you want to execute.
See a small example below combining both actions (here my custom action is a Sysout):
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
public class TestEditorPane {
private JEditorPane editorPane;
public static class CombinedAction implements ActionListener {
private final ActionListener action1;
private final ActionListener action2;
public CombinedAction(ActionListener action1, ActionListener action2) {
super();
this.action1 = action1;
this.action2 = action2;
}
#Override
public void actionPerformed(ActionEvent e) {
if (action1 != null) {
action1.actionPerformed(e);
}
if (action2 != null) {
action2.actionPerformed(e);
}
}
}
public TestEditorPane() {
}
private void initUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// JTEXTBOX
editorPane = new JEditorPane();
KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK);
final ActionListener ctrlVAction = editorPane.getActionForKeyStroke(ctrlV);
editorPane.registerKeyboardAction(new CombinedAction(ctrlVAction, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("This is my action on CTRL+V");
}
}), ctrlV, JComponent.WHEN_FOCUSED);
// JSCROLLPANE
JScrollPane scroll1 = new JScrollPane(editorPane);
scroll1.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
frame.add(scroll1);
frame.setSize(400, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
TestEditorPane test = new TestEditorPane();
test.initUI();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}

Categories