1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48:
49: import ;
50: import ;
51: import ;
52:
53: public class PlainView extends View implements TabExpander
54: {
55: Color selectedColor;
56: Color unselectedColor;
57:
58:
61: Color disabledColor;
62:
63:
67: int selectionStart;
68:
69:
73: int selectionEnd;
74:
75: Font font;
76:
77:
78: float maxLineLength = -1;
79:
80:
81: Element longestLine = null;
82:
83: protected FontMetrics metrics;
84:
85:
88: private transient Segment lineBuffer;
89:
90: public PlainView(Element elem)
91: {
92: super(elem);
93: }
94:
95:
98: protected void updateMetrics()
99: {
100: Component component = getContainer();
101: Font font = component.getFont();
102:
103: if (this.font != font)
104: {
105: this.font = font;
106: metrics = component.getFontMetrics(font);
107: }
108: }
109:
110:
113: protected Rectangle lineToRect(Shape a, int line)
114: {
115:
116: updateMetrics();
117:
118: Rectangle rect = a.getBounds();
119: int fontHeight = metrics.getHeight();
120: return new Rectangle(rect.x, rect.y + (line * fontHeight),
121: rect.width, fontHeight);
122: }
123:
124: public Shape modelToView(int position, Shape a, Position.Bias b)
125: throws BadLocationException
126: {
127:
128: updateMetrics();
129:
130: Document document = getDocument();
131:
132:
133: int lineIndex = getElement().getElementIndex(position);
134: Rectangle rect = lineToRect(a, lineIndex);
135:
136:
137: Element line = getElement().getElement(lineIndex);
138: int lineStart = line.getStartOffset();
139: Segment segment = getLineBuffer();
140: document.getText(lineStart, position - lineStart, segment);
141: int xoffset = Utilities.getTabbedTextWidth(segment, metrics, rect.x,
142: this, lineStart);
143:
144:
145: rect.x += xoffset;
146: rect.width = 1;
147: rect.height = metrics.getHeight();
148:
149: return rect;
150: }
151:
152:
161: protected void drawLine(int lineIndex, Graphics g, int x, int y)
162: {
163: try
164: {
165: Element line = getElement().getElement(lineIndex);
166: int startOffset = line.getStartOffset();
167: int endOffset = line.getEndOffset() - 1;
168:
169: if (selectionStart <= startOffset)
170:
171: if (selectionEnd <= startOffset)
172: {
173:
174: drawUnselectedText(g, x, y, startOffset, endOffset);
175: }
176: else if (selectionEnd <= endOffset)
177: {
178:
179:
180: x = drawSelectedText(g, x, y, startOffset, selectionEnd);
181: drawUnselectedText(g, x, y, selectionEnd, endOffset);
182: }
183: else
184:
185: drawSelectedText(g, x, y, startOffset, endOffset);
186: else if (selectionStart < endOffset)
187:
188: if (selectionEnd < endOffset)
189: {
190:
191:
192: x = drawUnselectedText(g, x, y, startOffset, selectionStart);
193: x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
194: drawUnselectedText(g, x, y, selectionEnd, endOffset);
195: }
196: else
197: {
198:
199:
200: x = drawUnselectedText(g, x, y, startOffset, selectionStart);
201: drawSelectedText(g, x, y, selectionStart, endOffset);
202: }
203: else
204:
205: drawUnselectedText(g, x, y, startOffset, endOffset);
206: }
207: catch (BadLocationException e)
208: {
209: AssertionError ae = new AssertionError("Unexpected bad location");
210: ae.initCause(e);
211: throw ae;
212: }
213: }
214:
215: protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
216: throws BadLocationException
217: {
218: g.setColor(selectedColor);
219: Segment segment = getLineBuffer();
220: getDocument().getText(p0, p1 - p0, segment);
221: return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
222: }
223:
224:
238: protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
239: throws BadLocationException
240: {
241: JTextComponent textComponent = (JTextComponent) getContainer();
242: if (textComponent.isEnabled())
243: g.setColor(unselectedColor);
244: else
245: g.setColor(disabledColor);
246:
247: Segment segment = getLineBuffer();
248: getDocument().getText(p0, p1 - p0, segment);
249: return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
250: }
251:
252: public void paint(Graphics g, Shape s)
253: {
254:
255: updateMetrics();
256:
257: JTextComponent textComponent = (JTextComponent) getContainer();
258:
259: selectedColor = textComponent.getSelectedTextColor();
260: unselectedColor = textComponent.getForeground();
261: disabledColor = textComponent.getDisabledTextColor();
262: selectionStart = textComponent.getSelectionStart();
263: selectionEnd = textComponent.getSelectionEnd();
264:
265: Rectangle rect = s.getBounds();
266:
267:
268: Document document = textComponent.getDocument();
269: Element root = document.getDefaultRootElement();
270: int y = rect.y + metrics.getAscent();
271: int height = metrics.getHeight();
272:
273: int count = root.getElementCount();
274: for (int i = 0; i < count; i++)
275: {
276: drawLine(i, g, rect.x, y);
277: y += height;
278: }
279: }
280:
281:
288: protected int getTabSize()
289: {
290: Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
291: if (tabSize == null)
292: return 8;
293: return ((Integer)tabSize).intValue();
294: }
295:
296:
304: public float nextTabStop(float x, int tabStop)
305: {
306: float tabSizePixels = getTabSize() * metrics.charWidth('m');
307: return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
308: }
309:
310:
314: float determineMaxLineLength()
315: {
316:
317: if (maxLineLength != -1)
318: return maxLineLength;
319:
320:
321: Element el = getElement();
322: Segment seg = getLineBuffer();
323: float span = 0;
324: for (int i = 0; i < el.getElementCount(); i++)
325: {
326: Element child = el.getElement(i);
327: int start = child.getStartOffset();
328: int end = child.getEndOffset() - 1;
329: try
330: {
331: el.getDocument().getText(start, end - start, seg);
332: }
333: catch (BadLocationException ex)
334: {
335: AssertionError ae = new AssertionError("Unexpected bad location");
336: ae.initCause(ex);
337: throw ae;
338: }
339:
340: if (seg == null || seg.array == null || seg.count == 0)
341: continue;
342:
343: int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
344: if (width > span)
345: {
346: longestLine = child;
347: span = width;
348: }
349: }
350: maxLineLength = span;
351: return maxLineLength;
352: }
353:
354: public float getPreferredSpan(int axis)
355: {
356: if (axis != X_AXIS && axis != Y_AXIS)
357: throw new IllegalArgumentException();
358:
359:
360: updateMetrics();
361:
362: Element el = getElement();
363: float span;
364:
365: switch (axis)
366: {
367: case X_AXIS:
368: span = determineMaxLineLength();
369: break;
370: case Y_AXIS:
371: default:
372: span = metrics.getHeight() * el.getElementCount();
373: break;
374: }
375:
376: return span;
377: }
378:
379:
391: public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
392: {
393: Rectangle rec = a.getBounds();
394: Document doc = getDocument();
395: Element root = doc.getDefaultRootElement();
396:
397:
398:
399:
400:
401:
402:
403:
404:
405: int lineClicked
406: = Math.min(Math.max((int) (y - rec.y) / metrics.getHeight(), 0),
407: root.getElementCount() - 1);
408:
409: Element line = root.getElement(lineClicked);
410:
411: Segment s = getLineBuffer();
412: int start = line.getStartOffset();
413:
414: int end = line.getEndOffset() - 1;
415: try
416: {
417: doc.getText(start, end - start, s);
418: }
419: catch (BadLocationException ble)
420: {
421: AssertionError ae = new AssertionError("Unexpected bad location");
422: ae.initCause(ble);
423: throw ae;
424: }
425:
426: int pos = Utilities.getTabbedTextOffset(s, metrics, rec.x, (int)x, this, start);
427: return Math.max (0, pos);
428: }
429:
430:
438: protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f)
439: {
440:
441: if (metrics == null)
442: {
443: updateMetrics();
444: preferenceChanged(null, true, true);
445: return;
446: }
447:
448: Element element = getElement();
449:
450:
451: if (longestLine == null)
452: findLongestLine(0, element.getElementCount() - 1);
453:
454: ElementChange change = changes.getChange(element);
455: if (changes.getType() == DocumentEvent.EventType.INSERT)
456: {
457:
458:
459:
460:
461: boolean linesAdded = true;
462: if (change == null)
463: linesAdded = false;
464:
465:
466: int start;
467: if (linesAdded)
468: start = change.getIndex();
469: else
470: start = element.getElementIndex(changes.getOffset());
471:
472:
473: int length = 0;
474: if (linesAdded)
475: length = change.getChildrenAdded().length - 1;
476:
477:
478: int oldMaxLength = (int) maxLineLength;
479: if (longestLine.getEndOffset() < changes.getOffset()
480: || longestLine.getStartOffset() > changes.getOffset()
481: + changes.getLength())
482: {
483: findLongestLine(start, start + length);
484: }
485: else
486: {
487: findLongestLine(0, element.getElementCount() - 1);
488: }
489:
490:
491:
492: preferenceChanged(null, maxLineLength != oldMaxLength, linesAdded);
493:
494:
495: int endLine = start;
496: if (linesAdded)
497: endLine = element.getElementCount() - 1;
498: damageLineRange(start, endLine, a, getContainer());
499:
500: }
501: else
502: {
503:
504:
505:
506: int oldMaxLength = (int) maxLineLength;
507: if (change != null)
508: {
509:
510: findLongestLine(0, element.getElementCount() - 1);
511: preferenceChanged(null, maxLineLength != oldMaxLength, true);
512: }
513: else
514: {
515:
516: int lineNo = getElement().getElementIndex(changes.getOffset());
517: Element line = getElement().getElement(lineNo);
518: if (longestLine == line)
519: {
520: findLongestLine(0, element.getElementCount() - 1);
521: preferenceChanged(null, maxLineLength != oldMaxLength, false);
522: }
523: damageLineRange(lineNo, lineNo, a, getContainer());
524: }
525: }
526: }
527:
528:
536: public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f)
537: {
538: updateDamage(changes, a, f);
539: }
540:
541:
549: public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f)
550: {
551: updateDamage(changes, a, f);
552: }
553:
554:
558: public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f)
559: {
560: updateDamage(changes, a, f);
561: }
562:
563:
577: protected void damageLineRange (int line0, int line1, Shape a, Component host)
578: {
579: if (a == null)
580: return;
581:
582: Rectangle rec0 = lineToRect(a, line0);
583: Rectangle rec1 = lineToRect(a, line1);
584:
585: if (rec0 == null || rec1 == null)
586:
587: host.repaint();
588: else
589: {
590: Rectangle repaintRec = SwingUtilities.computeUnion(rec0.x, rec0.y,
591: rec0.width,
592: rec0.height, rec1);
593: host.repaint(repaintRec.x, repaintRec.y, repaintRec.width,
594: repaintRec.height);
595: }
596: }
597:
598:
605: protected final Segment getLineBuffer()
606: {
607: if (lineBuffer == null)
608: lineBuffer = new Segment();
609: return lineBuffer;
610: }
611:
612:
619: private void findLongestLine(int start, int end)
620: {
621: for (int i = start; i <= end; i++)
622: {
623: int w = getLineLength(i);
624: if (w > maxLineLength)
625: {
626: maxLineLength = w;
627: longestLine = getElement().getElement(i);
628: }
629: }
630: }
631:
632:
639: private int getLineLength(int line)
640: {
641: Element lineEl = getElement().getElement(line);
642: Segment buffer = getLineBuffer();
643: try
644: {
645: Document doc = getDocument();
646: doc.getText(lineEl.getStartOffset(),
647: lineEl.getEndOffset() - lineEl.getStartOffset() - 1,
648: buffer);
649: }
650: catch (BadLocationException ex)
651: {
652: AssertionError err = new AssertionError("Unexpected bad location");
653: err.initCause(ex);
654: throw err;
655: }
656:
657: return Utilities.getTabbedTextWidth(buffer, metrics, 0, this,
658: lineEl.getStartOffset());
659: }
660: }