001: package gnu.jemacs.buffer;
002:
003: import java.awt.Color;
004:
005: /** A Writer that writes at a Buffer's point or a Marker. */
006:
007: public class BufferWriter extends java.io.Writer implements Runnable {
008: Marker marker;
009: Object style;
010: Object stylePlain;
011: boolean adjustPoint;
012:
013: /** We are not handling escape sequences. */
014: static final int NO_ESCAPES_STATE = 1;
015: /** We are ready to handle escape sequences. */
016: static final int NORMAL_STATE = 0;
017: /** Last character was a graphic in the last column.
018: * If next char is graphic, first move one column right
019: * (and line warp) before displaying it. */
020: static final int LAST_COLUMN_STATE = 1;
021: /** Seen an Escape character. */
022: static final int SEEN_ESC_STATE = 2;
023: /** We're in the middle of an escape sequence.
024: * What we've seen so far is in the savedOutput buffer. */
025: static final int SEEN_ESC_LBRAC_STATE = 3;
026: static final int SEEN_ESC_RBRAC_STATE = 4;
027: int state = NORMAL_STATE;
028:
029: static final int ESC = 033;
030:
031: boolean insertMode = false;
032:
033: char[] savedOutput;
034: int savedCount;
035:
036: public BufferWriter(Marker marker, boolean adjustPoint) {
037: this .marker = marker;
038: EToolkit toolkit = EToolkit.getInstance();
039: this .stylePlain = toolkit.getFace("output", true);
040: this .style = stylePlain;
041: this .adjustPoint = adjustPoint;
042: // StyleConstants.setItalic(this.style, true);
043: }
044:
045: public BufferWriter(Buffer buffer) {
046: this (buffer.pointMarker, false);
047: }
048:
049: boolean bold = false;
050: boolean underline = false;
051: boolean blink = false; // not implemented
052: boolean inverse;
053: boolean invisible;
054: String foregroundName;
055: String backgroundName;
056: Color foreground;
057: Color background;
058: StringBuffer styleNameBuf;
059: String styleName;
060:
061: void resetAttributes() {
062: bold = false;
063: underline = false;
064: blink = false;
065: inverse = false;
066: invisible = false;
067: foregroundName = null;
068: backgroundName = null;
069: foreground = null;
070: background = null;
071: }
072:
073: void updateStyle() {
074: if (styleNameBuf == null)
075: styleNameBuf = new StringBuffer(60);
076: styleNameBuf.setLength(0);
077: if (underline)
078: styleNameBuf.append("underlined,");
079: if (bold)
080: styleNameBuf.append("bold,");
081: if (foreground != null) {
082: styleNameBuf.append("fg=");
083: styleNameBuf.append(foregroundName != null ? foregroundName
084: : foreground.toString());
085: styleNameBuf.append(',');
086: }
087: if (background != null) {
088: styleNameBuf.append("bg=");
089: styleNameBuf.append(backgroundName != null ? backgroundName
090: : background.toString());
091: styleNameBuf.append(',');
092: }
093: int slen = styleNameBuf.length();
094: if (slen == 0) {
095: style = stylePlain;
096: styleName = "output";
097: return;
098: }
099: styleNameBuf.setLength(slen - 1); // Remove final comma.
100: styleName = styleNameBuf.toString();
101: EToolkit toolkit = EToolkit.getInstance();
102: style = toolkit.getFace(styleName, false);
103: if (style != null)
104: return;
105: style = toolkit.getFace(styleName, true);
106: toolkit.setUnderline(style, underline);
107: toolkit.setBold(style, bold);
108: if (foreground != null)
109: toolkit.setForeground(style, foreground);
110: if (background != null)
111: toolkit.setBackground(style, background);
112: }
113:
114: private String name;
115: private Color color;
116:
117: private void getColor(int index, boolean bright /*ignored, for now*/) {
118: switch (index) {
119: case 0:
120: color = Color.black;
121: name = "black";
122: break;
123: case 1:
124: color = Color.red;
125: name = "red";
126: break;
127: case 2:
128: color = Color.green;
129: name = "green";
130: break;
131: case 3:
132: color = Color.yellow;
133: name = "yellow";
134: break;
135: case 4:
136: color = Color.blue;
137: name = "blue";
138: break;
139: case 5:
140: color = Color.magenta;
141: name = "magenta";
142: break;
143: case 6:
144: color = Color.cyan;
145: name = "cyan";
146: break;
147: case 7:
148: color = Color.white;
149: name = "white";
150: break;
151: default:
152: color = null;
153: name = null;
154: }
155: }
156:
157: /**
158: * Process an SGR command with the given code.
159: * @param param parameter value from the escape sequence
160: * @param position following offset in savedOutput array
161: * @return updated value of position, if we gobble multiple parameters
162: */
163: public int handleSetCharacterRendition(int param, int position) {
164: switch (param) {
165: case -1:
166: case 0:
167: resetAttributes();
168: case 1:
169: bold = true;
170: break;
171: case 4:
172: underline = true;
173: break;
174: case 22:
175: bold = false;
176: break;
177: case 24:
178: underline = false;
179: break;
180: default:
181: if (param >= 30 && param <= 39) {
182: getColor(param - 30, false);
183: foreground = color;
184: foregroundName = name;
185: } else if (param >= 40 && param <= 49) {
186: getColor(param - 40, false);
187: background = color;
188: backgroundName = name;
189: } else if (param >= 90 && param <= 97) {
190: getColor(param - 90, true);
191: background = color;
192: backgroundName = name;
193: } else if (param >= 100 && param <= 107) {
194: getColor(param - 100, true);
195: background = color;
196: backgroundName = name;
197: }
198: // else ignore
199: }
200: return position;
201: }
202:
203: public void handleOperatingSystemCommand(char ch) {
204: if (ch == '\007') /* BEL */
205: {
206: // ignore, for now
207: state = NORMAL_STATE;
208: } else if (savedCount >= savedOutput.length) {
209: // Seems rather excessive ... Probably error.
210: int i;
211: for (i = 0; i < savedCount && savedOutput[i] != '\n';)
212: i++;
213: if (i < savedCount || ch == '\n')
214: state = NORMAL_STATE; // Seen '\n' - OK time to bail.
215: else
216: savedCount = 0; // No '\n' yet - drop what we've seen so far.
217: } else
218: savedOutput[savedCount++] = ch;
219: }
220:
221: /**
222: * Process a single command following CSI.
223: * CSI is "Control Sequence Introducer" - i.e. ESC [.
224: * @param ch the control command
225: * @param param parameter value from the escape sequence
226: * @param position following offset in savedOutput array
227: * @return updated value of position, if we gobble multiple parameters
228: */
229: public int handleCSICommand(char ch, int param, int position) {
230: switch (ch) {
231: case 'C': // \E[C - cursor right
232: moveColumns(param > 0 ? param : 1);
233: break;
234: case 'D': // \E[D - cursor left
235: moveColumns(-(param > 0 ? param : 1));
236: break;
237: case 'h': // \E[?h - DEC Private Mode Set
238: if (param == 4)
239: insertMode = true;
240: break;
241: case 'l': // \E[?l - DEC Private Mode Reset
242: if (param == 4)
243: insertMode = false;
244: break;
245: case 'm':
246: return handleSetCharacterRendition(param, position);
247: }
248: return position;
249: }
250:
251: public void handleEscapeBracket(char ch) {
252: if (ch == ';' || (ch >= '0' && ch <= '9')) {
253: if (savedCount >= savedOutput.length)
254: savedCount = 0; // Overflow - drop excess parameters.
255: savedOutput[savedCount++] = ch;
256: } else {
257: int value = -1;
258: for (int i = 0; i < savedCount; i++) {
259: ch = savedOutput[i];
260: if (ch >= '0' && ch <= '9')
261: value = (value <= 0 ? 0 : 10 * value) + (ch - '0');
262: else {
263: i = handleCSICommand('m', value, i);
264: value = -1;
265: }
266: }
267: handleCSICommand('m', value, savedCount);
268: updateStyle();
269: state = NORMAL_STATE;
270: }
271: }
272:
273: public void unTabifyRestOfLine() {
274: // FIXME
275: }
276:
277: /** Delete characters - but only in current screen line. */
278: public void removeChars(int count) {
279: int save = marker.getOffset();
280: moveColumns(count);
281: marker.removeChar(marker.getOffset() - save);
282: }
283:
284: /** Move some number of columns right (or left if count < 0). */
285: public void moveColumns(int count) {
286: marker.moveToColumn(marker.currentColumn() + count, true);
287: }
288:
289: /*
290: public synchronized void put(int x)
291: {
292: if (insertMode)
293: unTabifyRestOfLine();
294: else if (marker.getOffset() < marker.buffer.maxDot())
295: removeChars(1);
296: AbstractDocument document = marker.buffer;
297: boolean mustAdjustPoint
298: = adjustPoint && marker.getOffset() == marker.buffer.getDot();
299: marker.insert((char) x, 1, x == '\n' ? stylePlain : style);
300: if (mustAdjustPoint)
301: marker.buffer.setDot(marker.getOffset());
302: }
303: */
304:
305: public synchronized void put(char[] data, int off, int len) {
306: if (len == 0)
307: return;
308: if (insertMode)
309: unTabifyRestOfLine();
310: else
311: removeChars(len);
312: boolean mustAdjustPoint = adjustPoint
313: && marker.getOffset() == marker.buffer.getDot();
314: marker.insert(new String(data, off, len), style);
315: if (mustAdjustPoint)
316: marker.buffer.setDot(marker.getOffset());
317: }
318:
319: char[] buf1 = new char[1];
320:
321: public synchronized void write(int ch) {
322: boolean move = marker.getOffset() == marker.buffer.getDot();
323: write1(ch);
324: if (move)
325: marker.buffer.setDot(marker.getOffset());
326: }
327:
328: public synchronized void write1(int ch) {
329: if (state <= NORMAL_STATE) {
330: if (ch >= ' ' || ch == '\n' || state < NORMAL_STATE) {
331: buf1[0] = (char) ch;
332: put(buf1, 0, 1);
333: } else if (ch == ESC)
334: state = SEEN_ESC_STATE;
335: else if (ch == '\b')
336: moveColumns(-1);
337: else if (ch == '\t') {
338: int col = marker.currentColumn();
339: marker.moveToColumn(col + 8 - (col & 7), true);
340: } else if (ch == '\r') {
341: // FIXME - until we handle '\n' more generally.
342: // marker.moveToColumn(0, false);
343: } else
344: System.err.println("received ctrl-" + (char) (ch + 64));
345: } else if (state == SEEN_ESC_STATE) {
346: switch (ch) {
347: case '[':
348: state = SEEN_ESC_LBRAC_STATE;
349: if (savedOutput == null)
350: savedOutput = new char[100];
351: savedCount = 0;
352: break;
353: case ']':
354: state = SEEN_ESC_RBRAC_STATE;
355: if (savedOutput == null)
356: savedOutput = new char[100];
357: savedCount = 0;
358: break;
359: default:
360: state = NORMAL_STATE;
361: break;
362: }
363: } else if (state == SEEN_ESC_LBRAC_STATE) {
364: handleEscapeBracket((char) ch);
365: } else /* if (state == SEEN_ESC_RBRAC_STATE) */
366: {
367: handleOperatingSystemCommand((char) ch);
368: }
369: }
370:
371: public synchronized void write(char[] data, int off, int len) {
372: boolean move = marker.getOffset() == marker.buffer.getDot();
373: while (len > 0) {
374: if (state > NORMAL_STATE) {
375: write1(data[off++]);
376: len--;
377: } else {
378: int i;
379: if (state == NO_ESCAPES_STATE)
380: i = len;
381: else {
382: for (i = 0; i < len; i++) {
383: char ch = data[off + i];
384: if (ch < ' ')
385: break;
386: }
387: }
388: if (i > 0) {
389: put(data, off, i);
390: off += i;
391: len -= i;
392: }
393: if (i < len) {
394: write1(data[off++]);
395: len--;
396: }
397: }
398: }
399: if (move)
400: marker.buffer.setDot(marker.getOffset());
401: }
402:
403: public synchronized void flush() {
404: }
405:
406: public synchronized void close() {
407: }
408:
409: char[] buffer;
410: int count;
411:
412: public void run() {
413: write(buffer, 0, count);
414: }
415: }
|