mirror of
https://github.com/processing/processing4.git
synced 2026-02-03 21:59:20 +01:00
421 lines
11 KiB
Java
421 lines
11 KiB
Java
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.io.*;
|
|
|
|
|
|
// might be nice to have option to save this to a file
|
|
|
|
// debugging this class is tricky.. if it's throwing
|
|
// exceptions, don't take over System.err, and debug
|
|
// while watching just System.out
|
|
// or just write directly to systemOut or systemErr
|
|
|
|
public class PdeEditorConsole extends Component {
|
|
PdeEditor editor;
|
|
|
|
static final byte CR = (byte)'\r';
|
|
static final byte LF = (byte)'\n';
|
|
static final byte TAB = (byte)'\t';
|
|
static final int TAB_SIZE = 2;
|
|
//static byte tabchunk[] = new byte[TAB_SIZE];
|
|
//static {
|
|
//for (int i = 0; i < TAB_SIZE; i++) {
|
|
// tabchunk[i] = ' ';
|
|
//}
|
|
//}
|
|
|
|
int lineCount;
|
|
int maxLineCount;
|
|
String lines[];
|
|
boolean isError[];
|
|
int firstLine;
|
|
int scrollOffset;
|
|
|
|
byte cline[] = new byte[1024];
|
|
byte clength;
|
|
boolean cerror;
|
|
|
|
Color bgColor;
|
|
Color fgColorErr;
|
|
Color fgColorOut;
|
|
Color scrollEnabledColor;
|
|
Color scrollDisabledColor;
|
|
|
|
int scrollLeft, scrollRight;
|
|
int scrollUpTop, scrollUpBottom;
|
|
int scrollDownTop, scrollDownBottom;
|
|
|
|
Font font;
|
|
FontMetrics metrics;
|
|
int ascent;
|
|
int leading;
|
|
|
|
Image offscreen;
|
|
int sizeW, sizeH;
|
|
int imageW, imageH;
|
|
|
|
static final int HINSET = 6;
|
|
static final int VINSET = 6;
|
|
|
|
static PrintStream systemOut;
|
|
static PrintStream systemErr;
|
|
|
|
static PrintStream consoleOut;
|
|
static PrintStream consoleErr;
|
|
|
|
static OutputStream stdoutFile;
|
|
static OutputStream stderrFile;
|
|
|
|
|
|
public PdeEditorConsole(PdeEditor editor) {
|
|
this.editor = editor;
|
|
|
|
lineCount = PdeBase.getInteger("editor.console.lines", 6);
|
|
|
|
if (systemOut == null) {
|
|
systemOut = System.out;
|
|
systemErr = System.err;
|
|
|
|
// not text thing on macos
|
|
boolean tod = ((PdeBase.platform != PdeBase.MACOSX) &&
|
|
(PdeBase.platform != PdeBase.MACOS9));
|
|
|
|
if (PdeBase.getBoolean("editor.console.out.enabled", tod)) {
|
|
String outFileName =
|
|
PdeBase.get("editor.console.out.file", "lib/stdout.txt");
|
|
try {
|
|
stdoutFile = new FileOutputStream(outFileName);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
if (PdeBase.getBoolean("editor.console.err.enabled", tod)) {
|
|
String errFileName =
|
|
PdeBase.get("editor.console.err.file", "lib/stderr.txt");
|
|
try {
|
|
stderrFile = new FileOutputStream(errFileName);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
consoleOut =
|
|
new PrintStream(new PdeEditorConsoleStream(this, false, stdoutFile));
|
|
consoleErr =
|
|
new PrintStream(new PdeEditorConsoleStream(this, true, stderrFile));
|
|
|
|
System.setOut(consoleOut);
|
|
System.setErr(consoleErr);
|
|
}
|
|
|
|
maxLineCount = 1000;
|
|
lines = new String[maxLineCount];
|
|
isError = new boolean[maxLineCount];
|
|
for (int i = 0; i < maxLineCount; i++) {
|
|
lines[i] = "";
|
|
isError[i] = false;
|
|
}
|
|
firstLine = 0;
|
|
|
|
addMouseListener(new MouseAdapter() {
|
|
public void mousePressed(MouseEvent e) {
|
|
int x = e.getX();
|
|
int y = e.getY();
|
|
if (!((x > scrollLeft) && (x < scrollRight)))
|
|
return;
|
|
|
|
if ((y > scrollUpTop) && (y < scrollUpBottom)) {
|
|
scrollOffset -= lineCount;
|
|
update();
|
|
|
|
} else if ((y > scrollDownTop) && (y < scrollDownBottom)) {
|
|
scrollOffset += lineCount;
|
|
if (scrollOffset > 0) scrollOffset = 0;
|
|
update();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
public void update() {
|
|
Graphics g = this.getGraphics();
|
|
if (g != null) paint(g);
|
|
}
|
|
|
|
public void update(Graphics g) {
|
|
paint(g);
|
|
}
|
|
|
|
public void paint(Graphics screen) {
|
|
//systemOut.println("paint()");
|
|
if (bgColor == null) {
|
|
|
|
bgColor = PdeBase.getColor("editor.console.bgcolor",
|
|
new Color(26, 26, 26));
|
|
fgColorOut = PdeBase.getColor("editor.console.fgcolor.output",
|
|
new Color(153, 153, 153));
|
|
fgColorErr = PdeBase.getColor("editor.console.fgcolor.error",
|
|
new Color(204, 51, 0));
|
|
scrollEnabledColor =
|
|
PdeBase.getColor("editor.console.scrollbox.color.enabled",
|
|
new Color(51, 51, 51));
|
|
scrollDisabledColor =
|
|
PdeBase.getColor("editor.console.scrollbox.color.disabled",
|
|
new Color(35, 35, 35));
|
|
screen.setFont(font);
|
|
metrics = screen.getFontMetrics();
|
|
ascent = metrics.getAscent();
|
|
leading = ascent + metrics.getDescent();
|
|
//System.out.println(ascent + " " + leading);
|
|
}
|
|
|
|
Dimension size = getSize();
|
|
if ((size.width != sizeW) || (size.height != sizeH)) {
|
|
// component has been resized
|
|
|
|
if ((size.width > imageW) || (size.height > imageH)) {
|
|
// nix the image and recreate, it's too small
|
|
offscreen = null;
|
|
|
|
} else {
|
|
// who cares, just resize
|
|
sizeW = size.width;
|
|
sizeH = size.height;
|
|
//setButtonBounds();
|
|
}
|
|
}
|
|
//systemErr.println("size h, w = " + sizeW + " " + sizeH);
|
|
|
|
if (offscreen == null) {
|
|
sizeW = size.width;
|
|
sizeH = size.height;
|
|
//setButtonBounds();
|
|
imageW = sizeW;
|
|
imageH = sizeH;
|
|
offscreen = createImage(imageW, imageH);
|
|
}
|
|
|
|
if (offscreen == null) return;
|
|
Graphics g = offscreen.getGraphics();
|
|
/*
|
|
if (font == null) {
|
|
font = PdeBase.getFont("editor.console.font",
|
|
new Font("Monospaced", Font.PLAIN, 11));
|
|
//font = new Font("SansSerif", Font.PLAIN, 10);
|
|
g.setFont(font);
|
|
metrics = g.getFontMetrics();
|
|
ascent = metrics.getAscent();
|
|
}
|
|
*/
|
|
g.setFont(font);
|
|
|
|
g.setColor(bgColor);
|
|
g.fillRect(0, 0, imageW, imageH);
|
|
|
|
for (int i = 0; i < lineCount; i++) {
|
|
//int ii = (firstLine + i) % maxLineCount;
|
|
int ii = (firstLine + i) + scrollOffset;
|
|
while (ii < 0) ii += maxLineCount;
|
|
if (ii >= maxLineCount) ii = ii % maxLineCount;
|
|
|
|
g.setColor(isError[ii] ? fgColorErr : fgColorOut);
|
|
//System.out.println(leading);
|
|
g.drawString(lines[ii], HINSET, VINSET + ascent + i*ascent);
|
|
}
|
|
|
|
final int SCROLL_INSET = 4;
|
|
final int SCROLL_SIZE = 12;
|
|
|
|
scrollRight = sizeW - SCROLL_INSET;
|
|
scrollLeft = scrollRight - SCROLL_SIZE;
|
|
|
|
scrollUpTop = SCROLL_INSET;
|
|
scrollUpBottom = scrollUpTop + SCROLL_SIZE;
|
|
|
|
scrollDownBottom = sizeH - SCROLL_INSET;
|
|
if ((PdeBase.platform == PdeBase.MACOSX) ||
|
|
(PdeBase.platform != PdeBase.MACOS9)) {
|
|
scrollDownBottom -= 20; // because size boxes intrude
|
|
}
|
|
scrollDownTop = scrollDownBottom - SCROLL_SIZE;
|
|
|
|
g.setColor(scrollEnabledColor);
|
|
g.fillRect(scrollLeft, scrollUpTop, SCROLL_SIZE, SCROLL_SIZE);
|
|
g.setColor((scrollOffset != 0) ?
|
|
scrollEnabledColor : scrollDisabledColor);
|
|
g.fillRect(scrollLeft, scrollDownTop, SCROLL_SIZE, SCROLL_SIZE);
|
|
|
|
screen.drawImage(offscreen, 0, 0, null);
|
|
}
|
|
|
|
public void write(byte b[], int offset, int length, boolean err) {
|
|
if ((clength > 0) && (err != cerror)) {
|
|
// advance the line because switching between err/out streams
|
|
message(new String(cline, 0, clength), cerror, true);
|
|
clength = 0;
|
|
}
|
|
int last = offset+length - 1;
|
|
// starting a new line, so set its output type to out or err
|
|
if (clength == 0) cerror = err;
|
|
for (int i = offset; i <= last; i++) {
|
|
if (b[i] == CR) { // mac CR or win CRLF
|
|
if ((i != last) && (b[i+1] == LF)) {
|
|
// if windows CRLF, skip the LF too
|
|
i++;
|
|
}
|
|
message(new String(cline, 0, clength), cerror, true);
|
|
clength = 0;
|
|
|
|
} else if (b[i] == LF) { // unix LF only
|
|
message(new String(cline, 0, clength), cerror, true);
|
|
clength = 0;
|
|
|
|
} else if (b[i] == TAB) {
|
|
if (clength + TAB_SIZE > cline.length) {
|
|
byte temp[] = new byte[clength * 2];
|
|
System.arraycopy(cline, 0, temp, 0, clength);
|
|
cline = temp;
|
|
}
|
|
for (int m = 0; m < TAB_SIZE; m++) {
|
|
cline[clength++] = ' ';
|
|
}
|
|
|
|
} else {
|
|
if (cline.length == clength) {
|
|
byte temp[] = new byte[clength * 2];
|
|
System.arraycopy(cline, 0, temp, 0, clength);
|
|
cline = temp;
|
|
}
|
|
cline[clength++] = b[i];
|
|
}
|
|
}
|
|
if (clength != 0) {
|
|
message(new String(cline, 0, clength), cerror, false);
|
|
}
|
|
}
|
|
|
|
public void message(String what, boolean err, boolean advance) {
|
|
int currentLine = (firstLine + lineCount) % maxLineCount;
|
|
lines[currentLine] = what;
|
|
isError[currentLine] = err;
|
|
|
|
if (advance) {
|
|
firstLine = (firstLine + 1) % maxLineCount;
|
|
//systemOut.println((err ? "ERR: " : "OUT: ") + what);
|
|
systemOut.println(what);
|
|
scrollOffset = 0;
|
|
}
|
|
update();
|
|
}
|
|
|
|
public Dimension getPreferredSize() {
|
|
//systemOut.println("pref'd sizde");
|
|
if (font == null) {
|
|
font = PdeBase.getFont("editor.console.font",
|
|
new Font("Monospaced", Font.PLAIN, 11));
|
|
//font = new Font("SansSerif", Font.PLAIN, 10);
|
|
//g.setFont(font);
|
|
//metrics = g.getFontMetrics();
|
|
metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
|
|
ascent = metrics.getAscent();
|
|
}
|
|
|
|
//if (ascent == 0) {
|
|
// no useful font information yet
|
|
//System.out.println("PdeEditorConsole: setting size w/o metrics");
|
|
//return new Dimension(300, 84);
|
|
//} else {
|
|
//System.out.println("PdeEditorConsole: got metrics, setting size" +
|
|
// new Dimension(300 + HINSET*2,
|
|
// leading*lineCount + VINSET*2));
|
|
return new Dimension(300 + HINSET*2,
|
|
ascent*lineCount + VINSET*2);
|
|
//}
|
|
}
|
|
}
|
|
|
|
|
|
class PdeEditorConsoleStream extends OutputStream {
|
|
PdeEditorConsole parent;
|
|
boolean err; // whether stderr or stdout
|
|
byte single[] = new byte[1];
|
|
OutputStream echo;
|
|
|
|
public PdeEditorConsoleStream(PdeEditorConsole parent,
|
|
boolean err, OutputStream echo) {
|
|
this.parent = parent;
|
|
this.err = err;
|
|
this.echo = echo;
|
|
}
|
|
|
|
public void close() { }
|
|
|
|
public void flush() { }
|
|
|
|
public void write(byte b[]) { // appears never to be used
|
|
parent.write(b, 0, b.length, err);
|
|
if (echo != null) {
|
|
try {
|
|
echo.write(b); //, 0, b.length);
|
|
echo.flush();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
echo = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void write(byte b[], int offset, int length) {
|
|
parent.write(b, offset, length, err);
|
|
if (echo != null) {
|
|
try {
|
|
echo.write(b, offset, length);
|
|
echo.flush();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
echo = null;
|
|
}
|
|
}
|
|
/*
|
|
//System.out.println("leech2:");
|
|
if (length >= 1) {
|
|
int lastchar = b[offset + length - 1];
|
|
if (lastchar == '\r') {
|
|
length--;
|
|
} else if (lastchar == '\n') {
|
|
if (length >= 2) {
|
|
int secondtolastchar = b[offset + length - 2];
|
|
if (secondtolastchar == '\r') {
|
|
length -= 2;
|
|
} else {
|
|
length--;
|
|
}
|
|
} else {
|
|
length--;
|
|
}
|
|
}
|
|
//if ((lastchar = '\r') || (lastchar == '\n')) length--;
|
|
}
|
|
//if (b[offset + length - 1] == '\r'
|
|
parent.message("2: " + length + " " + new String(b, offset, length), err);
|
|
*/
|
|
}
|
|
|
|
public void write(int b) {
|
|
single[0] = (byte)b;
|
|
parent.write(single, 0, 1, err);
|
|
if (echo != null) {
|
|
try {
|
|
echo.write(b);
|
|
echo.flush();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
echo = null;
|
|
}
|
|
}
|
|
//parent.message(String.valueOf((char)b), err);
|
|
}
|
|
}
|