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:
442: if (a == null)
443: return;
444:
445: float oldMaxLineLength = maxLineLength;
446: Rectangle alloc = a.getBounds();
447: Element el = getElement();
448: ElementChange ec = changes.getChange(el);
449:
450:
451:
452: if (ec == null)
453: {
454: int line = el.getElementIndex(changes.getOffset());
455:
456:
457:
458:
459: if (changes.getType() == DocumentEvent.EventType.REMOVE
460: && el.getElement(line) == longestLine)
461: {
462: maxLineLength = -1;
463: if (determineMaxLineLength() != alloc.width)
464: preferenceChanged(this, true, false);
465: }
466:
467: damageLineRange(line, line, a, getContainer());
468: return;
469: }
470:
471: Element[] removed = ec.getChildrenRemoved();
472: Element[] newElements = ec.getChildrenAdded();
473:
474:
475:
476: if (removed == null && newElements == null)
477: {
478: int line = getElement().getElementIndex(changes.getOffset());
479:
480: damageLineRange(line, line, a, getContainer());
481: return;
482: }
483:
484:
485:
486: if (removed != null)
487: {
488: for (int i = 0; i < removed.length; i++)
489: if (removed[i].equals(longestLine))
490: {
491:
492: maxLineLength = -1;
493: if (determineMaxLineLength() != alloc.width)
494: preferenceChanged(this, true, removed.length != newElements.length);
495:
496: ((JTextComponent)getContainer()).repaint();
497:
498: return;
499: }
500: }
501:
502:
503: if (newElements == null)
504: {
505:
506: ((JTextComponent)getContainer()).repaint();
507:
508: return;
509: }
510:
511:
512: updateMetrics();
513:
514:
515:
516:
517: Segment seg = getLineBuffer();
518: float longestNewLength = 0;
519: Element longestNewLine = null;
520:
521:
522: for (int i = 0; i < newElements.length; i++)
523: {
524: Element child = newElements[i];
525: int start = child.getStartOffset();
526: int end = child.getEndOffset() - 1;
527: try
528: {
529: el.getDocument().getText(start, end - start, seg);
530: }
531: catch (BadLocationException ex)
532: {
533: AssertionError ae = new AssertionError("Unexpected bad location");
534: ae.initCause(ex);
535: throw ae;
536: }
537:
538: if (seg == null || seg.array == null || seg.count == 0)
539: continue;
540:
541: int width = metrics.charsWidth(seg.array, seg.offset, seg.count);
542: if (width > longestNewLength)
543: {
544: longestNewLine = child;
545: longestNewLength = width;
546: }
547: }
548:
549:
550:
551: if (longestNewLength > maxLineLength)
552: {
553: maxLineLength = longestNewLength;
554: longestLine = longestNewLine;
555: }
556:
557:
558:
559: boolean widthChanged = oldMaxLineLength != maxLineLength;
560: boolean heightChanged = removed.length != newElements.length;
561: if (widthChanged || heightChanged)
562: preferenceChanged(this, widthChanged, heightChanged);
563:
564:
565: ((JTextComponent)getContainer()).repaint();
566: }
567:
568:
576: public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f)
577: {
578: updateDamage(changes, a, f);
579: }
580:
581:
589: public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f)
590: {
591: updateDamage(changes, a, f);
592: }
593:
594:
598: public void changedUpdate (DocumentEvent changes, Shape a, ViewFactory f)
599: {
600: updateDamage(changes, a, f);
601: }
602:
603:
617: protected void damageLineRange (int line0, int line1, Shape a, Component host)
618: {
619: if (a == null)
620: return;
621:
622: Rectangle rec0 = lineToRect(a, line0);
623: Rectangle rec1 = lineToRect(a, line1);
624:
625: if (rec0 == null || rec1 == null)
626:
627: host.repaint();
628: else
629: {
630: Rectangle repaintRec = SwingUtilities.computeUnion(rec0.x, rec0.y,
631: rec0.width,
632: rec0.height, rec1);
633: host.repaint(repaintRec.x, repaintRec.y, repaintRec.width,
634: repaintRec.height);
635: }
636: }
637:
638:
645: protected final Segment getLineBuffer()
646: {
647: if (lineBuffer == null)
648: lineBuffer = new Segment();
649: return lineBuffer;
650: }
651: }