I am currently designing a chat application in Java. Therefore I created my own JFrame. All contents written to System.out are written into a JTextArea and I want to redirect System.in to use my JTextField. I wrote a simple class what should handle this:
public class InputField extends InputStream implements KeyListener {
private JTextField textField;
private String text = null;
public InputField(JTextField textField){
this.textField = textField;
textField.addKeyListener(this);
}
#Override
public int read() throws IOException {
//System.out.println("CALLED!");
while (text == null)
;
int b;
if (Objects.equals(text, "")) {
b = -1;
text = null;
} else {
b = text.charAt(0);
text = text.substring(1, text.length());
}
// System.out.println("GIVING: " + b);
return b;
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
text = textField.getText();
System.out.println("ENTER: "+ text);
textField.setText("");
}
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyReleased(KeyEvent e) {}
}
My reading mechanism:
StringBuilder cmd = new StringBuilder();
int b;
try {
while ((b = System.in.read()) != -1)
cmd.append(b);
// Do something with cmd
} catch (IOException e){}
The first time I input any text and press enter, it works just fine. After outputting a message the read() function is called, but if I try to enter more text the read() function isn't called anymore. Any suggestions to fix this?
Take a look at this image.
The first time you hit enter, it sets the text to "". Then this block sets b = -1:
if (Objects.equals(text, "")) {
b = -1;
Which is the value that read returns, which makes your main loop finish.
Related
I have an ActionListener with code where I want a string to collect certain data once the ActionListener is finished executing. Below is a code of the important class that executes the code:
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JTextField;
public class Main extends JFrame implements KeyListener {
JTextField UpperCaseEverything;
static int a = 1;
static String CurrentlySelectedText;
static String[] StringCurrentlySelectedText;
public Main () {
super("Original Text");
UpperCaseEverything = new JTextField("Click caps lock to capitalize the text (you can change the text, and as aforementioned, if Caps Lock is off, it will get your previous text before it was capitalized).");
CurrentlySelectedText = UpperCaseEverything.getText();
UpperCaseEverything.addKeyListener(
new KeyListener () {
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_CAPS_LOCK && a%2 == 1) {
String SelectedText = UpperCaseEverything.getText();
String UpperCaseText = SelectedText.toUpperCase();
UpperCaseEverything.setText(UpperCaseText);
a++;
}
else if (e.getKeyCode() != KeyEvent.VK_CAPS_LOCK) {
CurrentlySelectedText = UpperCaseEverything.getText();
}
else if (a%2 == 0) {
UpperCaseEverything.setText(CurrentlySelectedText);
a++;
}
}
public void keyReleased(KeyEvent e) {
}
}
);
add(UpperCaseEverything, BorderLayout.NORTH);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(250, 250);
setVisible(true);
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
The most important information from the code that I am having trouble with includes:
UpperCaseEverything.addKeyListener(
new KeyListener () {
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_CAPS_LOCK && a%2 == 1) {
String SelectedText = UpperCaseEverything.getText();
String UpperCaseText = SelectedText.toUpperCase();
UpperCaseEverything.setText(UpperCaseText);
a++;
}
else if (e.getKeyCode() != KeyEvent.VK_CAPS_LOCK) {
CurrentlySelectedText = UpperCaseEverything.getText();
}
else if (a%2 == 0) {
UpperCaseEverything.setText(CurrentlySelectedText);
a++;
}
}
public void keyReleased(KeyEvent e) {
}
}
);
In:
else if (e.getKeyCode() != KeyEvent.VK_CAPS_LOCK) {
CurrentlySelectedText = UpperCaseEverything.getText();
}
I want the CurrentlySelectedText String to collect all of the text from the JButton UpperCaseEverything after a user is finished with an input. That is because the String variable CurrentlySelectedText can collect the data that I don't want it to.
For example: if a user has typed: "Hi.", and deletes the full stop at the end, the sentence before the full stop was deleted is stored in the CurrentlySelectedText variable. If the user then clicks caps lock once, then the whole text (with the full stop removed) gets capitalized, as it should. However, if the user clicks the caps lock button again, then the "Hi." text is still saved inside of the CurrentlySelectedText variable, and not the "Hi" that is supposed to be saved is not saved in the CurrentlySelectedText variable. Consequently, the JButton UpperCaseEverything prints out "Hi." instead of "Hi". To solve such an issue, the String variable CurrentlySelectedText needs to save the text after the delete button has been clicked, but I do not know how to do that.
In java , i am trying to make simple currency converter, but for that i need a text field which can restrict input to numbers only and more importantly double numbers. I tried using JFormatedTextField but it only format the input after you have done your input and click elsewhere but i need to restrict TextField to consume() each invalid character while doing input.
Possible Attempts:
Using JFormatedTextField:
JFormatedTextField textField = new JFormatedTextField(new DoubleFormat());
textField.setBounds(190, 49, 146, 33);
frame.getContentPane().add(textField);
textField.setColumns(10);
Using KeyTyped Event:
char c = arg0.getKeyChar();
if(!(Character.isDigit(c) || c == KeyEvent.VK_BACK_SPACE || c== KeyEvent.VK_DELETE)){
arg0.consume();
}
Using KeyTyped Event with regex:
if(!((textField.getText().toString+arg0.getKeyChar()).matches("[0-9]*(.[0-9]*)?"))){
arg0.consume();
}
Second and third attempt were close but then second attempt failed on point values and third attempt always read first character on textField no matter what it is, So any suggestions ? i am not very fond of JAVA GUI so kindly be patient.
If you know how many places before and after decimal point you want, you can also use MaskFormatter. For example:
JFormattedTextField field = new JFormattedTextField(getMaskFormatter("######.##"));
(...)
private MaskFormatter getMaskFormatter(String format) {
MaskFormatter mask = null;
try {
mask = new MaskFormatter(format);
mask.setPlaceholderCharacter('0');
}catch (ParseException ex) {
ex.printStackTrace();
}
return mask;
}
However it will chenge a look of JTextField, so it will be always visible 000000.00 in it.
EDIT
Another way, not too elegant, but in my opinion working. Try with DecumentListener, maybe it will suit your needs:
field = new JFormattedTextField();
field.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
Runnable format = new Runnable() {
#Override
public void run() {
String text = field.getText();
if(!text.matches("\\d*(\\.\\d{0,2})?")){
field.setText(text.substring(0,text.length()-1));
}
}
};
SwingUtilities.invokeLater(format);
}
#Override
public void removeUpdate(DocumentEvent e) {
}
#Override
public void changedUpdate(DocumentEvent e) {
}
});
I used regex: \\d*(\\.\\d{0,2})? because two decimal places is enough for currency.
You would need to use a DocumentFilter. Read the section from the Swing tutorial on Implementing a DocumentFilter for an example to get you started.
Your implementation will be more complex because you will need to take the text already in the Document and then insert the new text in the appropriate location in the String and then invoke Double.parseDouble(...) on the String to make sure it is a valid double value.
If the validation succeeds then you continue with the insertion otherwise you can generate beep.
You can add a key listener to the text field and implement the keyReleased() method to determine if they value in the text field is a double after every key stroke by the user.
public class CurrencyJTF extends JFrame {
JButton jButton = new JButton("Unfocus");
final JFormattedTextField textField = new JFormattedTextField(new DecimalFormat());
double lastDouble = 0.0;
public CurrencyJTF() throws HeadlessException {
textField.setColumns(20);
textField.setText(lastDouble + "");
this.setLayout(new FlowLayout());
this.add(textField);
this.add(jButton);
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
handleKeyReleased();
}
});
}
private void handleKeyReleased() {
String text = textField.getText();
if (text.isEmpty()) return;
try {
lastDouble = Double.parseDouble(text);
} catch (NumberFormatException ex) {
textField.setText(lastDouble + ""); // or set to other values you want
}
}
public static void main(String[] args) {
JFrame frame = new CurrencyJTF();
frame.setVisible(true);
frame.pack();
}
}
You can write your own KeyListener something like that:
public class DoubleNumbersKeyListener implements KeyListener {
final HashSet<Character> valid_keys = new HashSet<>();
final ArrayList<Character> sequence = new ArrayList<>();
public DoubleNumbersKeyListener() {
valid_keys.add('.');
valid_keys.add('0');
valid_keys.add('1');
valid_keys.add('2');
valid_keys.add('3');
valid_keys.add('4');
valid_keys.add('5');
valid_keys.add('6');
valid_keys.add('7');
valid_keys.add('8');
valid_keys.add('9');
valid_keys.add((char) KeyEvent.VK_BACK_SPACE);
valid_keys.add((char) KeyEvent.VK_DELETE);
}
#Override
public void keyTyped(KeyEvent event) {
char c = event.getKeyChar();
if (!valid_keys.contains(c)) {
event.consume();
} else {
if (c == KeyEvent.VK_DELETE || c == KeyEvent.VK_BACK_SPACE) {
if (!sequence.isEmpty()) {
char last = sequence.remove(sequence.size() - 1);
if (last == '.') {
valid_keys.add(last);
}
}
} else {
sequence.add(c);
if (c == '.') {
valid_keys.remove(c);
}
}
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}
I have 2 textfields in a JFrame and I want to validate the data in textfield1 when the focus gets lost from textfield1. So I have used FocusListener and used showMessageDialog() in the FocusLost() method and that then sets back the focus back to textfield1. It works fine when I click on any component inside the JFrame window other than textfield1,but when I click anywhere outside the JFrame window, the showMessageDialog() gets called two times and the focus goes to textfield2 whereas the focus should remain on textfield1.
#Override
public void focusGained(FocusEvent e) {}
#Override
public void focusLost(FocusEvent e) {
boolean show = false;
String theRegex = "[0-9]";
Pattern checkRegex = Pattern.compile(theRegex);
Matcher regexMatcher = checkRegex.matcher( MemberID );
while ( !regexMatcher.find() && show==false){
JOptionPane.showMessageDialog(null,"Please enter numbers","Validation Error",JOptionPane.ERROR_MESSAGE);
MemberID_Text.requestFocusInWindow();
MemberID_Text.selectAll();
show = true;
}
}
you can do this to verify if a number is entered, and avoid regex all together
class IntVerifier extends InputVerifier {
#Override public boolean verify(JComponent input) {
String text =((JTextField) input).getText();
int n = 0;
try {
n = Integer.parseInt(text); }
catch (NumberFormatException e) {
return false;
}
return true;
}
}
then use the input verifier on the text field
IntVerifier intv = new IntVerifier();
myTextField = new JTextField();
myTextField.setInputVerifier(intv);
Here is my problem I got my System.in that is redirected to a JTextField. Right now the user can press enter and it will send my text away. But my client will not have access to this JTextfield so i wanted to know if i could recreate the Enter key in my code.
public static JTextField jtfEntree = new JTextField();
public static TexfFieldStreamer ts = new TexfFieldStreamer(jtfEntree);
System.setIn(ts);
jtfEntree.addActionListener(ts);
//************************************************************************
private void commandLaunch(String command)
//************************************************************************
{
jtfEntree.setText(command);
//here is where i want to fire the key Enter
}
//************************************************************************
class TexfFieldStreamer extends InputStream implements ActionListener{
private JTextField tf;
private String str = null;
private int pos = 0;
public TexfFieldStreamer(JTextField jtf) {
tf = jtf;
}
public int read() {
//test if the available input has reached its end
//and the EOS should be returned
if(str != null && pos == str.length()){
str =null;
//this is supposed to return -1 on "end of stream"
//but I'm having a hard time locating the constant
return java.io.StreamTokenizer.TT_EOF;
}
//no input available, block until more is available because that's
//the behavior specified in the Javadocs
while (str == null || pos >= str.length()) {
try {
//according to the docs read() should block until new input is available
synchronized (this) {
this.wait();
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
//read an additional character, return it and increment the index
return str.charAt(pos++);
}
public void actionPerformed(ActionEvent arg0)
{
// TODO Auto-generated method stub
str = tf.getText() + "\n";
pos = 0;
synchronized (this) {
//maybe this should only notify() as multiple threads may
//be waiting for input and they would now race for input
this.notify();
}
}
}
If you need more information ask in comments!
thank you for your help
P.S.: I did try to change the action listener to a document listener but it doesnt always fire the event so it didnt act like i wanted to.
Tried with the Robot but it seems like the textfield isnt getting the focus so the key is just pressed and nothing happens
//************************************************************************
protected static void commandExecute(String Command) //COMMAND EXECUTE
//************************************************************************
{
jtfEntree.setText(Command);
jtfEntree.requestFocus();
Robot robot;
try {
robot = new Robot();
robot.keyPress(KeyEvent.VK_ENTER);
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
jtfEntree.setText("");
}
//************************************************************************
Dont know if this would be of help. But did you try the robot class?
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ENTER);
Would simulate a key press for Enter.
Does this help?
I've added a keylistener to my JTextArea field, but it doesn't behave as I expected.
inputTextArea.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent k) {
//If the return button is hit, only set to a new line if shift is also down.
if(k.getKeyChar() == KeyEvent.VK_ENTER) {
if(k.isShiftDown()) {
inputTextArea.append(" \n");
} else {
//Send The Message...
boolean cleanTextField = false;
try {
sendMessage(inputTextArea.getText());
cleanTextField = true;
msgScrollPane.setAutoscrolls(true);
JScrollBar vbar = msgScrollPane.getVerticalScrollBar();
if ((vbar.getValue() + vbar.getVisibleAmount()) == vbar.getMaximum()) {
msgPane.setCaretPosition(msgDoc.getLength());
}
} catch (Exception ex) {
ex.printStackTrace();
cleanTextField = false;
} finally {
if(cleanTextField) {
inputTextArea.setText("");
}
}
}
}
}
});
I want this:
- If the return button is hit and shift is down: add a new line.
- If the return button is hit and the shift button isn't down: no new line, but submit.
Now it behaves like this:
- If I hit the return button and shift is down: no line added. Nothing happens.
- If I hit the return button and shift isn't down: submitted, but if I start typing again it begins on new line.
Does someone know how to do what I want?
EDIT:
I tried some other code to detect if the shift button is down:
if((k.getModifiersEx() == KeyEvent.SHIFT_DOWN_MASK) ||
(k.getModifiers() == KeyEvent.SHIFT_DOWN_MASK)) {
This doesn't work as well
You may use the InputMap and ActionMap of the JTextArea to map the key strokes to actions:
private static final String TEXT_SUBMIT = "text-submit";
private static final String INSERT_BREAK = "insert-break";
...
private void initialize() {
InputMap input = inputTextArea.getInputMap();
KeyStroke enter = KeyStroke.getKeyStroke("ENTER");
KeyStroke shiftEnter = KeyStroke.getKeyStroke("shift ENTER");
input.put(shiftEnter, INSERT_BREAK); // input.get(enter)) = "insert-break"
input.put(enter, TEXT_SUBMIT);
ActionMap actions = inputTextArea.getActionMap();
actions.put(TEXT_SUBMIT, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
submitText();
}
});
}
...
private void submitText() {
// TODO
}
The original action for ENTER - "insert-break" - is used for shift ENTER.
Try using keyTyped and not keyPressed. I beleive keyPressed gives you an event for the shift and for the enter, whereas keyTyped gives you one combined event with a modifier.
Instead of doing the actions immediately on receiving the event, sequence them for later by posting them using SwingUtilities.invokeLater(). The code should look like:
if(k.isShiftDown()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
inputTextArea.append(" \n");
}
});
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//rest of the else body here
}
});
}
In my opinion, the problems seen here are because application-defined actions and internal actions are not being properly sequenced, leading to repaints happening before the text has been modified.