package gnu.jemacs.buffer;
import java.awt.Color;
/** A Writer that writes at a Buffer's point or a Marker. */
public class BufferWriter extends java.io.Writer implements Runnable
{
Marker marker;
Object style;
Object stylePlain;
boolean adjustPoint;
/** We are not handling escape sequences. */
static final int NO_ESCAPES_STATE = 1;
/** We are ready to handle escape sequences. */
static final int NORMAL_STATE = 0;
/** Last character was a graphic in the last column.
* If next char is graphic, first move one column right
* (and line warp) before displaying it. */
static final int LAST_COLUMN_STATE = 1;
/** Seen an Escape character. */
static final int SEEN_ESC_STATE = 2;
/** We're in the middle of an escape sequence.
* What we've seen so far is in the savedOutput buffer. */
static final int SEEN_ESC_LBRAC_STATE = 3;
static final int SEEN_ESC_RBRAC_STATE = 4;
int state = NORMAL_STATE;
static final int ESC = 033;
boolean insertMode = false;
char[] savedOutput;
int savedCount;
public BufferWriter (Marker marker, boolean adjustPoint)
{
this.marker = marker;
EToolkit toolkit = EToolkit.getInstance();
this.stylePlain = toolkit.getFace("output", true);
this.style = stylePlain;
this.adjustPoint = adjustPoint;
// StyleConstants.setItalic(this.style, true);
}
public BufferWriter (Buffer buffer)
{
this(buffer.pointMarker, false);
}
boolean bold = false;
boolean underline = false;
boolean blink = false; // not implemented
boolean inverse;
boolean invisible;
String foregroundName;
String backgroundName;
Color foreground;
Color background;
StringBuffer styleNameBuf;
String styleName;
void resetAttributes()
{
bold = false;
underline = false;
blink = false;
inverse = false;
invisible = false;
foregroundName = null;
backgroundName = null;
foreground = null;
background = null;
}
void updateStyle()
{
if (styleNameBuf == null)
styleNameBuf = new StringBuffer(60);
styleNameBuf.setLength(0);
if (underline) styleNameBuf.append("underlined,");
if (bold) styleNameBuf.append("bold,");
if (foreground != null)
{
styleNameBuf.append("fg=");
styleNameBuf.append(foregroundName != null ? foregroundName
: foreground.toString());
styleNameBuf.append(',');
}
if (background != null)
{
styleNameBuf.append("bg=");
styleNameBuf.append(backgroundName != null ? backgroundName
: background.toString());
styleNameBuf.append(',');
}
int slen = styleNameBuf.length();
if (slen == 0)
{
style = stylePlain;
styleName = "output";
return;
}
styleNameBuf.setLength(slen-1); // Remove final comma.
styleName = styleNameBuf.toString();
EToolkit toolkit = EToolkit.getInstance();
style = toolkit.getFace(styleName, false);
if (style != null)
return;
style = toolkit.getFace(styleName, true);
toolkit.setUnderline(style, underline);
toolkit.setBold(style, bold);
if (foreground != null)
toolkit.setForeground(style, foreground);
if (background != null)
toolkit.setBackground(style, background);
}
private String name;
private Color color;
private void getColor(int index, boolean bright /*ignored, for now*/)
{
switch (index)
{
case 0: color = Color.black; name = "black"; break;
case 1: color = Color.red; name = "red"; break;
case 2: color = Color.green; name = "green"; break;
case 3: color = Color.yellow; name = "yellow"; break;
case 4: color = Color.blue; name = "blue"; break;
case 5: color = Color.magenta; name = "magenta"; break;
case 6: color = Color.cyan; name = "cyan"; break;
case 7: color = Color.white; name = "white"; break;
default: color = null; name = null;
}
}
/**
* Process an SGR command with the given code.
* @param param parameter value from the escape sequence
* @param position following offset in savedOutput array
* @return updated value of position, if we gobble multiple parameters
*/
public int handleSetCharacterRendition(int param, int position)
{
switch (param)
{
case -1: case 0:
resetAttributes();
case 1:
bold = true;
break;
case 4:
underline = true;
break;
case 22:
bold = false;
break;
case 24:
underline = false;
break;
default:
if (param >= 30 && param <= 39)
{
getColor(param - 30, false);
foreground = color;
foregroundName = name;
}
else if (param >= 40 && param <= 49)
{
getColor(param - 40, false);
background = color;
backgroundName = name;
}
else if (param >= 90 && param <= 97)
{
getColor(param - 90, true);
background = color;
backgroundName = name;
}
else if (param >= 100 && param <= 107)
{
getColor(param - 100, true);
background = color;
backgroundName = name;
}
// else ignore
}
return position;
}
public void handleOperatingSystemCommand(char ch)
{
if (ch == '\007') /* BEL */
{
// ignore, for now
state = NORMAL_STATE;
}
else if (savedCount >= savedOutput.length)
{
// Seems rather excessive ... Probably error.
int i;
for (i = 0; i < savedCount && savedOutput[i] != '\n';) i++;
if (i < savedCount || ch == '\n')
state = NORMAL_STATE; // Seen '\n' - OK time to bail.
else
savedCount = 0; // No '\n' yet - drop what we've seen so far.
}
else
savedOutput[savedCount++] = ch;
}
/**
* Process a single command following CSI.
* CSI is "Control Sequence Introducer" - i.e. ESC [.
* @param ch the control command
* @param param parameter value from the escape sequence
* @param position following offset in savedOutput array
* @return updated value of position, if we gobble multiple parameters
*/
public int handleCSICommand(char ch, int param, int position)
{
switch (ch)
{
case 'C': // \E[C - cursor right
moveColumns(param > 0 ? param : 1);
break;
case 'D': // \E[D - cursor left
moveColumns(- (param > 0 ? param : 1));
break;
case 'h': // \E[?h - DEC Private Mode Set
if (param == 4)
insertMode = true;
break;
case 'l': // \E[?l - DEC Private Mode Reset
if (param == 4)
insertMode = false;
break;
case 'm':
return handleSetCharacterRendition(param, position);
}
return position;
}
public void handleEscapeBracket(char ch)
{
if (ch == ';' || (ch >= '0' && ch <= '9'))
{
if (savedCount >= savedOutput.length)
savedCount = 0; // Overflow - drop excess parameters.
savedOutput[savedCount++] = ch;
}
else
{
int value = -1;
for (int i = 0; i < savedCount; i++)
{
ch = savedOutput[i];
if (ch >= '0' && ch <= '9')
value = (value <= 0 ? 0 : 10 * value) + (ch - '0');
else
{
i = handleCSICommand('m', value, i);
value = -1;
}
}
handleCSICommand('m', value, savedCount);
updateStyle();
state = NORMAL_STATE;
}
}
public void unTabifyRestOfLine()
{
// FIXME
}
/** Delete characters - but only in current screen line. */
public void removeChars(int count)
{
int save = marker.getOffset();
moveColumns(count);
marker.removeChar(marker.getOffset() - save);
}
/** Move some number of columns right (or left if count < 0). */
public void moveColumns(int count)
{
marker.moveToColumn(marker.currentColumn() + count, true);
}
/*
public synchronized void put(int x)
{
if (insertMode)
unTabifyRestOfLine();
else if (marker.getOffset() < marker.buffer.maxDot())
removeChars(1);
AbstractDocument document = marker.buffer;
boolean mustAdjustPoint
= adjustPoint && marker.getOffset() == marker.buffer.getDot();
marker.insert((char) x, 1, x == '\n' ? stylePlain : style);
if (mustAdjustPoint)
marker.buffer.setDot(marker.getOffset());
}
*/
public synchronized void put (char[] data, int off, int len)
{
if (len == 0)
return;
if (insertMode)
unTabifyRestOfLine();
else
removeChars(len);
boolean mustAdjustPoint
= adjustPoint && marker.getOffset() == marker.buffer.getDot();
marker.insert(new String(data, off, len), style);
if (mustAdjustPoint)
marker.buffer.setDot(marker.getOffset());
}
char[] buf1 = new char[1];
public synchronized void write(int ch)
{
boolean move = marker.getOffset() == marker.buffer.getDot();
write1(ch);
if (move) marker.buffer.setDot(marker.getOffset());
}
public synchronized void write1(int ch)
{
if (state <= NORMAL_STATE)
{
if (ch >= ' ' || ch == '\n' || state < NORMAL_STATE)
{
buf1[0] = (char) ch;
put(buf1, 0, 1);
}
else if (ch == ESC)
state = SEEN_ESC_STATE;
else if (ch == '\b')
moveColumns(-1);
else if (ch == '\t')
{
int col = marker.currentColumn();
marker.moveToColumn(col + 8 - (col & 7), true);
}
else if (ch == '\r')
{
// FIXME - until we handle '\n' more generally.
// marker.moveToColumn(0, false);
}
else
System.err.println("received ctrl-"+(char)(ch+64));
}
else if (state == SEEN_ESC_STATE)
{
switch (ch)
{
case '[':
state = SEEN_ESC_LBRAC_STATE;
if (savedOutput == null)
savedOutput = new char[100];
savedCount = 0;
break;
case ']':
state = SEEN_ESC_RBRAC_STATE;
if (savedOutput == null)
savedOutput = new char[100];
savedCount = 0;
break;
default:
state = NORMAL_STATE;
break;
}
}
else if (state == SEEN_ESC_LBRAC_STATE)
{
handleEscapeBracket((char) ch);
}
else /* if (state == SEEN_ESC_RBRAC_STATE) */
{
handleOperatingSystemCommand((char) ch);
}
}
public synchronized void write (char[] data, int off, int len)
{
boolean move = marker.getOffset() == marker.buffer.getDot();
while (len > 0)
{
if (state > NORMAL_STATE)
{
write1(data[off++]);
len--;
}
else
{
int i;
if (state == NO_ESCAPES_STATE)
i = len;
else
{
for (i = 0; i < len; i++)
{
char ch = data[off+i];
if (ch < ' ')
break;
}
}
if (i > 0)
{
put(data, off, i);
off += i;
len -= i;
}
if (i < len)
{
write1(data[off++]);
len--;
}
}
}
if (move) marker.buffer.setDot(marker.getOffset());
}
public synchronized void flush()
{
}
public synchronized void close()
{
}
char[] buffer;
int count;
public void run()
{
write(buffer, 0, count);
}
}
|