1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47:
48: import ;
49: import ;
50:
51:
55: public class WrappedPlainView extends BoxView implements TabExpander
56: {
57:
58: Color selectedColor;
59:
60:
61: Color unselectedColor;
62:
63:
64: Color disabledColor;
65:
66:
70: FontMetrics metrics;
71:
72:
73: boolean wordWrap;
74:
75:
76: ViewFactory viewFactory = new WrappedLineCreator();
77:
78:
79: int selectionStart;
80:
81:
82: int selectionEnd;
83:
84:
85: int lineHeight;
86:
87:
90: private int tabBase;
91:
92:
95: private int tabSize;
96:
97:
100: private transient Segment lineBuffer;
101:
102: public WrappedPlainView (Element elem)
103: {
104: this (elem, false);
105: }
106:
107: public WrappedPlainView (Element elem, boolean wordWrap)
108: {
109: super (elem, Y_AXIS);
110: this.wordWrap = wordWrap;
111: }
112:
113:
117: protected final Segment getLineBuffer()
118: {
119: if (lineBuffer == null)
120: lineBuffer = new Segment();
121: return lineBuffer;
122: }
123:
124:
132: public float nextTabStop(float x, int tabStop)
133: {
134: int next = (int) x;
135: if (tabSize != 0)
136: {
137: int numTabs = ((int) x - tabBase) / tabSize;
138: next = tabBase + (numTabs + 1) * tabSize;
139: }
140: return next;
141: }
142:
143:
150: protected int getTabSize()
151: {
152: Object tabSize = getDocument().getProperty(PlainDocument.tabSizeAttribute);
153: if (tabSize == null)
154: return 8;
155: return ((Integer)tabSize).intValue();
156: }
157:
158:
167: protected void drawLine(int p0, int p1, Graphics g, int x, int y)
168: {
169: try
170: {
171:
172:
173:
174:
175:
176:
177:
178:
179:
180: if ((selectionStart == selectionEnd) ||
181: (p0 > selectionEnd || p1 < selectionStart))
182: drawUnselectedText(g, x, y, p0, p1);
183:
184:
185: else if (p0 >= selectionStart && p1 <= selectionEnd)
186: drawSelectedText(g, x, y, p0, p1);
187:
188:
189: else if (p0 >= selectionStart)
190: {
191: x = drawSelectedText(g, x, y, p0, selectionEnd);
192: drawUnselectedText(g, x, y, selectionEnd, p1);
193: }
194:
195:
196: else if (selectionStart > p0 && selectionEnd > p1)
197: {
198: x = drawUnselectedText(g, x, y, p0, selectionStart);
199: drawSelectedText(g, x, y, selectionStart, p1);
200: }
201:
202:
203: else if (selectionStart > p0)
204: {
205: x = drawUnselectedText(g, x, y, p0, selectionStart);
206: x = drawSelectedText(g, x, y, selectionStart, selectionEnd);
207: drawUnselectedText(g, x, y, selectionEnd, p1);
208: }
209: }
210: catch (BadLocationException ble)
211: {
212:
213: }
214: }
215:
216:
228: protected int drawSelectedText(Graphics g, int x, int y, int p0, int p1)
229: throws BadLocationException
230: {
231: g.setColor(selectedColor);
232: Segment segment = getLineBuffer();
233: getDocument().getText(p0, p1 - p0, segment);
234: return Utilities.drawTabbedText(segment, x, y, g, this, p0);
235: }
236:
237:
247: protected int drawUnselectedText(Graphics g, int x, int y, int p0, int p1)
248: throws BadLocationException
249: {
250: JTextComponent textComponent = (JTextComponent) getContainer();
251: if (textComponent.isEnabled())
252: g.setColor(unselectedColor);
253: else
254: g.setColor(disabledColor);
255:
256: Segment segment = getLineBuffer();
257: getDocument().getText(p0, p1 - p0, segment);
258: return Utilities.drawTabbedText(segment, x, y, g, this, p0);
259: }
260:
261:
265: protected void loadChildren (ViewFactory f)
266: {
267: Element root = getElement();
268: int numChildren = root.getElementCount();
269: if (numChildren == 0)
270: return;
271:
272: View[] children = new View[numChildren];
273: for (int i = 0; i < numChildren; i++)
274: children[i] = new WrappedLine(root.getElement(i));
275: replace(0, 0, children);
276: }
277:
278:
288: protected int calculateBreakPosition(int p0, int p1)
289: {
290: Segment s = new Segment();
291: try
292: {
293: getDocument().getText(p0, p1 - p0, s);
294: }
295: catch (BadLocationException ex)
296: {
297: assert false : "Couldn't load text";
298: }
299: int width = getWidth();
300: int pos;
301: if (wordWrap)
302: pos = p0 + Utilities.getBreakLocation(s, metrics, tabBase,
303: tabBase + width, this, p0);
304: else
305: pos = p0 + Utilities.getTabbedTextOffset(s, metrics, tabBase,
306: tabBase + width, this, p0,
307: false);
308: return pos;
309: }
310:
311: void updateMetrics()
312: {
313: Container component = getContainer();
314: metrics = component.getFontMetrics(component.getFont());
315: tabSize = getTabSize()* metrics.charWidth('m');
316: }
317:
318:
322: public float getPreferredSpan (int axis)
323: {
324: updateMetrics();
325: return super.getPreferredSpan(axis);
326: }
327:
328:
332: public float getMinimumSpan (int axis)
333: {
334: updateMetrics();
335: return super.getMinimumSpan(axis);
336: }
337:
338:
342: public float getMaximumSpan (int axis)
343: {
344: updateMetrics();
345: return super.getMaximumSpan(axis);
346: }
347:
348:
352: public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f)
353: {
354:
355: updateChildren(e, a);
356:
357:
358: Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
359: : null;
360: View v = getViewAtPosition(e.getOffset(), r);
361: if (v != null)
362: v.insertUpdate(e, r, f);
363: }
364:
365:
369: public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f)
370: {
371:
372: updateChildren(e, a);
373:
374:
375: Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
376: : null;
377: View v = getViewAtPosition(e.getOffset(), r);
378: if (v != null)
379: v.removeUpdate(e, r, f);
380: }
381:
382:
387: public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f)
388: {
389:
390: updateChildren(e, a);
391: }
392:
393:
401: private void updateChildren(DocumentEvent ev, Shape a)
402: {
403: Element el = getElement();
404: DocumentEvent.ElementChange ec = ev.getChange(el);
405: if (ec != null)
406: {
407: Element[] removed = ec.getChildrenRemoved();
408: Element[] added = ec.getChildrenAdded();
409: View[] addedViews = new View[added.length];
410: for (int i = 0; i < added.length; i++)
411: addedViews[i] = new WrappedLine(added[i]);
412: replace(ec.getIndex(), removed.length, addedViews);
413: if (a != null)
414: {
415: preferenceChanged(null, true, true);
416: getContainer().repaint();
417: }
418: }
419: updateMetrics();
420: }
421:
422: class WrappedLineCreator implements ViewFactory
423: {
424:
425: public View create(Element elem)
426: {
427: return new WrappedLine(elem);
428: }
429: }
430:
431:
439: public void paint(Graphics g, Shape a)
440: {
441: Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
442: tabBase = r.x;
443:
444: JTextComponent comp = (JTextComponent)getContainer();
445:
446: updateMetrics();
447:
448: selectionStart = comp.getSelectionStart();
449: selectionEnd = comp.getSelectionEnd();
450:
451: selectedColor = comp.getSelectedTextColor();
452: unselectedColor = comp.getForeground();
453: disabledColor = comp.getDisabledTextColor();
454: selectedColor = comp.getSelectedTextColor();
455: lineHeight = metrics.getHeight();
456: g.setFont(comp.getFont());
457:
458: super.paint(g, a);
459: }
460:
461:
465: public void setSize (float width, float height)
466: {
467: updateMetrics();
468: if (width != getWidth())
469: preferenceChanged(null, true, true);
470: super.setSize(width, height);
471: }
472:
473: class WrappedLine extends View
474: {
475:
476: int numLines = 1;
477:
478: public WrappedLine(Element elem)
479: {
480: super(elem);
481: }
482:
483:
487: public void paint(Graphics g, Shape s)
488: {
489: Rectangle rect = s.getBounds();
490:
491: int end = getEndOffset();
492: int currStart = getStartOffset();
493: int currEnd;
494: int count = 0;
495:
496:
497: Container c = getContainer();
498: LayeredHighlighter lh = null;
499: JTextComponent tc = null;
500: if (c instanceof JTextComponent)
501: {
502: tc = (JTextComponent) c;
503: Highlighter h = tc.getHighlighter();
504: if (h instanceof LayeredHighlighter)
505: lh = (LayeredHighlighter) h;
506: }
507:
508: while (currStart < end)
509: {
510: currEnd = calculateBreakPosition(currStart, end);
511:
512:
513: if (lh != null)
514: {
515:
516: if (currEnd == end)
517: lh.paintLayeredHighlights(g, currStart, currEnd - 1, s, tc,
518: this);
519: else
520: lh.paintLayeredHighlights(g, currStart, currEnd, s, tc, this);
521:
522: }
523: drawLine(currStart, currEnd, g, rect.x, rect.y + metrics.getAscent());
524:
525: rect.y += lineHeight;
526: if (currEnd == currStart)
527: currStart ++;
528: else
529: currStart = currEnd;
530:
531: count++;
532:
533: }
534:
535: if (count != numLines)
536: {
537: numLines = count;
538: preferenceChanged(this, false, true);
539: }
540:
541: }
542:
543:
548: private int determineNumLines()
549: {
550: int nLines = 0;
551: int end = getEndOffset();
552: for (int i = getStartOffset(); i < end;)
553: {
554: nLines++;
555:
556:
557: int breakPoint = calculateBreakPosition(i, end);
558:
559: if (breakPoint == i)
560: i = breakPoint + 1;
561: else
562: i = breakPoint;
563: }
564: return nLines;
565: }
566:
567:
575: public float getPreferredSpan(int axis)
576: {
577: if (axis == X_AXIS)
578: return getWidth();
579: else if (axis == Y_AXIS)
580: {
581: if (metrics == null)
582: updateMetrics();
583: return numLines * metrics.getHeight();
584: }
585:
586: throw new IllegalArgumentException("Invalid axis for getPreferredSpan: "
587: + axis);
588: }
589:
590:
601: public Shape modelToView(int pos, Shape a, Bias b)
602: throws BadLocationException
603: {
604: Rectangle rect = a.getBounds();
605:
606:
607: if (rect.isEmpty())
608: throw new BadLocationException("Unable to calculate view coordinates "
609: + "when allocation area is empty.", pos);
610:
611: Segment s = getLineBuffer();
612: int lineHeight = metrics.getHeight();
613:
614:
615:
616: rect.height = lineHeight;
617: rect.width = 1;
618:
619: int currLineStart = getStartOffset();
620: int end = getEndOffset();
621:
622: if (pos < currLineStart || pos >= end)
623: throw new BadLocationException("invalid offset", pos);
624:
625: while (true)
626: {
627: int currLineEnd = calculateBreakPosition(currLineStart, end);
628:
629:
630:
631: if (pos >= currLineStart && pos < currLineEnd)
632: {
633: try
634: {
635: getDocument().getText(currLineStart, pos - currLineStart, s);
636: }
637: catch (BadLocationException ble)
638: {
639:
640: }
641: rect.x += Utilities.getTabbedTextWidth(s, metrics, rect.x,
642: WrappedPlainView.this,
643: currLineStart);
644: return rect;
645: }
646:
647: rect.y += lineHeight;
648:
649:
650:
651: if (currLineEnd == currLineStart)
652: currLineStart = end;
653: else
654: currLineStart = currLineEnd;
655: }
656:
657: }
658:
659:
670: public int viewToModel(float x, float y, Shape a, Bias[] b)
671: {
672: Segment s = getLineBuffer();
673: Rectangle rect = a.getBounds();
674: int currLineStart = getStartOffset();
675:
676:
677:
678:
679: int end = getEndOffset();
680:
681: int lineHeight = metrics.getHeight();
682: if (y < rect.y)
683: return currLineStart;
684:
685: if (y > rect.y + rect.height)
686: return end - 1;
687:
688:
689:
690:
691:
692: while (currLineStart != end)
693: {
694: int currLineEnd = calculateBreakPosition(currLineStart, end);
695:
696:
697:
698: if (y >= rect.y && y < rect.y + lineHeight)
699: {
700: try
701: {
702: getDocument().getText(currLineStart, currLineEnd - currLineStart, s);
703: }
704: catch (BadLocationException ble)
705: {
706:
707: }
708:
709: int offset = Utilities.getTabbedTextOffset(s, metrics, rect.x,
710: (int) x,
711: WrappedPlainView.this,
712: currLineStart);
713:
714:
715:
716:
717:
718: return (offset == currLineEnd) ? offset - 1 : offset;
719: }
720:
721: rect.y += lineHeight;
722:
723:
724:
725: currLineStart = currLineEnd;
726:
727: }
728:
729: return end;
730: }
731:
732:
745: void updateDamage (Rectangle a)
746: {
747: int nLines = determineNumLines();
748: if (numLines != nLines)
749: {
750: numLines = nLines;
751: preferenceChanged(this, false, true);
752: getContainer().repaint();
753: }
754: else if (a != null)
755: getContainer().repaint(a.x, a.y, a.width, a.height);
756: }
757:
758:
766: public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f)
767: {
768: Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
769: updateDamage(r);
770: }
771:
772:
780: public void removeUpdate (DocumentEvent changes, Shape a, ViewFactory f)
781: {
782:
783:
784:
785:
786:
787:
788:
789:
790:
791: Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
792: updateDamage(r);
793: }
794: }
795: }