1. /*
  2. * @(#)WrappedPlainView.java 1.32 03/03/06
  3. *
  4. * Copyright 2003 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.32 03/06/03
  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(attr, g, x, y,
  110. p0-elem.getStartOffset(),
  111. p1-elem.getStartOffset());
  112. } else {
  113. if (sel0 == sel1) {
  114. // no selection
  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(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(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. if (wordWrap) {
  207. p = p0 + Utilities.getBreakLocation(segment, metrics,
  208. tabBase, tabBase + getWidth(),
  209. this, p0);
  210. } else {
  211. p = p0 + Utilities.getTabbedTextOffset(segment, metrics,
  212. tabBase, tabBase + getWidth(),
  213. this, p0);
  214. }
  215. SegmentCache.releaseSharedSegment(segment);
  216. return p;
  217. }
  218. /**
  219. * Loads all of the children to initialize the view.
  220. * This is called by the <code>setParent</code> method.
  221. * Subclasses can reimplement this to initialize their
  222. * child views in a different manner. The default
  223. * implementation creates a child view for each
  224. * child element.
  225. *
  226. * @param f the view factory
  227. */
  228. protected void loadChildren(ViewFactory f) {
  229. Element e = getElement();
  230. int n = e.getElementCount();
  231. if (n > 0) {
  232. View[] added = new View[n];
  233. for (int i = 0; i < n; i++) {
  234. added[i] = new WrappedLine(e.getElement(i));
  235. }
  236. replace(0, 0, added);
  237. }
  238. }
  239. /**
  240. * Update the child views in response to a
  241. * document event.
  242. */
  243. void updateChildren(DocumentEvent e, Shape a) {
  244. Element elem = getElement();
  245. DocumentEvent.ElementChange ec = e.getChange(elem);
  246. if (ec != null) {
  247. // the structure of this element changed.
  248. Element[] removedElems = ec.getChildrenRemoved();
  249. Element[] addedElems = ec.getChildrenAdded();
  250. View[] added = new View[addedElems.length];
  251. for (int i = 0; i < addedElems.length; i++) {
  252. added[i] = new WrappedLine(addedElems[i]);
  253. }
  254. replace(ec.getIndex(), removedElems.length, added);
  255. // should damge a little more intelligently.
  256. if (a != null) {
  257. preferenceChanged(null, true, true);
  258. getContainer().repaint();
  259. }
  260. }
  261. // update font metrics which may be used by the child views
  262. updateMetrics();
  263. }
  264. /**
  265. * Load the text buffer with the given range
  266. * of text. This is used by the fragments
  267. * broken off of this view as well as this
  268. * view itself.
  269. */
  270. final void loadText(Segment segment, int p0, int p1) {
  271. try {
  272. Document doc = getDocument();
  273. doc.getText(p0, p1 - p0, segment);
  274. } catch (BadLocationException bl) {
  275. throw new StateInvariantError("Can't get line text");
  276. }
  277. }
  278. final void updateMetrics() {
  279. Component host = getContainer();
  280. Font f = host.getFont();
  281. metrics = host.getFontMetrics(f);
  282. tabSize = getTabSize() * metrics.charWidth('m');
  283. }
  284. // --- TabExpander methods ------------------------------------------
  285. /**
  286. * Returns the next tab stop position after a given reference position.
  287. * This implementation does not support things like centering so it
  288. * ignores the tabOffset argument.
  289. *
  290. * @param x the current position >= 0
  291. * @param tabOffset the position within the text stream
  292. * that the tab occurred at >= 0.
  293. * @return the tab stop, measured in points >= 0
  294. */
  295. public float nextTabStop(float x, int tabOffset) {
  296. if (tabSize == 0)
  297. return x;
  298. int ntabs = ((int) x - tabBase) / tabSize;
  299. return tabBase + ((ntabs + 1) * tabSize);
  300. }
  301. // --- View methods -------------------------------------
  302. /**
  303. * Renders using the given rendering surface and area
  304. * on that surface. This is implemented to stash the
  305. * selection positions, selection colors, and font
  306. * metrics for the nested lines to use.
  307. *
  308. * @param g the rendering surface to use
  309. * @param a the allocated region to render into
  310. *
  311. * @see View#paint
  312. */
  313. public void paint(Graphics g, Shape a) {
  314. Rectangle alloc = (Rectangle) a;
  315. tabBase = alloc.x;
  316. JTextComponent host = (JTextComponent) getContainer();
  317. sel0 = host.getSelectionStart();
  318. sel1 = host.getSelectionEnd();
  319. unselected = (host.isEnabled()) ?
  320. host.getForeground() : host.getDisabledTextColor();
  321. Caret c = host.getCaret();
  322. selected = c.isSelectionVisible() ? host.getSelectedTextColor() : unselected;
  323. g.setFont(host.getFont());
  324. // superclass paints the children
  325. super.paint(g, a);
  326. }
  327. /**
  328. * Sets the size of the view. This should cause
  329. * layout of the view along the given axis, if it
  330. * has any layout duties.
  331. *
  332. * @param width the width >= 0
  333. * @param height the height >= 0
  334. */
  335. public void setSize(float width, float height) {
  336. updateMetrics();
  337. if ((int) width != getWidth()) {
  338. // invalidate the view itself since the childrens
  339. // desired widths will be based upon this views width.
  340. preferenceChanged(null, true, true);
  341. widthChanging = true;
  342. }
  343. super.setSize(width, height);
  344. widthChanging = false;
  345. }
  346. /**
  347. * Determines the preferred span for this view along an
  348. * axis. This is implemented to provide the superclass
  349. * behavior after first making sure that the current font
  350. * metrics are cached (for the nested lines which use
  351. * the metrics to determine the height of the potentially
  352. * wrapped lines).
  353. *
  354. * @param axis may be either View.X_AXIS or View.Y_AXIS
  355. * @return the span the view would like to be rendered into.
  356. * Typically the view is told to render into the span
  357. * that is returned, although there is no guarantee.
  358. * The parent may choose to resize or break the view.
  359. * @see View#getPreferredSpan
  360. */
  361. public float getPreferredSpan(int axis) {
  362. updateMetrics();
  363. return super.getPreferredSpan(axis);
  364. }
  365. /**
  366. * Determines the minimum span for this view along an
  367. * axis. This is implemented to provide the superclass
  368. * behavior after first making sure that the current font
  369. * metrics are cached (for the nested lines which use
  370. * the metrics to determine the height of the potentially
  371. * wrapped lines).
  372. *
  373. * @param axis may be either View.X_AXIS or View.Y_AXIS
  374. * @return the span the view would like to be rendered into.
  375. * Typically the view is told to render into the span
  376. * that is returned, although there is no guarantee.
  377. * The parent may choose to resize or break the view.
  378. * @see View#getMinimumSpan
  379. */
  380. public float getMinimumSpan(int axis) {
  381. updateMetrics();
  382. return super.getMinimumSpan(axis);
  383. }
  384. /**
  385. * Determines the maximum span for this view along an
  386. * axis. This is implemented to provide the superclass
  387. * behavior after first making sure that the current font
  388. * metrics are cached (for the nested lines which use
  389. * the metrics to determine the height of the potentially
  390. * wrapped lines).
  391. *
  392. * @param axis may be either View.X_AXIS or View.Y_AXIS
  393. * @return the span the view would like to be rendered into.
  394. * Typically the view is told to render into the span
  395. * that is returned, although there is no guarantee.
  396. * The parent may choose to resize or break the view.
  397. * @see View#getMaximumSpan
  398. */
  399. public float getMaximumSpan(int axis) {
  400. updateMetrics();
  401. return super.getMaximumSpan(axis);
  402. }
  403. /**
  404. * Gives notification that something was inserted into the
  405. * document in a location that this view is responsible for.
  406. * This is implemented to simply update the children.
  407. *
  408. * @param e the change information from the associated document
  409. * @param a the current allocation of the view
  410. * @param f the factory to use to rebuild if the view has children
  411. * @see View#insertUpdate
  412. */
  413. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  414. updateChildren(e, a);
  415. Rectangle alloc = ((a != null) && isAllocationValid()) ?
  416. getInsideAllocation(a) : null;
  417. int pos = e.getOffset();
  418. View v = getViewAtPosition(pos, alloc);
  419. if (v != null) {
  420. v.insertUpdate(e, alloc, f);
  421. }
  422. }
  423. /**
  424. * Gives notification that something was removed from the
  425. * document in a location that this view is responsible for.
  426. * This is implemented to simply update the children.
  427. *
  428. * @param e the change information from the associated document
  429. * @param a the current allocation of the view
  430. * @param f the factory to use to rebuild if the view has children
  431. * @see View#removeUpdate
  432. */
  433. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  434. updateChildren(e, a);
  435. Rectangle alloc = ((a != null) && isAllocationValid()) ?
  436. getInsideAllocation(a) : null;
  437. int pos = e.getOffset();
  438. View v = getViewAtPosition(pos, alloc);
  439. if (v != null) {
  440. v.removeUpdate(e, alloc, f);
  441. }
  442. }
  443. /**
  444. * Gives notification from the document that attributes were changed
  445. * in a location that this view is responsible for.
  446. *
  447. * @param e the change information from the associated document
  448. * @param a the current allocation of the view
  449. * @param f the factory to use to rebuild if the view has children
  450. * @see View#changedUpdate
  451. */
  452. public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  453. updateChildren(e, a);
  454. }
  455. // --- variables -------------------------------------------
  456. FontMetrics metrics;
  457. Segment lineBuffer;
  458. boolean widthChanging;
  459. int tabBase;
  460. int tabSize;
  461. boolean wordWrap;
  462. int sel0;
  463. int sel1;
  464. Color unselected;
  465. Color selected;
  466. /**
  467. * Simple view of a line that wraps if it doesn't
  468. * fit withing the horizontal space allocated.
  469. * This class tries to be lightweight by carrying little
  470. * state of it's own and sharing the state of the outer class
  471. * with it's sibblings.
  472. */
  473. class WrappedLine extends View {
  474. WrappedLine(Element elem) {
  475. super(elem);
  476. }
  477. /**
  478. * Calculate the number of lines that will be rendered
  479. * by logical line when it is wrapped.
  480. */
  481. final int calculateLineCount() {
  482. int nlines = 0;
  483. int p1 = getEndOffset();
  484. for (int p0 = getStartOffset(); p0 < p1; ) {
  485. nlines += 1;
  486. int p = calculateBreakPosition(p0, p1);
  487. p0 = (p == p0) ? p1 : p;
  488. }
  489. return nlines;
  490. }
  491. /**
  492. * Determines the preferred span for this view along an
  493. * axis.
  494. *
  495. * @param axis may be either X_AXIS or Y_AXIS
  496. * @return the span the view would like to be rendered into.
  497. * Typically the view is told to render into the span
  498. * that is returned, although there is no guarantee.
  499. * The parent may choose to resize or break the view.
  500. * @see View#getPreferredSpan
  501. */
  502. public float getPreferredSpan(int axis) {
  503. switch (axis) {
  504. case View.X_AXIS:
  505. float width = getWidth();
  506. if (width == Integer.MAX_VALUE) {
  507. // We have been initially set to MAX_VALUE, but we don't
  508. // want this as our preferred.
  509. return 100f;
  510. }
  511. return width;
  512. case View.Y_AXIS:
  513. if (nlines == 0 || widthChanging) {
  514. nlines = calculateLineCount();
  515. }
  516. int h = nlines * metrics.getHeight();
  517. return h;
  518. default:
  519. throw new IllegalArgumentException("Invalid axis: " + axis);
  520. }
  521. }
  522. /**
  523. * Renders using the given rendering surface and area on that
  524. * surface. The view may need to do layout and create child
  525. * views to enable itself to render into the given allocation.
  526. *
  527. * @param g the rendering surface to use
  528. * @param a the allocated region to render into
  529. * @see View#paint
  530. */
  531. public void paint(Graphics g, Shape a) {
  532. Rectangle alloc = (Rectangle) a;
  533. int y = alloc.y + metrics.getAscent();
  534. int x = alloc.x;
  535. JTextComponent host = (JTextComponent)getContainer();
  536. Highlighter h = host.getHighlighter();
  537. LayeredHighlighter dh = (h instanceof LayeredHighlighter) ?
  538. (LayeredHighlighter)h : null;
  539. int p1 = getEndOffset();
  540. for (int p0 = getStartOffset(); p0 < p1; ) {
  541. int p = calculateBreakPosition(p0, p1);
  542. if (dh != null) {
  543. if (p == p1) {
  544. dh.paintLayeredHighlights(g, p0, p - 1, a, host, this);
  545. }
  546. else {
  547. dh.paintLayeredHighlights(g, p0, p, a, host, this);
  548. }
  549. }
  550. drawLine(p0, p, g, x, y);
  551. p0 = (p == p0) ? p1 : p;
  552. y += metrics.getHeight();
  553. }
  554. }
  555. /**
  556. * Provides a mapping from the document model coordinate space
  557. * to the coordinate space of the view mapped to it.
  558. *
  559. * @param pos the position to convert
  560. * @param a the allocated region to render into
  561. * @return the bounding box of the given position is returned
  562. * @exception BadLocationException if the given position does not represent a
  563. * valid location in the associated document
  564. * @see View#modelToView
  565. */
  566. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  567. Rectangle alloc = a.getBounds();
  568. alloc.height = metrics.getHeight();
  569. alloc.width = 1;
  570. int p1 = getEndOffset();
  571. int p0 = getStartOffset();
  572. int testP = (b == Position.Bias.Forward) ? pos :
  573. Math.max(p0, pos - 1);
  574. while (p0 < p1) {
  575. int p = calculateBreakPosition(p0, p1);
  576. if ((pos >= p0) && (testP < p)) {
  577. // it's in this line
  578. Segment segment = SegmentCache.getSharedSegment();
  579. loadText(segment, p0, pos);
  580. alloc.x += Utilities.getTabbedTextWidth(segment, metrics,
  581. alloc.x,
  582. WrappedPlainView.this, p0);
  583. SegmentCache.releaseSharedSegment(segment);
  584. return alloc;
  585. }
  586. if (p == p1 && pos == p1) {
  587. // Wants end.
  588. if (pos > p0) {
  589. Segment segment = SegmentCache.getSharedSegment();
  590. loadText(segment, p0, pos);
  591. alloc.x += Utilities.getTabbedTextWidth(segment,
  592. metrics, alloc.x,
  593. WrappedPlainView.this, p0);
  594. SegmentCache.releaseSharedSegment(segment);
  595. }
  596. return alloc;
  597. }
  598. p0 = (p == p0) ? p1 : p;
  599. alloc.y += alloc.height;
  600. }
  601. throw new BadLocationException(null, pos);
  602. }
  603. /**
  604. * Provides a mapping from the view coordinate space to the logical
  605. * coordinate space of the model.
  606. *
  607. * @param x the X coordinate
  608. * @param y the Y coordinate
  609. * @param a the allocated region to render into
  610. * @return the location within the model that best represents the
  611. * given point in the view
  612. * @see View#viewToModel
  613. */
  614. public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
  615. // PENDING(prinz) implement bias properly
  616. bias[0] = Position.Bias.Forward;
  617. Rectangle alloc = (Rectangle) a;
  618. Document doc = getDocument();
  619. int x = (int) fx;
  620. int y = (int) fy;
  621. if (y < alloc.y) {
  622. // above the area covered by this icon, so the the position
  623. // is assumed to be the start of the coverage for this view.
  624. return getStartOffset();
  625. } else if (y > alloc.y + alloc.height) {
  626. // below the area covered by this icon, so the the position
  627. // is assumed to be the end of the coverage for this view.
  628. return getEndOffset() - 1;
  629. } else {
  630. // positioned within the coverage of this view vertically,
  631. // so we figure out which line the point corresponds to.
  632. // if the line is greater than the number of lines contained, then
  633. // simply use the last line as it represents the last possible place
  634. // we can position to.
  635. alloc.height = metrics.getHeight();
  636. int p1 = getEndOffset();
  637. for (int p0 = getStartOffset(); p0 < p1; ) {
  638. int p = calculateBreakPosition(p0, p1);
  639. if ((y >= alloc.y) && (y < (alloc.y + alloc.height))) {
  640. // it's in this line
  641. if (x < alloc.x) {
  642. // point is to the left of the line
  643. return p0;
  644. } else if (x > alloc.x + alloc.width) {
  645. // point is to the right of the line
  646. return p;
  647. } else {
  648. // Determine the offset into the text
  649. Segment segment = SegmentCache.getSharedSegment();
  650. loadText(segment, p0, p1);
  651. int n = Utilities.getTabbedTextOffset(segment, metrics,
  652. alloc.x, x,
  653. WrappedPlainView.this, p0);
  654. SegmentCache.releaseSharedSegment(segment);
  655. return Math.min(p0 + n, p1 - 1);
  656. }
  657. }
  658. p0 = (p == p0) ? p1 : p;
  659. alloc.y += alloc.height;
  660. }
  661. return getEndOffset() - 1;
  662. }
  663. }
  664. public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  665. int n = calculateLineCount();
  666. if (this.nlines != n) {
  667. this.nlines = n;
  668. WrappedPlainView.this.preferenceChanged(this, false, true);
  669. // have to repaint any views after the receiver.
  670. getContainer().repaint();
  671. }
  672. else if (a != null) {
  673. Component c = getContainer();
  674. Rectangle alloc = (Rectangle) a;
  675. c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  676. }
  677. }
  678. public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  679. int n = calculateLineCount();
  680. if (this.nlines != n) {
  681. // have to repaint any views after the receiver.
  682. this.nlines = n;
  683. WrappedPlainView.this.preferenceChanged(this, false, true);
  684. getContainer().repaint();
  685. }
  686. else if (a != null) {
  687. Component c = getContainer();
  688. Rectangle alloc = (Rectangle) a;
  689. c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  690. }
  691. }
  692. // --- variables ---------------------------------------
  693. int nlines;
  694. }
  695. }