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:
93: private int tabBase;
94:
95:
98: private int tabSize;
99:
100: public PlainView(Element elem)
101: {
102: super(elem);
103: }
104:
105:
108: protected void updateMetrics()
109: {
110: Component component = getContainer();
111: Font font = component.getFont();
112:
113: if (this.font != font)
114: {
115: this.font = font;
116: metrics = component.getFontMetrics(font);
117: tabSize = getTabSize() * metrics.charWidth('m');
118: }
119: }
120:
121:
124: protected Rectangle lineToRect(Shape a, int line)
125: {
126:
127: updateMetrics();
128:
129: Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
130: int fontHeight = metrics.getHeight();
131: return new Rectangle(rect.x, rect.y + (line * fontHeight),
132: rect.width, fontHeight);
133: }
134:
135: public Shape modelToView(int position, Shape a, Position.Bias b)
136: throws BadLocationException
137: {
138:
139: updateMetrics();
140:
141: Document document = getDocument();
142:
143:
144: int lineIndex = getElement().getElementIndex(position);
145: Rectangle rect = lineToRect(a, lineIndex);
146: tabBase = rect.x;
147:
148:
149: Element line = getElement().getElement(lineIndex);
150: int lineStart = line.getStartOffset();
151: Segment segment = getLineBuffer();
152: document.getText(lineStart, position - lineStart, segment);
153: int xoffset = Utilities.getTabbedTextWidth(segment, metrics, tabBase,
154: this, lineStart);
155:
156:
157: rect.x += xoffset;
158: rect.width = 1;
159: rect.height = metrics.getHeight();
160:
161: return rect;
162: }
163:
164:
173: protected void drawLine(int lineIndex, Graphics g, int x, int y)
174: {
175: try
176: {
177: Element line = getElement().getElement(lineIndex);
178: int startOffset = line.getStartOffset();
179: int endOffset = line.getEndOffset() - 1;
180:
181: if (selectionStart <= startOffset)
182:
183: if (selectionEnd <= startOffset)
184: {
185:
186: drawUnselectedText(g, x, y, startOffset, endOffset);
187: }
188: else if (selectionEnd <= endOffset)
189: {
190:
191:
192: x = drawSelectedText(g, x, y, startOffset, selectionEnd);
193: drawUnselectedText(g, x, y, selectionEnd, endOffset);
194: }
195: else
196:
197: drawSelectedText(g, x, y, startOffset, endOffset);
198: else if (selectionStart < endOffset)
199:
200: if (selectionEnd < endOffset)
201: {
202:
203:
204: x = drawUnselectedText(g, x, y, startOffset, selectionStart);
205: x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
206: drawUnselectedText(g, x, y, selectionEnd, endOffset);
207: }
208: else
209: {
210:
211:
212: x = drawUnselectedText(g, x, y, startOffset, selectionStart);
213: drawSelectedText(g, x, y, selectionStart, endOffset);
214: }
215: else
216:
217: drawUnselectedText(g, x, y, startOffset, endOffset);
218: }
219: catch (BadLocationException e)
220: {
221: AssertionError ae = new AssertionError("Unexpected bad location");
222: ae.initCause(e);
223: throw ae;
224: }
225: }
226:
227: protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
228: throws BadLocationException
229: {
230: g.setColor(selectedColor);
231: Segment segment = getLineBuffer();
232: getDocument().getText(p0, p1 - p0, segment);
233: return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
234: }
235:
236:
250: protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
251: throws BadLocationException
252: {
253: JTextComponent textComponent = (JTextComponent) getContainer();
254: if (textComponent.isEnabled())
255: g.setColor(unselectedColor);
256: else
257: g.setColor(disabledColor);
258:
259: Segment segment = getLineBuffer();
260: getDocument().getText(p0, p1 - p0, segment);
261: return Utilities.drawTabbedText(segment, x, y, g, this, segment.offset);
262: }
263:
264: public void paint(Graphics g, Shape s)
265: {
266:
267: updateMetrics();
268:
269: JTextComponent textComponent = (JTextComponent) getContainer();
270:
271: selectedColor = textComponent.getSelectedTextColor();
272: unselectedColor = textComponent.getForeground();
273: disabledColor = textComponent.getDisabledTextColor();
274: selectionStart = textComponent.getSelectionStart();
275: selectionEnd = textComponent.getSelectionEnd();
276:
277: Rectangle rect = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
278: tabBase = rect.x;
279:
280:
281: Document document = textComponent.getDocument();
282: Element root = getElement();
283: int height = metrics.getHeight();
284:
285:
286:
287: LayeredHighlighter hl = null;
288: Highlighter h = textComponent.getHighlighter();
289: if (h instanceof LayeredHighlighter)
290: hl = (LayeredHighlighter) h;
291:
292: int count = root.getElementCount();
293:
294:
295: Rectangle clip = g.getClipBounds();
296: SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height,
297: clip);
298: int line0 = (clip.y - rect.y) / height;
299: line0 = Math.max(0, Math.min(line0, count - 1));
300: int line1 = (clip.y + clip.height - rect.y) / height;
301: line1 = Math.max(0, Math.min(line1, count - 1));
302: int y = rect.y + metrics.getAscent() + height * line0;
303: for (int i = line0; i <= line1; i++)
304: {
305: if (hl != null)
306: {
307: Element lineEl = root.getElement(i);
308:
309: if (i == count)
310: hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
311: lineEl.getEndOffset(), s, textComponent,
312: this);
313: else
314: hl.paintLayeredHighlights(g, lineEl.getStartOffset(),
315: lineEl.getEndOffset() - 1, s,
316: textComponent, this);
317: }
318: drawLine(i, g, rect.x, y);
319: y += height;
320: }
321: }
322:
323:
330: protected int getTabSize()
331: {
332: Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
333: if (tabSize == null)
334: return 8;
335: return ((Integer)tabSize).intValue();
336: }
337:
338:
346: public float nextTabStop(float x, int tabStop)
347: {
348: float next = x;
349: if (tabSize != 0)
350: {
351: int numTabs = (((int) x) - tabBase) / tabSize;
352: next = tabBase + (numTabs + 1) * tabSize;
353: }
354: return next;
355: }
356:
357:
361: float determineMaxLineLength()
362: {
363:
364: if (maxLineLength != -1)
365: return maxLineLength;
366:
367:
368: Element el = getElement();
369: Segment seg = getLineBuffer();
370: float span = 0;
371: for (int i = 0; i < el.getElementCount(); i++)
372: {
373: Element child = el.getElement(i);
374: int start = child.getStartOffset();
375: int end = child.getEndOffset() - 1;
376: try
377: {
378: el.getDocument().getText(start, end - start, seg);
379: }
380: catch (BadLocationException ex)
381: {
382: AssertionError ae = new AssertionError("Unexpected bad location");
383: ae.initCause(ex);
384: throw ae;
385: }
386:
387: if (seg == null || seg.array == null || seg.count == 0)
388: continue;
389:
390: int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
391: if (width > span)
392: {
393: longestLine = child;
394: span = width;
395: }
396: }
397: maxLineLength = span;
398: return maxLineLength;
399: }
400:
401: public float getPreferredSpan(int axis)
402: {
403: if (axis != X_AXIS && axis != Y_AXIS)
404: throw new IllegalArgumentException();
405:
406:
407: updateMetrics();
408:
409: Element el = getElement();
410: float span;
411:
412: switch (axis)
413: {
414: case X_AXIS:
415: span = determineMaxLineLength();
416: break;
417: case Y_AXIS:
418: default:
419: span = metrics.getHeight() * el.getElementCount();
420: break;
421: }
422:
423: return span;
424: }
425:
426:
438: public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
439: {
440: Rectangle rec = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
441: tabBase = rec.x;
442:
443: int pos;
444: if ((int) y < rec.y)
445:
446: pos = getStartOffset();
447: else if ((int) y > rec.y + rec.height)
448:
449: pos = getEndOffset() - 1;
450: else
451: {
452:
453: Document doc = getDocument();
454: Element root = doc.getDefaultRootElement();
455: int line = Math.abs(((int) y - rec.y) / metrics.getHeight());
456: if (line >= root.getElementCount())
457: pos = getEndOffset() - 1;
458: else
459: {
460: Element lineEl = root.getElement(line);
461: if (x < rec.x)
462:
463: pos = lineEl.getStartOffset();
464: else if (x > rec.x + rec.width)
465:
466: pos = lineEl.getEndOffset() - 1;
467: else
468: {
469: try
470: {
471: int p0 = lineEl.getStartOffset();
472: int p1 = lineEl.getEndOffset();
473: Segment s = new Segment();
474: doc.getText(p0, p1 - p0, s);
475: tabBase = rec.x;
476: pos = p0 + Utilities.getTabbedTextOffset(s, metrics,
477: tabBase, (int) x,
478: this, p0);
479: }
480: catch (BadLocationException ex)
481: {
482:
483: pos = -1;
484: }
485: }
486:
487: }
488: }
489:
490: b[0] = Position.Bias.Forward;
491: return pos;
492: }
493:
494:
502: protected void updateDamage(DocumentEvent changes, Shape a, ViewFactory f)
503: {
504:
505: if (metrics == null)
506: {
507: updateMetrics();
508: preferenceChanged(null, true, true);
509: return;
510: }
511:
512: Element element = getElement();
513:
514:
515: if (longestLine == null)
516: findLongestLine(0, element.getElementCount() - 1);
517:
518: ElementChange change = changes.getChange(element);
519: if (changes.getType() == DocumentEvent.EventType.INSERT)
520: {
521:
522:
523:
524:
525: boolean linesAdded = true;
526: if (change == null)
527: linesAdded = false;
528:
529:
530: int start;
531: if (linesAdded)
532: start = change.getIndex();
533: else
534: start = element.getElementIndex(changes.getOffset());
535:
536:
537: int length = 0;
538: if (linesAdded)
539: length = change.getChildrenAdded().length - 1;
540:
541:
542: int oldMaxLength = (int) maxLineLength;
543: if (longestLine.getEndOffset() < changes.getOffset()
544: || longestLine.getStartOffset() > changes.getOffset()
545: + changes.getLength())
546: {
547: findLongestLine(start, start + length);
548: }
549: else
550: {
551: findLongestLine(0, element.getElementCount() - 1);
552: }
553:
554:
555:
556: preferenceChanged(null, maxLineLength != oldMaxLength, linesAdded);
557:
558:
559: int endLine = start;
560: if (linesAdded)
561: endLine = element.getElementCount() - 1;
562: damageLineRange(start, endLine, a, getContainer());
563:
564: }
565: else
566: {
567:
568:
569:
570: int oldMaxLength = (int) maxLineLength;
571: if (change != null)
572: {
573:
574: findLongestLine(0, element.getElementCount() - 1);
575: preferenceChanged(null, maxLineLength != oldMaxLength, true);
576: }
577: else
578: {
579:
580: int lineNo = getElement().getElementIndex(changes.getOffset());
581: Element line = getElement().getElement(lineNo);
582: if (longestLine == line)
583: {
584: findLongestLine(0, element.getElementCount() - 1);
585: preferenceChanged(null, maxLineLength != oldMaxLength, false);
586: }
587: damageLineRange(lineNo, lineNo, a, getContainer());
588: }
589: }
590: }
591:
592:
600: public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f)
601: {
602: updateDamage(changes, a, f);
603: }
604:
605:
613: public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f)
614: {
615: updateDamage(changes, a, f);
616: }
617:
618:
622: public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f)
623: {
624: updateDamage(changes, a, f);
625: }
626:
627:
641: protected void damageLineRange (int line0, int line1, Shape a, Component host)
642: {
643: if (a == null)
644: return;
645:
646: Rectangle rec0 = lineToRect(a, line0);
647: Rectangle rec1 = lineToRect(a, line1);
648:
649: if (rec0 == null || rec1 == null)
650:
651: host.repaint();
652: else
653: {
654: Rectangle repaintRec = SwingUtilities.computeUnion(rec0.x, rec0.y,
655: rec0.width,
656: rec0.height, rec1);
657: host.repaint(repaintRec.x, repaintRec.y, repaintRec.width,
658: repaintRec.height);
659: }
660: }
661:
662:
669: protected final Segment getLineBuffer()
670: {
671: if (lineBuffer == null)
672: lineBuffer = new Segment();
673: return lineBuffer;
674: }
675:
676:
683: private void findLongestLine(int start, int end)
684: {
685: for (int i = start; i <= end; i++)
686: {
687: int w = getLineLength(i);
688: if (w > maxLineLength)
689: {
690: maxLineLength = w;
691: longestLine = getElement().getElement(i);
692: }
693: }
694: }
695:
696:
703: private int getLineLength(int line)
704: {
705: Element lineEl = getElement().getElement(line);
706: Segment buffer = getLineBuffer();
707: try
708: {
709: Document doc = getDocument();
710: doc.getText(lineEl.getStartOffset(),
711: lineEl.getEndOffset() - lineEl.getStartOffset() - 1,
712: buffer);
713: }
714: catch (BadLocationException ex)
715: {
716: AssertionError err = new AssertionError("Unexpected bad location");
717: err.initCause(ex);
718: throw err;
719: }
720:
721: return Utilities.getTabbedTextWidth(buffer, metrics, tabBase, this,
722: lineEl.getStartOffset());
723: }
724: }