Working on a project I got into running java applications through a small console-like window. Thanks to the wonderful community in here I managed to solve the problem with outputting the data from a proccess but my command-line applications running will constantly give errors as there is no input stream.
Based on the last helpful reply in that thread I suppose I shall approach similarly the JTextFieldInputStream extends InputStream implementation, but looking in the javadocs and throughout google and the internet for some class that does just that I really found nothing explaining how to do this.
So I am asking for some link, example, tutorial, sample code for it just like in the previous topic.
Give me just a class that extends InputStream and can be extended to read from a JTextField when I press Enter and I will do the rest with implementing this and making it work! Thanks in advance!
What I don't understand if why you need a JTextField that extends InputStream? Basically, what you are looking for is:
Add an ActionListener on the JTextField (ie, when use presses Enter, actionPerformed will be invoked)
You need to grab the text of the JTextField using getText()
You can then "transform" the String text to an InputStream with new ByteArrayInputStream(text.getBytes("UTF-8"));
Here is a small snippet that should get you the basic idea:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class TestTextField {
private void initUI() {
JFrame frame = new JFrame(TestTextField.class.getSimpleName());
frame.setLayout(new FlowLayout());
final JTextField textfield = new JTextField(20);
textfield.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
String text = textfield.getText();
InputStream is = new ByteArrayInputStream(text.getBytes("UTF-8"));
// Here do something with your input stream (something non-blocking)
System.err.println(text);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textfield);
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTextField().initUI();
}
});
}
}
How about this implementation
import java.io.IOException;
import java.io.InputStream;
import javax.swing.JTextField;
public class JTextFieldInputStream extends InputStream {
byte[] contents;
int pointer = 0;
public JTextFieldInputStream(final JTextField text) {
text.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
if(e.getKeyChar()=='\n'){
contents = text.getText().getBytes();
pointer = 0;
text.setText("");
}
super.keyReleased(e);
}
});
}
#Override
public int read() throws IOException {
if(pointer >= contents.length) return -1;
return this.contents[pointer++];
}
}
to use this input stream, do the following
InputStream in = new JTextFieldInputStream( someTextField );
char c;
while( (c = in.read()) != -1){
//do whatever with c
}
does it read only when I hit enter?
it reads when you call in.read() if the return value -1 it means end of the stream
(And will I be able to modify so that the Enter key empties the JTextField?)
you need to add an action listener and this functionality has nothing to do with the job of input stream
Related
I am a beginner in Java, I am trying to build a chatbot. The query and responses are hard coded in 2D string array chatbot[][].
This is the complete code that I have used, Two objects for JTextArea 'input and dialogue ' has been used. Object input has been attached to JPanel and is used to get the text from the user and then this is searched in the 2D string array.If it is found then the response is thrown from the same array.
Now my requirement is when the response is any URL it should be displayed as a link so that user can directly click and go to the site(http://google.com for the current scenario).Kindly suggest how this can be achieved or any modification in code is required. addText is the method that is defined to add entire conversation to dialogue text area which is non editable.
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.lang.Math;
import java.awt.Color;
import java.awt.Toolkit;
public class test implements KeyListener{
JFrame j=new JFrame("Temenos ChatBot");
JPanel p=new JPanel();
JTextArea dialog=new JTextArea(20,50); //it will contain the whole conversation(non editable)
JTextArea input=new JTextArea(1,50); //where we user input our query(editable)
JScrollPane scroll=new JScrollPane(
dialog,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
);
String[][] chatbot={
{"hi","hello","hola","ola","howdy"}, //check for query
{"hi","hello","hey"}, //provides output of the query
{"google","provide link for google","www.google.com"}, //Query
{"http://google.com"}, //response
//default
{"we can't find your query",
"(Sorry, we could not understand)"}
};
public static void main(String[]args){
new test();
}
private void icon(){
j.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("dy1MzZ-W.png")));//change the icon, paste the icon in default package
}
public test(){
j.setSize(600,400);
j.setResizable(false);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
dialog.setEditable(false);
input.addKeyListener(this);
p.add(scroll);
p.add(input);
p.setBackground(new Color(0,150,255));
j.add(p);
j.setVisible(true);
icon();
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_ENTER){
input.setEditable(false);
//-----grab quote-----------
String quote=input.getText(); //takes whatever text user is inputting
input.setText("");
addText("->You:\t"+quote);
quote.trim();
while(
quote.charAt(quote.length()-1)=='!' ||
quote.charAt(quote.length()-1)=='.' ||
quote.charAt(quote.length()-1)=='?'
){
quote=quote.substring(0,quote.length()-1);
}
quote.trim();
byte response=0; //byte is data type that will check for response
/*
0:we're searching through chatBot[][] for matches
1:we didn't find anything
2:we did find something
*/
//-----check for matches----
int j=0;//which group we're checking
while(response==0){
if(inArray(quote.toLowerCase(),chatbot[j*2])){
response=2;
int r=(int)Math.floor(Math.random()*chatbot[(j*2)+1].length);
addText("\n->A.I.sha:\t"+chatbot[(j*2)+1][r]);
}
j++;
if(j*2==chatbot.length-1 && response==0){
response=1;
} //if it has come to end then set response to 1
}
//-----default--------------
if(response==1){
int r=(int)Math.floor(Math.random()*chatbot[chatbot.length-1].length);
addText("\n->A.I.sha:\t"+chatbot[chatbot.length-1][r]);
}
addText("\n");
}
}
public void keyReleased(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_ENTER){
input.setEditable(true);
}
}
public void keyTyped(KeyEvent e){}
public void addText(String str){
dialog.setText(dialog.getText()+str);
}//it will add whatever text dialogue box is having
public boolean inArray(String in,String[] str){
boolean match=false;
for(int i=0;i<str.length;i++){
if(str[i].equals(in)){
match=true;
} //Searches for string 'in' in string array,if it finds return true
}
return match;
}
}
If you're making a GUI in swing, you could follow another stack overflow answer, such as this one
How to add hyperlink in JLabel
Or, you could just have java open the browser to that webpage:
// Create Desktop object
Desktop d=Desktop.getDesktop();
// Browse a URL, say google.com
d.browse(new URI("http://google.com"));
You can use the parse static method from Uri
Uri myUri = Uri.parse("http://google.com")
I'm using a JFrame and I wanted to display an image and pause the code until the user presses ANY key. After that key being pressed the image would close and the code would continue running.
What I did:
Created a flag
final boolean[] flag = {true};
Added a addKeyListener to the JFrame object that would change the flag
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
frame.setVisible(false);
frame.dispose();
flag[0] = false;
}
});
Wait loop until flagged
while (flag[0]){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
This is working, but I understand that it is a bit resourceful.
Is there any other way of making the wait loop? Is there any listener of the listener?
2nd try, using CountDownLatch:
Set the latch
final CountDownLatch latch = new CountDownLatch(1);
CountDown
for (JFrame frame : framesList) {
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
frame.setVisible(false);
frame.dispose();
latch.countDown();
}
});
Wait
latch.await();
So, you want to display an image and have the execution stop until the window is closed. This just screams modal dialog to me. A modal dialog will stop the code execution from where it is made visible, it will do it in such away so as not to block the Event Dispatching Thread and make your entire problem come to a screaming halt and hang the program. See How to use dialogs for more details...
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
BufferedImage img = ImageIO.read(...);
ImageShower.show(null, img);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public static class ImageShower extends JPanel {
private JLabel label = new JLabel();
public ImageShower() {
setLayout(new BorderLayout());
add(label);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
am.put("close", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
Window window = SwingUtilities.windowForComponent(ImageShower.this);
if (window != null) {
window.dispose();
}
}
});
}
public void setImage(Image img) {
label.setIcon(new ImageIcon(img));
}
public static void show(Component owner, Image img) {
Window parent = null;
if (owner != null) {
parent = SwingUtilities.windowForComponent(owner);
}
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
Window window = SwingUtilities.windowForComponent(btn);
if (window != null) {
window.dispose();
}
}
});
JDialog dialog = new JDialog(parent, Dialog.ModalityType.APPLICATION_MODAL);
ImageShower shower = new ImageShower();
shower.setImage(img);
dialog.add(shower);
dialog.add(close, BorderLayout.SOUTH);
dialog.getRootPane().setDefaultButton(close);
dialog.pack();
dialog.setLocationRelativeTo(owner);
dialog.setVisible(true);
}
}
}
"But wait, may images are large and take time to load and I don't want to freeze the UI while the load"...
Okay, for that, I'd look towards using a SwingWorker, which can load the image in the background but which provides simple methods for ensuring the the image is displayed within the context of the EDT properly...
public class ImageLoadAndShow extends SwingWorker<Void, Image> {
#Override
protected Void doInBackground() throws Exception {
BufferedImage img = ImageIO.read(...);
publish(img);
return null;
}
#Override
protected void process(List<Image> chunks) {
Image img = chunks.get(chunks.size() - 1);
ImageShower.show(null, img);
}
}
Not, if the image fails to load, you won't know about it, as the doInBackground method will pass the Exception out of the method. You'd need to use a combination of a PropertyChangeListener and the SwingWorkers get method to trap it, just remember, get is blocking, so calling it inside the context of the EDT will block until the worker completes
"But I need to carry out other operations when the dialog is closed"
There are a few ways you might be able to achieve this, depending on what it is you want to do, for this example, I've stuck with the SwingWorker, because it was easy to copy and paste the basic structure, but you could use a Runnable wrapped in a Thread
public class ImageLoadShowAndWait extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
BufferedImage img = ImageIO.read(...);
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
ImageShower.show(null, img);
}
});
return null;
}
}
Now, if none of that does what you want...then I'd like to know what it is you're actually doing :P, have a look at Foxtrot which provides an API which allows you to execute code asynchronisly within the EDT without blocking it (entirly), but which will stop the code execution at the point it's called until it completes
The thing is that I wanted it to close the JFrame when ANY key is pressed
KeyListener is going to give you issues, maybe not today, maybe not tomorrow, but it will blow up in your face eventually. The example I've provide binds the Escape key to dispose of the window. It also makes the "Close" button the default button, which provides Space and/or Enter keys as well and a nice visual queue to the user.
If you want to use KeyListener, that's up to you, but your core problem doesn't seem to revolve around it, but the ability to display a window and pause the code execution till it's closed
I am stuck with a very unusual situation. I have a class "ScreenSizeSelector" which has a method 'getSelectedScreenSize'. The method's work is to create a UI, user drags the UI and method return back size of window.
Now I am calling the method of class in following ways:
A simple class (non GUI)
On the button click from a JFrame
In the first case, it is working perfectly fine (i.e. size selector window opens, user drags it, resize it and it is giving back window coordinates) but in second case, window opens but in disabled mode, user is not able to perform any operation on the window, not even able to close the window.
Here is the code I am using
ScreenSizeSelector class :
package screenrecorder;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.Border;
class ScreenSizeSelector {
private JFrame sizeSelectorWindow;
private JButton btnOk;
private Border emptyBorder;
private Rectangle screenArea = null;
private static Object lock = new Object();
public Rectangle getSelectedScreenSize(){
screenSizeSelectorUI();
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (sizeSelectorWindow.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return screenArea;
}
public void screenSizeSelectorUI() {
emptyBorder = BorderFactory.createEmptyBorder();
sizeSelectorWindow = new JFrame("Select screen area");
btnOk = new JButton("Start");
sizeSelectorWindow.setUndecorated(true);
sizeSelectorWindow.getRootPane().setWindowDecorationStyle(3);
sizeSelectorWindow.setBackground( new Color(0, 0, 0, 0) );
sizeSelectorWindow.setSize(400,400);
sizeSelectorWindow.addWindowListener(new WindowEventHandler());
sizeSelectorWindow.setAlwaysOnTop(true);
sizeSelectorWindow.setLocationRelativeTo(null);
btnOk.setToolTipText("Click this button after deciding the screen area");
btnOk.addActionListener(new ButtonEventHandler());
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttonPanel.setBackground(new Color(0,0,0,0));
buttonPanel.add(btnOk);
sizeSelectorWindow.add(buttonPanel,BorderLayout.SOUTH);
sizeSelectorWindow.setVisible(true);
sizeSelectorWindow.setEnabled(true);
}
class ButtonEventHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
int x = (int)(sizeSelectorWindow.getBounds().getX());
int y = (int) (sizeSelectorWindow.getBounds().getY());
int width = sizeSelectorWindow.getWidth();
int height = sizeSelectorWindow.getHeight();
screenArea = new Rectangle(x,y,width,height);
sizeSelectorWindow.dispatchEvent(new WindowEvent(sizeSelectorWindow, WindowEvent.WINDOW_CLOSING));
}
}
class WindowEventHandler implements WindowListener{
#Override
public void windowOpened(WindowEvent e) {
}
#Override
public void windowClosing(WindowEvent e) {
synchronized (lock) {
sizeSelectorWindow.setVisible(false);
lock.notify();
}
}
#Override
public void windowClosed(WindowEvent e) {
}
#Override
public void windowIconified(WindowEvent e) {
sizeSelectorWindow.setState(JFrame.NORMAL);
Toolkit.getDefaultToolkit().beep();
}
#Override
public void windowDeiconified(WindowEvent e) {}
#Override
public void windowActivated(WindowEvent e) {}
#Override
public void windowDeactivated(WindowEvent e) {}
}
}
Test1 class :
package screenrecorder;
import java.awt.Rectangle;
public class Test1{
public static void main(String[] args){
System.out.println(new ScreenSizeSelector().getSelectedScreenSize());
}
}
Test2 class :
package screenrecorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Test2 extends JFrame{
public Test2(){
JButton btn = new JButton("Click ME");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(new ScreenSizeSelector().getSelectedScreenSize());
}
});
getContentPane().add(btn);
setSize(100,100);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args){
new Test2();
}
}
Any help is appreciated.
when you click the button, the action listener waits for the getSelectedScreenSize() function to return. and the getSelectedScreenSize() function is waiting for the second window created by screenSelectorUI() to be invisible. screenSelectorUI() does create a second window, but you set the color like this:
sizeSelectorWindow.setBackground( new Color(0, 0, 0, 0) );
if you look at the color constructor javadocs:
public Color(int r,
int g,
int b,
int a)
Creates an sRGB color with the specified red, green, blue, and alpha values in the range (0 - 255).
Parameters:
r - the red component
g - the green component
b - the blue component
a - the alpha component
you set the alpha value to 0, making it completely invisible. (alpha value is transparency) also, this second window is undecorated and does not exit on close, so you don't even know it's there at all.
what I don't get is how test1 worked at all.
side note: when I try test 1 on mac it only shows the button and all I can do is click it. the button will disappear, but the application will still be running.
This is basically a total guess, but a lot of the swing components make requests to the operating system, not commands. sort of like saying, "hey can I please be resized to 400, 400?" the OS doesn't technically have to do what you say. and I was reading How does Java handle multithreading? which says that multithreading really depends on the OS. I have a feeling it just messes up somewhere when screenSelectorUI() is called by itself, but somehow gets it right when it's inside the thread of some button.
I am new to Swing and I have a situation. I am designing an application that renders the GUI components dynamically based on an xml file input(meta-data) . Now most of my JTextFields have InputVerifier set to them, for validation purpose. The input verifier pops up JOptionPane whenever there is an invalid input.
Now, if a user enter an invalid data and moves ahead and clicks a button on the Panel, then a dialog pops up and the user have to respond to it. but after that also the button does not paint to release state. It still looked like it is pressed but actually it is not. As the whole code is pretty messy, I am putting the problem scenario in the code below:-
What should I do so that the JButton looks unpressed? I would appreciate if the logic is also explained.
Thanks in advance.
package test;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
JTextField tf;
tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (b.hasFocus())
System.out.println("Button clicked");
}
});
addWindowListener(new MyWAdapter());
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
//frame.pack();
}
class MyWAdapter extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
class PassVerifier extends InputVerifier {
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
else {
String message = "illegal value: " + tf.getText();
JOptionPane.showMessageDialog(tf.getParent(), message,
"Illegal Value", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
The method verify is actually not a good place to open a JOptionPane.
There are several approaches you could consider to solve your problem:
You want this JOptionPane to appear everytime the textfield looses the focus and the input is incorrect: use a FocusListener on the JTextField and act upon appropriate events
You want this JOptionPane to appear everytime the buttons is pressed: use your ActionListener to do it if the input is incorrect.
Here is a small snippet of the latter option:
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
final JTextField tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!tf.getInputVerifier().verify(tf)) {
JOptionPane.showMessageDialog(tf.getParent(), "illegal value: " + tf.getText(), "Illegal Value",
JOptionPane.ERROR_MESSAGE);
}
if (b.hasFocus()) {
System.out.println("Button clicked");
}
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
}
class PassVerifier extends InputVerifier {
#Override
public boolean verify(JComponent input) {
final JTextField tf = (JTextField) input;
String pass = tf.getText();
return pass.equals("Manish");
}
}
}
Also consider setting the default close operation of the JFrame instead of adding a window listener (but it is a good approach to use a WindowListener if you want to pop up a dialog asking the user if he is sure he wants to exit your application).
I added a call to SwingUtilities to ensure that the GUI is on the event thread, and I removed your reference to Frame.
The GUI works for me on Windows XP.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class VerifierTest implements Runnable {
private static final long serialVersionUID = 1L;
public VerifierTest() {
}
#Override
public void run() {
JFrame frame = new JFrame();
frame.setSize(400, 200);
JTextField tf;
tf = new JTextField("TextField1");
tf.setInputVerifier(new PassVerifier());
frame.getContentPane().add(tf, BorderLayout.NORTH);
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
frame.getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (b.hasFocus())
System.out.println("Button clicked");
}
});
frame.addWindowListener(new MyWAdapter());
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new VerifierTest());
}
class MyWAdapter extends WindowAdapter {
#Override
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
class PassVerifier extends InputVerifier {
#Override
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
else {
String message = "illegal value: " + tf.getText();
JOptionPane.showMessageDialog(tf.getParent(), message,
"Illegal Value", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
I have added a new mouse listener to the button as below and its seems to be working fine for me now, but I am not sure if it is a good way of rectifying the buttons selection state.
package test;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.plaf.basic.BasicButtonListener;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
JTextField tf;
tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (b.hasFocus())
System.out.println("Button clicked");
}
});
b.addMouseListener(new BasicButtonListener(b) {
#Override
public void mouseExited(MouseEvent e) {
((JButton)e.getSource()).getModel().setArmed(false);
((JButton)e.getSource()).getModel().setPressed(false);
}
});
addWindowListener(new MyWAdapter());
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
// frame.pack();
}
class MyWAdapter extends WindowAdapter {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
}
class PassVerifier extends InputVerifier {
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
else {
final String message = "illegal value: " + tf.getText();
JOptionPane.showMessageDialog(null, message,
"Illegal Value", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
First: all implementations of InputVerifier which open the dialog in verify() are invalid. They violated their contract, API doc:
This method should have no side effects.
with the "should" really meaning "must not". The correct place for side-effects is shouldYieldFocus.
Second: moving the side-effect (showing the message dialog) correctly into the shouldYieldFocus doesn't work as well ... due to a bug (THEY call it feature request ;-), that's older than a decade and in the top 10 RFEs
Being a hack-around a bug, #dareurdrem's mouseListener is as good as any workable hack can get :-)
Update
After playing a bit with different options to hack around the bug, here's another hack - it's as brittle as all hacks are (and doesn't survive a LAF toggle, has to be re-installed if dynamic toggling is required)
For hacking the mouse behaviour the basic approach is to hook into the listener installed by the ui:
find the original
implement a custom listener which delegates most events directly to the original
for pressed events request focus first: if yielded delegate to original, if not do nothing
The last bullet is slightly more involved because focus events can be asynchronous, so we have to invoke the check for being focused. Invoking, in turn, requires to send a release in case nobody objected.
Another quirk is the rootPane's pressed action (for its defaultButton): it's done without respecting any inputVerifiers by unconditionally calling doClick. That can be hacked by hooking into the action, following the same pattern as hooking into the mouseListener:
find the rootPane's pressed action
implement a custom action which checks for a potentially vetoing inputVerifier: delegate to the original if not, do nothing otherwise
The example modified along those lines:
public class VerifierTest implements Runnable {
private static final long serialVersionUID = 1L;
#Override
public void run() {
InteractiveTestCase.setLAF("Win");
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
JTextField tf = new JTextField("TextField1");
tf.setInputVerifier(new PassVerifier());
frame.add(tf, BorderLayout.NORTH);
final JButton b = new JButton("Button");
frame.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked");
}
});
// hook into the mouse listener
replaceBasicButtonListener(b);
frame.add(new JTextField("not validating, something else to focus"),
BorderLayout.SOUTH);
frame.getRootPane().setDefaultButton(b);
// hook into the default button action
Action pressDefault = frame.getRootPane().getActionMap().get("press");
frame.getRootPane().getActionMap().put("press", new DefaultButtonAction(pressDefault));
frame.setVisible(true);
}
protected void replaceBasicButtonListener(AbstractButton b) {
final BasicButtonListener original = getButtonListener(b);
if (original == null) return;
Hacker l = new Hacker(original);
b.removeMouseListener(original);
b.addMouseListener(l);
}
public static class Hacker implements MouseListener {
private BasicButtonListener original;
/**
* #param original the listener to delegate to.
*/
public Hacker(BasicButtonListener original) {
this.original = original;
}
/**
* Hook into the mousePressed: first request focus and
* check its success before handling it.
*/
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
if(e.getComponent().contains(e.getX(), e.getY())) {
// check if we can get the focus
e.getComponent().requestFocus();
invokeHandleEvent(e);
return;
}
}
original.mousePressed(e);
}
/**
* Handle the pressed only if we are focusOwner.
*/
protected void handlePressed(final MouseEvent e) {
if (!e.getComponent().hasFocus()) {
// something vetoed the focus transfer
// do nothing
return;
} else {
original.mousePressed(e);
// need a fake released now: the one from the
// original cycle might never has reached us
MouseEvent released = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_RELEASED,
e.getWhen(), e.getModifiers(),
e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()
);
original.mouseReleased(released);
}
}
/**
* focus requests might be handled
* asynchronously. So wrap the check
* wrap the block into an invokeLater.
*/
protected void invokeHandleEvent(final MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
handlePressed(e);
}
});
}
#Override
public void mouseClicked(MouseEvent e) {
original.mouseClicked(e);
}
#Override
public void mouseReleased(MouseEvent e) {
original.mouseReleased(e);
}
#Override
public void mouseEntered(MouseEvent e) {
original.mouseEntered(e);
}
#Override
public void mouseExited(MouseEvent e) {
original.mouseExited(e);
}
}
public static class DefaultButtonAction extends AbstractAction {
private Action original;
/**
* #param original
*/
public DefaultButtonAction(Action original) {
this.original = original;
}
#Override
public void actionPerformed(ActionEvent e) {
JRootPane root = (JRootPane) e.getSource();
JButton owner = root.getDefaultButton();
if (owner != null && owner.getVerifyInputWhenFocusTarget()) {
Component c = KeyboardFocusManager
.getCurrentKeyboardFocusManager()
.getFocusOwner();
if (c instanceof JComponent && ((JComponent) c).getInputVerifier() != null) {
if (!((JComponent) c).getInputVerifier().shouldYieldFocus((JComponent) c)) return;
}
}
original.actionPerformed(e);
}
}
/**
* Returns the ButtonListener for the passed in Button, or null if one
* could not be found.
*/
private BasicButtonListener getButtonListener(AbstractButton b) {
MouseMotionListener[] listeners = b.getMouseMotionListeners();
if (listeners != null) {
for (MouseMotionListener listener : listeners) {
if (listener instanceof BasicButtonListener) {
return (BasicButtonListener) listener;
}
}
}
return null;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new VerifierTest());
}
public static class PassVerifier extends InputVerifier {
/**
* Decide whether or not the input is valid without
* side-effects.
*/
#Override
public boolean verify(JComponent input) {
final JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish"))
return true;
return false;
}
/**
* Implemented to ask the user what to do if the input isn't valid.
* Note: not necessarily the best usability, it's mainly to
* demonstrate the different effects on not/agreeing with
* yielding focus transfer.
*/
#Override
public boolean shouldYieldFocus(final JComponent input) {
boolean valid = super.shouldYieldFocus(input);
if (!valid) {
String message = "illegal value: " + ((JTextField) input).getText();
int goAnyWay = JOptionPane.showConfirmDialog(input, "invalid value: " +
message + " - go ahead anyway?");
valid = goAnyWay == JOptionPane.OK_OPTION;
}
return valid;
}
}
}
Actually the real problem is in how the focus system and awt listeners interact. There are a few bugs declared in Java that the developers are going back and forth on who is responsible.
The mouse listener does : processMouseEvent and within that logic, the current FocusOwner is asked to yield Focus. it fails. But because half the event is processed already, the button becomes armed and the focus remains with the field.
I finally saw one developer comment: Don't let the listener proceed if the field is not allowed to lose focus.
For example:
Define a JTextfield with edits to only allow values < 100.
A message pops up when you lose focus.
I overrode my base JButton classes' processMouseEvent(MouseEvent e)
with code:
protected void processMouseEvent(MouseEvent e) {
if ( e.getComponent() != null && e.getComponent().isEnabled() ) { //should not be processing mouse events if it's disabled.
if (e.getID() == MouseEvent.MOUSE_RELEASED && e.getClickCount() == 1) {
// The mouse button is being released as per normal, and it's the first click. Process it as per normal.
super.processMouseEvent(e);
// If the release occured within the bounds of this component, we want to simulate a click as well
if (this.contains(e.getX(), e.getY())) {
super.processMouseEvent(new MouseEvent(e.getComponent(),
MouseEvent.MOUSE_CLICKED,
e.getWhen(),
e.getModifiers(),
e.getX(),
e.getY(),
e.getClickCount(),
e.isPopupTrigger(),
e.getButton()));
}
}
else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 1) {
// Normal clicks are ignored to prevent duplicate events from normal, non-moved events
}
else if (e.getID() == MouseEvent.MOUSE_PRESSED && e.getComponent() != null && (e.getComponent().isFocusOwner() || e.getComponent().requestFocusInWindow())) {// if already focus owner process mouse event
super.processMouseEvent(e);
}
else {
// Otherwise, just process as per normal.
if (e.getID() != MouseEvent.MOUSE_PRESSED) {
super.processMouseEvent(e);
}
}
}
}
in the guts of this logic is the simple questions.
Button: Are you already focus owner.
if not: can you(Button) possibly GAIN focus ( remember - shouldYieldFocus() is called on the current focus holder inside the requestFocusInWindow() call and will return false ALWAYS if not valid )
This Also has the side affect of popping up your error dialog!
This logic Stops the Java libraries processMouseEvent logic from processing half an event while the Focus System stops it from completing.
Obviously you'll need this type of logic on all your different JComponents that perform an action on a click.
I want to write a little code which react to a mouse click. But it seems the only way is to listen to clicks on Java components. A direct listener to all clicks would be great.
Is there actually a possibility to implement this in Java?
Thanks in advance!
Update:
Found out, that it would need a hook via JNI with some C coding.
More information on
http://www.jotschi.de/?p=90
Best regards,
fnst
It's perfectly possible if you are willing to use a third party library - JNativeHook
It provides these functionality using JNI which is otherwise not possible in pure java apps.
Don´t think so, because of the sandbox you´re running in.
some food for thought: use Point in Java to detect where the click occurs. http://download.oracle.com/javase/1.4.2/docs/api/java/awt/Point.html
admittedly, ive only used it once before for detecting rows in a table something like, but it seems the closest to what ive found answering your qn.:
public void mouseClicked(MouseEvent e) {
JTable target = (JTable)e.getSource();
//get the coordinates of the mouse click
Point p = e.getPoint();
//get the row index that contains that coordinate
row= target.rowAtPoint(p);
}
im sorry if this isn't what you're looking for, but otherwise, clicking on components is the only way to go. whats the alternative- clicking on containers? well that just doesn't make any sense does it?
As far as I know, there is not easy way to accomplish what you want. But what you want could be done.. Well for modern way to accomplish this task, I suggest you examine java.awt.Dialog show method..
package mouseclickevent;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.MenuComponent;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.lang.reflect.Method;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class FrmEvent extends JFrame {
public FrmEvent(){
JPanel panel = new JPanel();
getContentPane().add(panel);
JButton btn = new JButton("Test");
panel.add(btn);
panel.add(new JTextField("Test"));
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
FrmEvent frm = new FrmEvent();
frm.setBounds(300,300, 200, 200);
frm.show();
}
});
}
private void consumeEvents() {
try {
if (Thread.currentThread().getClass().getName().endsWith(
"EventDispatchThread")) {
EventQueue eq = null;
eq = Toolkit.getDefaultToolkit().getSystemEventQueue();
if (eq == null) {
return;
}
while (isVisible() || isShowing()) {
AWTEvent event = eq.getNextEvent();
Object src = event.getSource();
Class kActiveEvent= Class.forName("java.awt.ActiveEvent");
if (kActiveEvent != null) {
if (kActiveEvent.isInstance(event)) {
Method m;
Class types[] = {};
Object args[] = {};
m = kActiveEvent.getMethod("dispatch", types);
if (m != null) {
m.invoke(event, args);
continue;
}
}
}
dispatchEvent(src, event);
}
}
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private void dispatchEvent(Object src, AWTEvent event) {
if (event.getID()== MouseEvent.MOUSE_CLICKED) {
System.out.println("mouseClicked");
}
if (src instanceof Component) {
( (Component) src).dispatchEvent(event);
} else if (src instanceof MenuComponent) {
( (MenuComponent) src).dispatchEvent(event);
}
}
public void show(){
super.show();
consumeEvents();
}
public static void main(String[] args) {
FrmEvent frm = new FrmEvent();
frm.setBounds(300,300, 200, 200);
frm.show();
}
}