Hey guys I am making a terminal application using Swing and Apache Commons. I was able to redirect System.out
and System.err
to a JTextArea
I recently tried the same thing, here's my code:
class TexfFieldStreamer extends InputStream implements ActionListener {
private JTextField tf;
private String str = null;
private int pos = 0;
public TexfFieldStreamer(JTextField jtf) {
tf = jtf;
}
//gets triggered everytime that "Enter" is pressed on the textfield
@Override
public void actionPerformed(ActionEvent e) {
str = tf.getText() + "\n";
pos = 0;
tf.setText("");
synchronized (this) {
//maybe this should only notify() as multiple threads may
//be waiting for input and they would now race for input
this.notifyAll();
}
}
@Override
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++);
}
}
and use it like this:
JTextField tf = new JTextField();
TextFieldStreamer ts = new TextFieldStreamer(tf);
//maybe this next line should be done in the TextFieldStreamer ctor
//but that would cause a "leak a this from the ctor" warning
tf.addActionListener(ts);
System.setIn(ts);
Has been a while since I coded Java so I may not be up-to-date with the patterns. You should probably also overload int available()
but my example just consists of the bare minimum to make it work with a BufferedReader
s readLine()
function.
Edit: For this to work for a JTextField
you have to use implements KeyListener
instead of implements ActionListener
and then use addKeyListener(...)
on your TextArea. The function that you need instead of actionPerformed(...)
is public void keyPressed(KeyEvent e)
and then you have to test for if (e.getKeyCode() == e.VK_ENTER)
and instead of using the whole text you just use a substring from the last line before the cursor with
//ignores the special case of an empty line
//so a test for \n before the Caret or the Caret still being at 0 is necessary
int endpos = tf.getCaret().getMark();
int startpos = tf.getText().substring(0, endpos-1).lastIndexOf('\n')+1;
for the input string. Because otherwise you would read the whole TextArea every time you press enter.
You need to create your own implementation of an InputStream
that takes its input from whatever Swing component you want… basically have a buffer that you copy text to from your Swing component and that serves as a source for the InputStream
(which of course needs to block if no input is available).