1. /*
  2. * @(#)WrappedPlainView.java 1.38 04/05/26
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text;
  8. import java.util.Vector;
  9. import java.util.Properties;
  10. import java.awt.*;
  11. import javax.swing.event.*;
  12. /**
  13. * View of plain text (text with only one font and color)
  14. * that does line-wrapping. This view expects that its
  15. * associated element has child elements that represent
  16. * the lines it should be wrapping. It is implemented
  17. * as a vertical box that contains logical line views.
  18. * The logical line views are nested classes that render
  19. * the logical line as multiple physical line if the logical
  20. * line is too wide to fit within the allocation. The
  21. * line views draw upon the outer class for its state
  22. * to reduce their memory requirements.
  23. * <p>
  24. * The line views do all of their rendering through the
  25. * <code>drawLine</code> method which in turn does all of
  26. * its rendering through the <code>drawSelectedText</code>
  27. * and <code>drawUnselectedText</code> methods. This
  28. * enables subclasses to easily specialize the rendering
  29. * without concern for the layout aspects.
  30. *
  31. * @author Timothy Prinzing
  32. * @version 1.38 05/26/04
  33. * @see View
  34. */
  35. public class WrappedPlainView extends BoxView implements TabExpander {
  36. /**
  37. * Creates a new WrappedPlainView. Lines will be wrapped
  38. * on character boundaries.
  39. *
  40. * @param elem the element underlying the view
  41. */
  42. public WrappedPlainView(Element elem) {
  43. this(elem, false);
  44. }
  45. /**
  46. * Creates a new WrappedPlainView. Lines can be wrapped on
  47. * either character or word boundaries depending upon the
  48. * setting of the wordWrap parameter.
  49. *
  50. * @param elem the element underlying the view
  51. * @param wordWrap should lines be wrapped on word boundaries?
  52. */
  53. public WrappedPlainView(Element elem, boolean wordWrap) {
  54. super(elem, Y_AXIS);
  55. this.wordWrap = wordWrap;
  56. }
  57. /**
  58. * Returns the tab size set for the document, defaulting to 8.
  59. *
  60. * @return the tab size
  61. */
  62. protected int getTabSize() {
  63. Integer i = (Integer) getDocument().getProperty(PlainDocument.tabSizeAttribute);
  64. int size = (i != null) ? i.intValue() : 8;
  65. return size;
  66. }
  67. /**
  68. * Renders a line of text, suppressing whitespace at the end
  69. * and expanding any tabs. This is implemented to make calls
  70. * to the methods <code>drawUnselectedText</code> and
  71. * <code>drawSelectedText</code> so that the way selected and
  72. * unselected text are rendered can be customized.
  73. *
  74. * @param p0 the starting document location to use >= 0
  75. * @param p1 the ending document location to use >= p1
  76. * @param g the graphics context
  77. * @param x the starting X position >= 0
  78. * @param y the starting Y position >= 0
  79. * @see #drawUnselectedText
  80. * @see #drawSelectedText
  81. */
  82. protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
  83. Element lineMap = getElement();
  84. Element line = lineMap.getElement(lineMap.getElementIndex(p0));
  85. Element elem;
  86. try {
  87. if (line.isLeaf()) {
  88. drawText(line, p0, p1, g, x, y);
  89. } else {
  90. // this line contains the composed text.
  91. int idx = line.getElementIndex(p0);
  92. int lastIdx = line.getElementIndex(p1);
  93. for(; idx <= lastIdx; idx++) {
  94. elem = line.getElement(idx);
  95. int start = Math.max(elem.getStartOffset(), p0);
  96. int end = Math.min(elem.getEndOffset(), p1);
  97. x = drawText(elem, start, end, g, x, y);
  98. }
  99. }
  100. } catch (BadLocationException e) {
  101. throw new StateInvariantError("Can't render: " + p0 + "," + p1);
  102. }
  103. }
  104. private int drawText(Element elem, int p0, int p1, Graphics g, int x, int y) throws BadLocationException {
  105. p1 = Math.min(getDocument().getLength(), p1);
  106. AttributeSet attr = elem.getAttributes();
  107. if (Utilities.isComposedTextAttributeDefined(attr)) {
  108. g.setColor(unselected);
  109. x = Utilities.drawComposedText(this, attr, g, x, y,
  110. p0-elem.getStartOffset(),
  111. p1-elem.getStartOffset());
  112. } else {
  113. if (sel0 == sel1 || selected == unselected) {
  114. // no selection, or it is invisible
  115. x = drawUnselectedText(g, x, y, p0, p1);
  116. } else if ((p0 >= sel0 && p0 <= sel1) && (p1 >= sel0 && p1 <= sel1)) {
  117. x = drawSelectedText(g, x, y, p0, p1);
  118. } else if (sel0 >= p0 && sel0 <= p1) {
  119. if (sel1 >= p0 && sel1 <= p1) {
  120. x = drawUnselectedText(g, x, y, p0, sel0);
  121. x = drawSelectedText(g, x, y, sel0, sel1);
  122. x = drawUnselectedText(g, x, y, sel1, p1);
  123. } else {
  124. x = drawUnselectedText(g, x, y, p0, sel0);
  125. x = drawSelectedText(g, x, y, sel0, p1);
  126. }
  127. } else if (sel1 >= p0 && sel1 <= p1) {
  128. x = drawSelectedText(g, x, y, p0, sel1);
  129. x = drawUnselectedText(g, x, y, sel1, p1);
  130. } else {
  131. x = drawUnselectedText(g, x, y, p0, p1);
  132. }
  133. }
  134. return x;
  135. }
  136. /**
  137. * Renders the given range in the model as normal unselected
  138. * text.
  139. *
  140. * @param g the graphics context
  141. * @param x the starting X coordinate >= 0
  142. * @param y the starting Y coordinate >= 0
  143. * @param p0 the beginning position in the model >= 0
  144. * @param p1 the ending position in the model >= p0
  145. * @return the X location of the end of the range >= 0
  146. * @exception BadLocationException if the range is invalid
  147. */
  148. protected int drawUnselectedText(Graphics g, int x, int y,
  149. int p0, int p1) throws BadLocationException {
  150. g.setColor(unselected);
  151. Document doc = getDocument();
  152. Segment segment = SegmentCache.getSharedSegment();
  153. doc.getText(p0, p1 - p0, segment);
  154. int ret = Utilities.drawTabbedText(this, segment, x, y, g, this, p0);
  155. SegmentCache.releaseSharedSegment(segment);
  156. return ret;
  157. }
  158. /**
  159. * Renders the given range in the model as selected text. This
  160. * is implemented to render the text in the color specified in
  161. * the hosting component. It assumes the highlighter will render
  162. * the selected background.
  163. *
  164. * @param g the graphics context
  165. * @param x the starting X coordinate >= 0
  166. * @param y the starting Y coordinate >= 0
  167. * @param p0 the beginning position in the model >= 0
  168. * @param p1 the ending position in the model >= p0
  169. * @return the location of the end of the range.
  170. * @exception BadLocationException if the range is invalid
  171. */
  172. protected int drawSelectedText(Graphics g, int x,
  173. int y, int p0, int p1) throws BadLocationException {
  174. g.setColor(selected);
  175. Document doc = getDocument();
  176. Segment segment = SegmentCache.getSharedSegment();
  177. doc.getText(p0, p1 - p0, segment);
  178. int ret = Utilities.drawTabbedText(this, segment, x, y, g, this, p0);
  179. SegmentCache.releaseSharedSegment(segment);
  180. return ret;
  181. }
  182. /**
  183. * Gives access to a buffer that can be used to fetch
  184. * text from the associated document.
  185. *
  186. * @return the buffer
  187. */
  188. protected final Segment getLineBuffer() {
  189. if (lineBuffer == null) {
  190. lineBuffer = new Segment();
  191. }
  192. return lineBuffer;
  193. }
  194. /**
  195. * This is called by the nested wrapped line
  196. * views to determine the break location. This can
  197. * be reimplemented to alter the breaking behavior.
  198. * It will either break at word or character boundaries
  199. * depending upon the break argument given at
  200. * construction.
  201. */
  202. protected int calculateBreakPosition(int p0, int p1) {
  203. int p;
  204. Segment segment = SegmentCache.getSharedSegment();
  205. loadText(segment, p0, p1);
  206. int currentWidth = getWidth();
  207. if (wordWrap) {
  208. p = p0 + Utilities.getBreakLocation(segment, metrics,
  209. tabBase, tabBase + currentWidth,
  210. this, p0);
  211. } else {
  212. p = p0 + Utilities.getTabbedTextOffset(segment, metrics,
  213. tabBase, tabBase + currentWidth,
  214. this, p0, false);
  215. }
  216. SegmentCache.releaseSharedSegment(segment);
  217. return p;
  218. }
  219. /**
  220. * Loads all of the children to initialize the view.
  221. * This is called by the <code>setParent</code> method.
  222. * Subclasses can reimplement this to initialize their
  223. * child views in a different manner. The default
  224. * implementation creates a child view for each
  225. * child element.
  226. *
  227. * @param f the view factory
  228. */
  229. protected void loadChildren(ViewFactory f) {
  230. Element e = getElement();
  231. int n = e.getElementCount();
  232. if (n > 0) {
  233. View[] added = new View[n];
  234. for (int i = 0; i < n; i++) {
  235. added[i] = new WrappedLine(e.getElement(i));
  236. }
  237. replace(0, 0, added);
  238. }
  239. }
  240. /**
  241. * Update the child views in response to a
  242. * document event.
  243. */
  244. void updateChildren(DocumentEvent e, Shape a) {
  245. Element elem = getElement();
  246. DocumentEvent.ElementChange ec = e.getChange(elem);
  247. if (ec != null) {
  248. // the structure of this element changed.
  249. Element[] removedElems = ec.getChildrenRemoved();
  250. Element[] addedElems = ec.getChildrenAdded();
  251. View[] added = new View[addedElems.length];
  252. for (int i = 0; i < addedElems.length; i++) {
  253. added[i] = new WrappedLine(addedElems[i]);
  254. }
  255. replace(ec.getIndex(), removedElems.length, added);
  256. // should damge a little more intelligently.
  257. if (a != null) {
  258. preferenceChanged(null, true, true);
  259. getContainer().repaint();
  260. }
  261. }
  262. // update font metrics which may be used by the child views
  263. updateMetrics();
  264. }
  265. /**
  266. * Load the text buffer with the given range
  267. * of text. This is used by the fragments
  268. * broken off of this view as well as this
  269. * view itself.
  270. */
  271. final void loadText(Segment segment, int p0, int p1) {
  272. try {
  273. Document doc = getDocument();
  274. doc.getText(p0, p1 - p0, segment);
  275. } catch (BadLocationException bl) {
  276. throw new StateInvariantError("Can't get line text");
  277. }
  278. }
  279. final void updateMetrics() {
  280. Component host = getContainer();
  281. Font f = host.getFont();
  282. metrics = host.getFontMetrics(f);
  283. tabSize = getTabSize() * metrics.charWidth('m');
  284. }
  285. // --- TabExpander methods ------------------------------------------
  286. /**
  287. * Returns the next tab stop position after a given reference position.
  288. * This implementation does not support things like centering so it
  289. * ignores the tabOffset argument.
  290. *
  291. * @param x the current position >= 0
  292. * @param tabOffset the position within the text stream
  293. * that the tab occurred at >= 0.
  294. * @return the tab stop, measured in points >= 0
  295. */
  296. public float nextTabStop(float x, int tabOffset) {
  297. if (tabSize == 0)
  298. return x;
  299. int ntabs = ((int) x - tabBase) / tabSize;
  300. return tabBase + ((ntabs + 1) * tabSize);
  301. }
  302. // --- View methods -------------------------------------
  303. /**
  304. * Renders using the given rendering surface and area
  305. * on that surface. This is implemented to stash the
  306. * selection positions, selection colors, and font
  307. * metrics for the nested lines to use.
  308. *
  309. * @param g the rendering surface to use
  310. * @param a the allocated region to render into
  311. *
  312. * @see View#paint
  313. */
  314. public void paint(Graphics g, Shape a) {
  315. Rectangle alloc = (Rectangle) a;
  316. tabBase = alloc.x;
  317. JTextComponent host = (JTextComponent) getContainer();
  318. sel0 = host.getSelectionStart();
  319. sel1 = host.getSelectionEnd();
  320. unselected = (host.isEnabled()) ?
  321. host.getForeground() : host.getDisabledTextColor();
  322. Caret c = host.getCaret();
  323. selected = c.isSelectionVisible() && host.getHighlighter() != null ?
  324. host.getSelectedTextColor() : unselected;
  325. g.setFont(host.getFont());
  326. // superclass paints the children
  327. super.paint(g, a);
  328. }
  329. /**
  330. * Sets the size of the view. This should cause
  331. * layout of the view along the given axis, if it
  332. * has any layout duties.
  333. *
  334. * @param width the width >= 0
  335. * @param height the height >= 0
  336. */
  337. public void setSize(float width, float height) {
  338. updateMetrics();
  339. if ((int) width != getWidth()) {
  340. // invalidate the view itself since the childrens
  341. // desired widths will be based upon this views width.
  342. preferenceChanged(null, true, true);
  343. widthChanging = true;
  344. }
  345. super.setSize(width, height);
  346. widthChanging = false;
  347. }
  348. /**
  349. * Determines the preferred span for this view along an
  350. * axis. This is implemented to provide the superclass
  351. * behavior after first making sure that the current font
  352. * metrics are cached (for the nested lines which use
  353. * the metrics to determine the height of the potentially
  354. * wrapped lines).
  355. *
  356. * @param axis may be either View.X_AXIS or View.Y_AXIS
  357. * @return the span the view would like to be rendered into.
  358. * Typically the view is told to render into the span
  359. * that is returned, although there is no guarantee.
  360. * The parent may choose to resize or break the view.
  361. * @see View#getPreferredSpan
  362. */
  363. public float getPreferredSpan(int axis) {
  364. updateMetrics();
  365. return super.getPreferredSpan(axis);
  366. }
  367. /**
  368. * Determines the minimum span for this view along an
  369. * axis. This is implemented to provide the superclass
  370. * behavior after first making sure that the current font
  371. * metrics are cached (for the nested lines which use
  372. * the metrics to determine the height of the potentially
  373. * wrapped lines).
  374. *
  375. * @param axis may be either View.X_AXIS or View.Y_AXIS
  376. * @return the span the view would like to be rendered into.
  377. * Typically the view is told to render into the span
  378. * that is returned, although there is no guarantee.
  379. * The parent may choose to resize or break the view.
  380. * @see View#getMinimumSpan
  381. */
  382. public float getMinimumSpan(int axis) {
  383. updateMetrics();
  384. return super.getMinimumSpan(axis);
  385. }
  386. /**
  387. * Determines the maximum span for this view along an
  388. * axis. This is implemented to provide the superclass
  389. * behavior after first making sure that the current font
  390. * metrics are cached (for the nested lines which use
  391. * the metrics to determine the height of the potentially
  392. * wrapped lines).
  393. *
  394. * @param axis may be either View.X_AXIS or View.Y_AXIS
  395. * @return the span the view would like to be rendered into.
  396. * Typically the view is told to render into the span
  397. * that is returned, although there is no guarantee.
  398. * The parent may choose to resize or break the view.
  399. * @see View#getMaximumSpan
  400. */
  401. public float getMaximumSpan(int axis) {
  402. updateMetrics();
  403. return super.getMaximumSpan(axis);
  404. }
  405. /**
  406. * Gives notification that something was inserted into the
  407. * document in a location that this view is responsible for.
  408. * This is implemented to simply update the children.
  409. *
  410. * @param e the change information from the associated document
  411. * @param a the current allocation of the view
  412. * @param f the factory to use to rebuild if the view has children
  413. * @see View#insertUpdate
  414. */
  415. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  416. updateChildren(e, a);
  417. Rectangle alloc = ((a != null) && isAllocationValid()) ?
  418. getInsideAllocation(a) : null;
  419. int pos = e.getOffset();
  420. View v = getViewAtPosition(pos, alloc);
  421. if (v != null) {
  422. v.insertUpdate(e, alloc, f);
  423. }
  424. }
  425. /**
  426. * Gives notification that something was removed from the
  427. * document in a location that this view is responsible for.
  428. * This is implemented to simply update the children.
  429. *
  430. * @param e the change information from the associated document
  431. * @param a the current allocation of the view
  432. * @param f the factory to use to rebuild if the view has children
  433. * @see View#removeUpdate
  434. */
  435. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  436. updateChildren(e, a);
  437. Rectangle alloc = ((a != null) && isAllocationValid()) ?
  438. getInsideAllocation(a) : null;
  439. int pos = e.getOffset();
  440. View v = getViewAtPosition(pos, alloc);
  441. if (v != null) {
  442. v.removeUpdate(e, alloc, f);
  443. }
  444. }
  445. /**
  446. * Gives notification from the document that attributes were changed
  447. * in a location that this view is responsible for.
  448. *
  449. * @param e the change information from the associated document
  450. * @param a the current allocation of the view
  451. * @param f the factory to use to rebuild if the view has children
  452. * @see View#changedUpdate
  453. */
  454. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  455. updateChildren(e, a);
  456. }
  457. // --- variables -------------------------------------------
  458. FontMetrics metrics;
  459. Segment lineBuffer;
  460. boolean widthChanging;
  461. int tabBase;
  462. int tabSize;
  463. boolean wordWrap;
  464. int sel0;
  465. int sel1;
  466. Color unselected;
  467. Color selected;
  468. /**
  469. * Simple view of a line that wraps if it doesn't
  470. * fit withing the horizontal space allocated.
  471. * This class tries to be lightweight by carrying little
  472. * state of it's own and sharing the state of the outer class
  473. * with it's sibblings.
  474. */
  475. class WrappedLine extends View {
  476. WrappedLine(Element elem) {
  477. super(elem);
  478. }
  479. /**
  480. * Calculate the number of lines that will be rendered
  481. * by logical line when it is wrapped.
  482. */
  483. final int calculateLineCount() {
  484. int nlines = 0;
  485. int p1 = getEndOffset();
  486. for (int p0 = getStartOffset(); p0 < p1; ) {
  487. nlines += 1;
  488. int p = calculateBreakPosition(p0, p1);
  489. p0 = (p == p0) ? ++p : p; // this is the fix of #4410243
  490. // we check on situation when
  491. // width is too small and
  492. // break position is calculated
  493. // incorrect
  494. }
  495. return nlines;
  496. }
  497. /**
  498. * Determines the preferred span for this view along an
  499. * axis.
  500. *
  501. * @param axis may be either X_AXIS or Y_AXIS
  502. * @return the span the view would like to be rendered into.
  503. * Typically the view is told to render into the span
  504. * that is returned, although there is no guarantee.
  505. * The parent may choose to resize or break the view.
  506. * @see View#getPreferredSpan
  507. */
  508. public float getPreferredSpan(int axis) {
  509. switch (axis) {
  510. case View.X_AXIS:
  511. float width = getWidth();
  512. if (width == Integer.MAX_VALUE) {
  513. // We have been initially set to MAX_VALUE, but we don't
  514. // want this as our preferred.
  515. return 100f;
  516. }
  517. return width;
  518. case View.Y_AXIS:
  519. if (nlines == 0 || widthChanging) {
  520. nlines = calculateLineCount();
  521. }
  522. int h = nlines * metrics.getHeight();
  523. return h;
  524. default:
  525. throw new IllegalArgumentException("Invalid axis: " + axis);
  526. }
  527. }
  528. /**
  529. * Renders using the given rendering surface and area on that
  530. * surface. The view may need to do layout and create child
  531. * views to enable itself to render into the given allocation.
  532. *
  533. * @param g the rendering surface to use
  534. * @param a the allocated region to render into
  535. * @see View#paint
  536. */
  537. public void paint(Graphics g, Shape a) {
  538. Rectangle alloc = (Rectangle) a;
  539. int y = alloc.y + metrics.getAscent();
  540. int x = alloc.x;
  541. JTextComponent host = (JTextComponent)getContainer();
  542. Highlighter h = host.getHighlighter();
  543. LayeredHighlighter dh = (h instanceof LayeredHighlighter) ?
  544. (LayeredHighlighter)h : null;
  545. int p1 = getEndOffset();
  546. for (int p0 = getStartOffset(); p0 < p1; ) {
  547. int p = calculateBreakPosition(p0, p1);
  548. if (dh != null) {
  549. if (p == p1) {
  550. dh.paintLayeredHighlights(g, p0, p - 1, a, host, this);
  551. }
  552. else {
  553. dh.paintLayeredHighlights(g, p0, p, a, host, this);
  554. }
  555. }
  556. drawLine(p0, p, g, x, y);
  557. p0 = (p == p0) ? p1 : p;
  558. y += metrics.getHeight();
  559. }
  560. }
  561. /**
  562. * Provides a mapping from the document model coordinate space
  563. * to the coordinate space of the view mapped to it.
  564. *
  565. * @param pos the position to convert
  566. * @param a the allocated region to render into
  567. * @return the bounding box of the given position is returned
  568. * @exception BadLocationException if the given position does not represent a
  569. * valid location in the associated document
  570. * @see View#modelToView
  571. */
  572. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  573. Rectangle alloc = a.getBounds();
  574. alloc.height = metrics.getHeight();
  575. alloc.width = 1;
  576. int p1 = getEndOffset();
  577. int p0 = getStartOffset();
  578. int testP = (b == Position.Bias.Forward) ? pos :
  579. Math.max(p0, pos - 1);
  580. while (p0 < p1) {
  581. int p = calculateBreakPosition(p0, p1);
  582. if ((pos >= p0) && (testP < p)) {
  583. // it's in this line
  584. Segment segment = SegmentCache.getSharedSegment();
  585. loadText(segment, p0, pos);
  586. alloc.x += Utilities.getTabbedTextWidth(segment, metrics,
  587. alloc.x,
  588. WrappedPlainView.this, p0);
  589. SegmentCache.releaseSharedSegment(segment);
  590. return alloc;
  591. }
  592. if (p == p1 && pos == p1) {
  593. // Wants end.
  594. if (pos > p0) {
  595. Segment segment = SegmentCache.getSharedSegment();
  596. loadText(segment, p0, pos);
  597. alloc.x += Utilities.getTabbedTextWidth(segment,
  598. metrics, alloc.x,
  599. WrappedPlainView.this, p0);
  600. SegmentCache.releaseSharedSegment(segment);
  601. }
  602. return alloc;
  603. }
  604. p0 = (p == p0) ? p1 : p;
  605. alloc.y += alloc.height;
  606. }
  607. throw new BadLocationException(null, pos);
  608. }
  609. /**
  610. * Provides a mapping from the view coordinate space to the logical
  611. * coordinate space of the model.
  612. *
  613. * @param x the X coordinate
  614. * @param y the Y coordinate
  615. * @param a the allocated region to render into
  616. * @return the location within the model that best represents the
  617. * given point in the view
  618. * @see View#viewToModel
  619. */
  620. public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
  621. // PENDING(prinz) implement bias properly
  622. bias[0] = Position.Bias.Forward;
  623. Rectangle alloc = (Rectangle) a;
  624. Document doc = getDocument();
  625. int x = (int) fx;
  626. int y = (int) fy;
  627. if (y < alloc.y) {
  628. // above the area covered by this icon, so the the position
  629. // is assumed to be the start of the coverage for this view.
  630. return getStartOffset();
  631. } else if (y > alloc.y + alloc.height) {
  632. // below the area covered by this icon, so the the position
  633. // is assumed to be the end of the coverage for this view.
  634. return getEndOffset() - 1;
  635. } else {
  636. // positioned within the coverage of this view vertically,
  637. // so we figure out which line the point corresponds to.
  638. // if the line is greater than the number of lines contained, then
  639. // simply use the last line as it represents the last possible place
  640. // we can position to.
  641. alloc.height = metrics.getHeight();
  642. int p1 = getEndOffset();
  643. for (int p0 = getStartOffset(); p0 < p1; ) {
  644. int p = calculateBreakPosition(p0, p1);
  645. if ((y >= alloc.y) && (y < (alloc.y + alloc.height))) {
  646. // it's in this line
  647. if (x < alloc.x) {
  648. // point is to the left of the line
  649. return p0;
  650. } else if (x > alloc.x + alloc.width) {
  651. // point is to the right of the line
  652. return p - 1;
  653. } else {
  654. // Determine the offset into the text
  655. Segment segment = SegmentCache.getSharedSegment();
  656. loadText(segment, p0, p1);
  657. int n = Utilities.getTabbedTextOffset(segment, metrics,
  658. alloc.x, x,
  659. WrappedPlainView.this, p0);
  660. SegmentCache.releaseSharedSegment(segment);
  661. return Math.min(p0 + n, p1 - 1);
  662. }
  663. }
  664. p0 = (p == p0) ? p1 : p;
  665. alloc.y += alloc.height;
  666. }
  667. return getEndOffset() - 1;
  668. }
  669. }
  670. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  671. int n = calculateLineCount();
  672. if (this.nlines != n) {
  673. this.nlines = n;
  674. WrappedPlainView.this.preferenceChanged(this, false, true);
  675. // have to repaint any views after the receiver.
  676. getContainer().repaint();
  677. }
  678. else if (a != null) {
  679. Component c = getContainer();
  680. Rectangle alloc = (Rectangle) a;
  681. c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  682. }
  683. }
  684. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  685. int n = calculateLineCount();
  686. if (this.nlines != n) {
  687. // have to repaint any views after the receiver.
  688. this.nlines = n;
  689. WrappedPlainView.this.preferenceChanged(this, false, true);
  690. getContainer().repaint();
  691. }
  692. else if (a != null) {
  693. Component c = getContainer();
  694. Rectangle alloc = (Rectangle) a;
  695. c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  696. }
  697. }
  698. // --- variables ---------------------------------------
  699. int nlines;
  700. }
  701. }