001: package snow.texteditor;
002:
003: import snow.utils.SysUtils;
004: import java.awt.event.ActionListener;
005: import java.awt.event.ActionEvent;
006: import tide.annotations.ReturnInstanceRef;
007: import snow.utils.gui.GUIUtils;
008: import snow.utils.DateUtils;
009: import snow.Basics;
010: import javax.swing.text.*;
011: import javax.swing.*;
012: import java.awt.*;
013: import java.util.*;
014: import java.io.*;
015:
016: /** A simple styled document with highlighter, errors, writers, ...
017: * Todo: use a performant readwrite lock system.
018: */
019: @net.jcip.annotations.ThreadSafe
020: public class SimpleDocument extends DefaultStyledDocument implements
021: Appendable {
022: public SimpleColorHighlighter searchHighlighter = new SimpleColorHighlighter(
023: new Color(30, 190, 30, 70));
024: //no UIManager.getColor("Tree.selectionBackground"));
025: //public SimpleColorHighlighter autoSelectHighlighter = new SimpleColorHighlighter( new Color(120, 120, 120, 50) );
026: // used to highlight errors (definitions)
027: public static final SimpleColorHighlighter errorHighlighter = new SimpleColorHighlighter(
028: new Color(200, 130, 30, 50));
029: public static final SimpleColorHighlighter autoSelectHighlighter = new SimpleColorHighlighter(
030: new Color(120, 120, 120, 50));
031:
032: volatile double viewMagnification = 1.0;
033:
034: public SimpleDocument() {
035: super ();
036:
037: //Style defaultStyle = this.getStyle("default");
038: Style def = StyleContext.getDefaultStyleContext().getStyle(
039: StyleContext.DEFAULT_STYLE);
040:
041: Style regular = addStyle("regular", def); // the base
042:
043: this .addStyle("body", regular);
044:
045: Style error = this .addStyle("error", regular);
046: StyleConstants.setForeground(error, Color.red);
047:
048: // no more used ?
049: Style highlightStyle = this .addStyle("highlight", regular);
050: StyleConstants.setBackground(highlightStyle, Color.blue);
051: StyleConstants.setForeground(highlightStyle, Color.white);
052:
053: setTabsForDoc(2, 20);
054: }
055:
056: /** Slow !
057: */
058: public char getCharAt(int pos) {
059: try {
060: return this .getText(pos, 1).charAt(0);
061: } catch (Exception e) {
062: System.err.println("Cannot read char at " + pos
063: + " in doc: " + e.getMessage());
064: e.printStackTrace();
065: return 0;
066: }
067: }
068:
069: /** @return the text from start(comprise) to end (non comprise).
070: */
071: public String getTextFromTo(int start, int end) {
072: if (start == -1)
073: return "";
074: if (end <= start)
075: return "";
076:
077: try {
078: return getText(start, end - start);
079: } catch (Exception e) {
080: e.printStackTrace();
081: }
082: return "";
083: }
084:
085: @ReturnInstanceRef
086: public SimpleDocument append(String s) {
087: return this .insertString(s, this .getLength());
088: }
089:
090: public void appendLine(String s) {
091: this .insertString(s + "\r\n", this .getLength());
092: }
093:
094: public void appendDatedLine(String s) {
095: // not human (Yesterday, ...)
096: this .insertString(DateUtils.formatDateAndTimeFix(System
097: .currentTimeMillis())
098: + ": " + s + "\r\n", this .getLength());
099: }
100:
101: public void appendErrorLine(String s) {
102: try {
103: this .insertString(this .getLength(), s + "\r\n", this
104: .getStyle("error"));
105: } catch (Exception e) {
106: System.err.println("Cannot write text in doc("
107: + e.getMessage() + "):\n" + s);
108: }
109: }
110:
111: public void appendError(String s) {
112: try {
113: this .insertString(this .getLength(), s, this
114: .getStyle("error"));
115: } catch (Exception e) {
116: System.err.println("Cannot write text in doc("
117: + e.getMessage() + "):\n" + s);
118: }
119: }
120:
121: /** Called from the search/replace dialog...
122: * NOT TESTED !!
123: */
124: public void replace(String txt, int pos, int len) {
125: //try
126: {
127: //Element pe = getCharacterElement(pos);
128: //this.replace(pos, len, txt, pe.getAttributes());
129: // TEST MODE !!
130: this .setCharacterAttributes(pos, len, this
131: .getStyle("highlight"), true);
132:
133: }
134: //catch (BadLocationException e) {}
135: }
136:
137: public void setText(String text) {
138: clearDocument();
139: insertString(text, 0);
140: }
141:
142: /** Seems to cause repaints (=> please call in EDT).
143: */
144: public void clearDocument() {
145: writeLock();
146: try {
147: // Marked as thread safe in the doc !!
148: this .remove(0, this .getLength());
149: } catch (Exception ignored) {
150: Basics.ignore(ignored);
151: } finally {
152: writeUnlock();
153: }
154: }
155:
156: /** Called from editor to view the whole document with a bigger font.
157: * Changes the "default" style.
158: */
159: public void increaseFontSize() {
160: viewMagnification *= 1.1;
161: rescaleFontSizes();
162: }
163:
164: /** Called from editor to view the whole document with a smaller font.
165: * Changes the "default" style
166: */
167: public void decreaseFontSize() {
168: viewMagnification /= 1.1;
169: rescaleFontSizes();
170: }
171:
172: /** Use the global viewMagnification factor.
173: */
174: private void rescaleFontSizes() {
175: int baseSize = UIManager.getFont("EditorPane.font").getSize(); // this is the user font from the look and feel ...
176:
177: // ok, changing this cause change of comment, error, ... styles
178: Style s = this .getStyle("default");
179: //int fs = StyleConstants.getFontSize(s);
180: //System.out.println("actual regular fs: " + baseSize);
181:
182: StyleConstants.setFontSize(s, (int) Math
183: .round(viewMagnification * baseSize));
184: }
185:
186: @ReturnInstanceRef
187: public SimpleDocument insertString(String s, int pos) {
188: writeLock();
189: try {
190: this .insertString(pos, s, this .getStyle("body")); // [Jan2006] use body style instead of the last !
191: } catch (Exception ignored) {
192: Basics.ignore(ignored);
193: } finally {
194: writeUnlock();
195: }
196: return this ;
197: }
198:
199: /** Gets the whole text ! (Without alternate form of JComponents added, however, ... )
200: */
201: public String getText() {
202: readLock();
203: try {
204: return this .getText(0, this .getLength());
205: } catch (Exception err) {
206: throw new RuntimeException(err);
207: } finally {
208: readUnlock();
209: }
210: }
211:
212: //
213: // "Searchable"
214: //
215:
216: /** @return -1 if not found
217: * @param ignoreCase if true, the text to search for must be passed in uppercase
218: */
219: public int search(String str, int from, boolean ignoreCase) {
220: if (from < 0)
221: from = 0; // important !
222:
223: readLock();
224: try {
225: String textPart = this
226: .getTextFromTo(from, this .getLength());
227: if (ignoreCase)
228: textPart = textPart.toUpperCase();
229: int posPart = textPart.indexOf(str);
230: if (posPart == -1)
231: return -1;
232: return posPart + from;
233: } finally {
234: readUnlock();
235: }
236: }
237:
238: /** @return -1 if not found
239: * @param ignoreCase if true, the text to search for must be passed in uppercase.
240: * @param from -1 to find the last (i.e. start from end).
241: */
242: public int searchBackward(String str, int from, boolean ignoreCase) {
243: if (from < 0)
244: from = this .getLength(); // important !
245:
246: readLock();
247: try {
248: String textPart = this .getTextFromTo(0, from);
249: if (ignoreCase)
250: textPart = textPart.toUpperCase();
251: int posPart = textPart.lastIndexOf(str);
252: if (posPart == -1)
253: return -1;
254: return posPart;
255: } finally {
256: readUnlock();
257: }
258: }
259:
260: /** Sets the tab to be used for the document.
261: * Should be called at end !!
262: */
263: public void setTabsForDoc(int tabWidth, int nTabs) {
264: int fs = 12;
265: try {
266: fs = UIManager.getFont("EditorPane.font").getSize();
267: } catch (Exception e) {
268: e.printStackTrace();
269: }
270:
271: int tabWidthInDPI = fs * tabWidth;
272:
273: TabStop[] tabs = new TabStop[nTabs];
274: double tab = fs * tabWidth;
275: for (int j = 0; j < tabs.length; j++) {
276: //System.out.println(""+tab);
277: tabs[j] = new TabStop((float) tab, TabStop.ALIGN_LEFT,
278: TabStop.LEAD_NONE);
279: tab += tabWidthInDPI;
280: }
281: TabSet tabSet = new TabSet(tabs);
282: setTabsForDoc(tabSet);
283: }
284:
285: /** Sets the tab to be used for the document.
286: * Should be called at end !!
287: */
288: public void setTabsForDoc(TabSet tabSet) {
289: SimpleAttributeSet attributes = new SimpleAttributeSet();
290: StyleConstants.setTabSet(attributes, tabSet);
291: try {
292: setParagraphAttributes(0, getLength(), attributes, false);
293: } catch (Exception e) {
294: throw new RuntimeException("Cannot set tabs: "
295: + e.getMessage());
296: }
297: }
298:
299: /** Append txt with all lines prepended with prependLine (ex: "out> ")
300: * don't forget the CR at end...
301: */
302: public void appendNamed(String txt, String prependLine) {
303: BufferedReader in = new BufferedReader(new StringReader(txt));
304: String line = null;
305: try {
306: while ((line = in.readLine()) != null) {
307: append("\n" + prependLine + line);
308: }
309: } catch (Exception e) {
310: appendError("Cannot read line: " + e.getMessage());
311: }
312: }
313:
314: /** Append a component (ONLY USE IF ONE VIEW IS SHOWING THIS DOC !!).
315: */
316: public void append(final JComponent c) {
317: SimpleAttributeSet attributes = new SimpleAttributeSet();
318: StyleConstants.setComponent(attributes, c);
319: this .append(" "); // dummy
320: this .setCharacterAttributes(this .getLength() - 1, 1,
321: attributes, true);
322: }
323:
324: /** DO NOT USE IF document is shared in several views !
325: */
326: public void append(final JComponent c, int pos, int len) {
327: SimpleAttributeSet attributes = new SimpleAttributeSet();
328: StyleConstants.setComponent(attributes, c);
329: //this.append(" ");
330: this .setCharacterAttributes(pos, len, attributes, true);
331: }
332:
333: private static Font getFontFrom(AttributeSet as) {
334: return new Font(StyleConstants.getFontFamily(as), Font.PLAIN,
335: StyleConstants.getFontSize(as));
336: }
337:
338: /** DO NOT USE IF document is shared in several views !
339: */
340: public void appendClickableURL(final String ref, int pos, int len) {
341: final JButton bt = GUIUtils.createClickableURL(ref);
342:
343: AttributeSet pe = this .getStyle("default");
344: bt.setFont(getFontFrom(pe));
345: bt.setOpaque(false);
346: bt.setForeground(Color.BLUE);
347:
348: if (pos < 0) {
349: append(bt);
350: } else {
351: append(bt, pos, len);
352: }
353: }
354:
355: //
356: // Appendable
357: //
358:
359: public Appendable append(char c) {
360: this .append("" + c);
361: return this ;
362: }
363:
364: public Appendable append(CharSequence csq) {
365: this .append("" + csq); // FB: apparent infinite loop [Nov2007] code was this.append(csq);
366: return this ;
367: }
368:
369: public Appendable append(CharSequence csq, int start, int end) {
370: this .append(csq.subSequence(start, end));
371: return this ;
372: }
373:
374: // Allow connecting system.out / err
375: //
376:
377: public PrintStream createPrintStreamForThisDocument(
378: boolean errorMode) {
379: //avoid passing null
380: final OutputStream os = new OutputStream() {
381: public void write(int b) {
382: }
383: };
384:
385: FilterOutputStream fs = new FilterOutputStream(os) {
386: /*@Override
387: public synchronized void write(final int b) throws IOException
388: {
389:
390: }*/
391: @Override
392: public synchronized void write(final byte[] b,
393: final int off, final int len) throws IOException {
394: append(new String(b, off, len));
395: }
396:
397: @Override
398: public synchronized void write(final byte[] b)
399: throws IOException {
400: append(new String(b));
401: }
402: };
403: return new PrintStream(fs);
404: }
405:
406: // Allow transparently writing in this document using a Writer
407: //
408:
409: public Writer createWriterForThisDocument(boolean errorMode) {
410: return new DocWriter(errorMode);
411: }
412:
413: class DocWriter extends Writer {
414: boolean errorWriter;
415:
416: public DocWriter(boolean errorWriter) {
417: super (SimpleDocument.this );
418: this .errorWriter = errorWriter;
419: }
420:
421: @Override
422: public void flush() {
423: }
424:
425: @Override
426: public void write(char[] cbuf, int off, int len) {
427: try {
428: if (errorWriter) {
429: SimpleDocument.this .appendError(new String(cbuf,
430: off, len)); // don't add directly here !!! (infinite loop !)
431: } else {
432: SimpleDocument.this .append(new String(cbuf, off,
433: len)); // don't add directly here !!! (infinite loop !)
434: }
435: } catch (Exception e) {
436: appendErrorLine("\r\ncannot append char buffer: "
437: + e.getMessage());
438: }
439: }
440:
441: @Override
442: public void close() {
443: }
444: }
445:
446: /* test */
447: public static void main(String[] arguments) {
448: SimpleDocument doc = new SimpleDocument();
449: JTextPane tp = new JTextPane(doc);
450: doc.append("Test:\nHello ");
451: doc.append("Hello ");
452: JButton bt = new JButton("Hello");
453: bt.setAlignmentY(0.75f);
454: bt.setFont(tp.getFont()); // "default" style
455: GUIUtils.makeNiceButton(bt);
456: doc.append(bt);
457: doc.append(" Hello.\nend and click here, please: ");
458: doc.appendClickableURL("http://java.sun.com", -1, -1);
459: doc.append(" \nnow its the end");
460: GUIUtils.displayInFrame("Test", new JScrollPane(tp), true);
461: }
462:
463: }
|