> Is there an easy way to set up a streaming swing console? (Scrollbars &
> read-only clipboard access would be nice.)
[quoted text clipped - 9 lines]
>
> Rob
I've done this and it was somewhat tricky to get it right. I've tried to
extract the relevant code in the class below. Hopefully I didn't cut out
anything essential. It should give you a huge leg up.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.DateFormat;
import java.util.Date;
import java.util.Vector;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
public class ConsolePane extends JScrollPane implements Printable
{
ConsoleInputStream consoleInputStream = new ConsoleInputStream();
PrintStream consoleOutputStream =
new PrintStream(new FilteredStream(new ByteArrayOutputStream()));
Vector listeners = new Vector();
PrintStream oldErr;
InputStream oldIn;
PrintStream oldOut;
int startOfBuffer;
MyJTextArea textArea;
public ConsolePane()
{
textArea = new MyJTextArea();
setViewportView(textArea);
textArea.setLineWrap(true);
setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
public void addConsoleListener(ConsoleListener listener)
{
listeners.add(listener);
}
private void appendNewText(String str)
{
if (!EventQueue.isDispatchThread())
System.err.println("appendNewText called from non-dispatch
thread");
textArea.append(str);
int length = textArea.getDocument().getLength();
textArea.setCaretPosition(length);
startOfBuffer = length;
notifyListeners();
}
public void clear()
{
try
{
textArea.getDocument().remove(0, count());
startOfBuffer = 0;
consoleOutputStream = new PrintStream(new FilteredStream(new
ByteArrayOutputStream()));
consoleInputStream = new ConsoleInputStream();
}
catch (BadLocationException e)
{
System.out.println(e);
}
}
/**
* Returns the number of characters in the console buffer.
*/
private int count()
{
return textArea.getDocument().getLength();
}
public String getText()
{
return textArea.getText();
}
public void notifyListeners()
{
for (int i = 0; i < listeners.size(); i++)
{
ConsoleListener listener = (ConsoleListener)
listeners.elementAt(i);
listener.newText(this);
}
}
public void startErrCapture()
{
oldErr = System.err;
System.setErr(consoleOutputStream);
}
public void startInCapture()
{
oldIn = System.in;
System.setIn(consoleInputStream);
}
public void startOutCapture()
{
oldOut = System.out;
System.setOut(consoleOutputStream);
}
public void stopErrCapture()
{
System.setErr(oldErr);
}
public void stopInCapture()
{
System.setIn(oldIn);
}
public void stopOutCapture()
{
System.setOut(oldOut);
}
/**
* ConsoleInputStream is a simple extension of ByteArrayInputStream. It
adds a
* growable buffer.
*/
private class ConsoleInputStream extends ByteArrayInputStream
{
/**
* Creates a new ConsoleInputStream with an initial buffer size of
20
*/
public ConsoleInputStream()
{
super(new byte[20], 0, 0);
}
/**
* Adds a byte to the InputSteam.
*/
public void addByte(byte aByte)
{
// If the buffer is full, create a new one twice the size and
copy the
// old stuff over.
if (count == buf.length)
{
byte newBuf[] = new byte[buf.length * 2];
System.arraycopy(buf, 0, newBuf, 0, buf.length);
buf = newBuf;
}
// Add the byte to the buffer and notify any waiters.
buf[count++] = aByte;
}
public synchronized void newTextReady()
{
notifyAll();
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned.
* <p>
*
* @return the next byte of data, or <code>-1</code> if the end of
the
* stream has been reached.
*/
public synchronized int read()
{
while (pos == count)
{
try
{
wait();
}
catch (InterruptedException e)
{
}
}
return super.read();
}
/**
* Reads up to <code>len</code> bytes of data into an array of bytes
* from this input stream.
* If <code>pos</code> equals <code>count</code>,
* then the method blocks until data is avaialble.
* Otherwise, the number <code>k</code>
* of bytes read is equal to the smaller of
* <code>len</code> and <code>count-pos</code>.
* If <code>k</code> is positive, then bytes
* <code>buf[pos]</code> through <code>buf[pos+k-1]</code>
* are copied into <code>b[off]</code> through
* <code>b[off+k-1]</code> in the manner performed
* by <code>System.arraycopy</code>. The
* value <code>k</code> is added into <code>pos</code>
* and <code>k</code> is returned.
* <p>
*
* @param b the buffer into which the data is read.
* @param off the start offset of the data.
* @param len the maximum number of bytes read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end
of
* the stream has been reached.
*/
public synchronized int read(byte b[], int off, int len)
{
while (pos == count)
{
try
{
wait();
}
catch (InterruptedException e)
{
}
}
return super.read(b, off, len);
}
}
private class FilteredStream extends FilterOutputStream
{
public FilteredStream(OutputStream aStream)
{
super(aStream);
}
private void commonWrite(String str)
{
if (EventQueue.isDispatchThread())
{
appendNewText(str);
}
else
{
SwingUtilities.invokeLater(new TextAreaAppender(str));
if (textArea.isShowing())
{
try
{
synchronized (textArea)
{
textArea.wait(100);
}
}
catch (InterruptedException e)
{
}
}
}
}
public void write(byte b[]) throws IOException
{
String aString = new String(b);
commonWrite(aString);
}
public void write(byte b[], int off, int len) throws IOException
{
String aString = new String(b, off, len);
commonWrite(aString);
}
}
private class TextAreaAppender implements Runnable
{
String str;
public TextAreaAppender(String str)
{
this.str = str;
}
public void run()
{
appendNewText(str);
}
}
}

Signature
Bill Tschumy
Otherwise -- Austin, TX
http://www.otherwise.com
Rob McDonald - 08 Jul 2005 13:30 GMT
Thanks, I'll take a close look at that.
Last night, I came across this...
http://javaalmanac.com/egs/javax.swing.text/ta_Console.html
You would think there would be a straightforward Swing component that would
set all this up for you. Oh well...
Thanks again,
Rob
> I've done this and it was somewhat tricky to get it right. I've tried to
> extract the relevant code in the class below. Hopefully I didn't cut out
[quoted text clipped - 49 lines]
> setViewportView(textArea);
> textArea.setLineWrap(true);
setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
> }
>
[quoted text clipped - 261 lines]
>
> }
Andrew Thompson - 08 Jul 2005 13:32 GMT
> Thanks, I'll take a close look at that.
Can you take a close look at trimming the (now redundant)
350+ lines of the earlier post before *replying* in future?

Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.LensEscapes.com/ Images that escape the mundane
> Is there an easy way to set up a streaming swing console? (Scrollbars &
> read-only clipboard access would be nice.)
Isn't their a component that does that in the Swing demo which comes with
every JDK? You could look at the demo's source.