001: // Copyright (c) 2002 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.jemacs.swing;
005:
006: import java.io.*;
007: import java.lang.reflect.InvocationTargetException;
008:
009: import gnu.jemacs.buffer.*;
010: import gnu.mapping.*;
011: import gnu.lists.CharSeq;
012: import javax.swing.text.*;
013: import java.awt.Color;
014:
015: /** An Emacs buffer implemented using the Swing toolkits. */
016:
017: public class SwingBuffer extends Buffer {
018: public DefaultStyledDocument doc;
019:
020: public Caret curPosition = null;
021:
022: public BufferContent content;
023: public StyledDocument modelineDocument;
024:
025: public static javax.swing.text.StyleContext styles = new javax.swing.text.StyleContext();
026: static public Style defaultStyle = styles.addStyle("default", null);
027: public Style inputStyle = defaultStyle;
028: public static Style redStyle = styles.addStyle("red", null);
029: static Style blueStyle = styles.addStyle("blue", null);
030: static {
031: String version = System.getProperty("java.version");
032: if (version != null
033: && (version.startsWith("1.2") || version
034: .startsWith("1.3"))) {
035: StyleConstants.setFontFamily(defaultStyle,
036: "Lucida Sans TypeWriter");
037: StyleConstants.setFontSize(defaultStyle, 14);
038: }
039: StyleConstants.setForeground(redStyle, Color.red);
040: StyleConstants.setForeground(blueStyle, Color.blue);
041: }
042:
043: public SwingBuffer(String name) {
044: this (name, new BufferContent());
045: }
046:
047: public SwingBuffer(String name, BufferContent content) {
048: super (name);
049: doc = new DefaultStyledDocument(content, styles);
050: this .content = content;
051:
052: pointMarker = new Marker(this , 0, true);
053: markMarker = new Marker();
054:
055: modelineDocument = new javax.swing.text.DefaultStyledDocument(
056: new javax.swing.text.StringContent(), styles);
057: // Needed for proper bidi (bi-directional text) handling.
058: // Does cause extra overhead, so should perhaps not be default.
059: // Instead only set it if we insert Hebrew/Arabic text? FIXME.
060: doc.putProperty("i18n", Boolean.TRUE);
061: redrawModeline();
062: }
063:
064: public void removeRegion(int start, int end)
065: throws javax.swing.text.BadLocationException {
066: doc.remove(start, end - start);
067: }
068:
069: public void removeAll() {
070: try {
071: doc.remove(0, maxDot());
072: } catch (javax.swing.text.BadLocationException ex) {
073: throw new gnu.mapping.WrappedException(ex);
074: }
075: }
076:
077: public void removeChar(int count) {
078: remove(getDot(), count);
079: }
080:
081: void remove(int point, int count) {
082: try {
083: if (count < 0) {
084: count = -count;
085: if (point - count < minDot())
086: Signal.signal("Beginning of buffer");
087: point -= count;
088: } else {
089: if (point + count > maxDot())
090: Signal.signal("End of buffer");
091: }
092: doc.remove(point, count);
093:
094: // Should not be needed, but seems to be. Otherwise, getDot()
095: // returns its *old* value, which is `count' characters too high.
096: // The problem seesm to be that Swing does not properly update
097: // a Windows's caret position when the underlying Document has text
098: // removed. Unfortunately, this fix probably won't do the right
099: // thing for *other windows* that reference the same buffer. FIXME.
100: // (Strangely, the correct thing seems to happen for insertions.)
101: setDot(point);
102: } catch (javax.swing.text.BadLocationException ex) {
103: throw new Error("bad location: " + ex);
104: }
105: //doc.remove(index, count);
106: }
107:
108: public void removePos(int ipos, int count) {
109: remove(nextIndex(ipos), count);
110: }
111:
112: public void save(Writer out) throws Exception {
113: int length = getLength();
114: int todo = length;
115: Segment segment = new Segment();
116: int offset = 0;
117: while (offset < length) {
118: int count = length;
119: if (count > 4096)
120: count = 4096;
121: doc.getText(offset, count, segment);
122: out.write(segment.array, segment.offset, segment.count);
123: offset += count;
124: }
125: }
126:
127: public void insert(char ch, int count) {
128: pointMarker.insert(ch, count, inputStyle);
129: }
130:
131: public void insert(int index, String string, Object style) {
132: if (style == null)
133: style = defaultStyle;
134: try {
135: doc.insertString(index, string, (Style) style);
136: } catch (javax.swing.text.BadLocationException ex) {
137: throw new Error("bad location: " + ex);
138: }
139: }
140:
141: public void insert(String string, Object style) {
142: insert(getDot(), string, style);
143: }
144:
145: public void insert(String string, Object style, int ipos) {
146: insert(nextIndex(ipos), string, style);
147: }
148:
149: public void insertFile(Reader in) throws Exception {
150: char[] buffer = new char[2048];
151: int offset = getDot();
152: for (;;) {
153: int count = in.read(buffer, 0, buffer.length);
154: if (count <= 0)
155: break;
156: doc
157: .insertString(offset, new String(buffer, 0, count),
158: null);
159: offset += count;
160: }
161: }
162:
163: public void redrawModeline() {
164: try {
165: modelineDocument.remove(0, modelineDocument.getLength());
166:
167: modelineDocument.insertString(0, "-----", redStyle);
168: modelineDocument.insertString(modelineDocument.getLength(),
169: "JEmacs: " + getName(), blueStyle);
170: modelineDocument.insertString(modelineDocument.getLength(),
171: " ---", redStyle);
172: } catch (javax.swing.text.BadLocationException ex) {
173: throw new Error("internal error in redraw-modeline- " + ex);
174: }
175: }
176:
177: public long scan(char target, int start, int end, int count,
178: boolean allowQuit) {
179: if (end == 0)
180: end = count > 0 ? content.length() - 1 : 0;
181: return content.scan(target, start, end, count, allowQuit);
182: }
183:
184: public int lineStartOffset(int offset) {
185: return (int) content.scan('\n', offset, minDot(), -1, true);
186: }
187:
188: public int getDot() {
189: if (pointMarker.sequence == null)
190: return curPosition.getDot();
191: else
192: return pointMarker.getOffset();
193: }
194:
195: public void setDot(int i) {
196: if (i > maxDot())
197: throw new Error("set dot to " + i + " max:" + maxDot());
198: if (pointMarker.sequence == null)
199: curPosition.setDot(i);
200: else
201: pointMarker.set(this , i);
202: }
203:
204: public int maxDot() {
205: // Subtract 1 for the content's final "\n".
206: return content.length() - 1;
207: }
208:
209: public int getLength() {
210: return doc.getLength();
211: }
212:
213: public CharSeq getStringContent() {
214: return content.buffer;
215: }
216:
217: public int createPos(int index, boolean isAfter) {
218: return content.buffer.createPos(index, isAfter);
219: }
220:
221: public Object get(int index) {
222: return content.buffer.get(index);
223: }
224:
225: public int size() {
226: return content.buffer.size();
227: }
228:
229: public int nextIndex(int ipos) {
230: return content.buffer.nextIndex(ipos);
231: }
232:
233: public long savePointMark() {
234: int pointPosition = content.buffer.createPos(getDot(), false);
235: int markPosition = 0; // FIXME
236: return ((long) markPosition) << 32
237: | ((long) pointPosition & 0xffffffffl);
238: }
239:
240: public void restorePointMark(long pointMark) {
241: int pointPosition = (int) pointMark;
242: int markPosition = (int) (pointMark >> 32);
243: setDot(content.buffer.nextIndex(pointPosition));
244: content.buffer.releasePos(pointPosition);
245: // Restore mark - FIXME
246: // content.releasePosition(markPosition);
247: }
248:
249: public InPort openReader(int start, int count) {
250: return new BufferReader(content.buffer, getPath(), start, count);
251: }
252:
253: /**
254: * @see gnu.jemacs.buffer.Buffer#invoke(java.lang.Runnable)
255: */
256: public void invoke(Runnable doRun) {
257: try {
258: javax.swing.SwingUtilities.invokeAndWait(doRun);
259: } catch (Exception e) {
260: throw new WrappedException(e);
261: }
262: }
263:
264: }
|