/jEdit/tags/jedit-4-0-pre8/org/gjt/sp/jedit/textarea/ChunkCache.java

# · Java · 918 lines · 653 code · 129 blank · 136 comment · 173 complexity · 79d5fdfc916555ec89405bfcd1ed9122 MD5 · raw file

  1. /*
  2. * ChunkCache.java - Intermediate layer between token lists from a TokenMarker
  3. * and what you see on screen
  4. * :tabSize=8:indentSize=8:noTabs=false:
  5. * :folding=explicit:collapseFolds=1:
  6. *
  7. * Copyright (C) 2001, 2002 Slava Pestov
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.textarea;
  24. //{{{ Imports
  25. import javax.swing.text.*;
  26. import java.awt.font.*;
  27. import java.awt.geom.*;
  28. import java.awt.*;
  29. import java.util.ArrayList;
  30. import org.gjt.sp.jedit.syntax.*;
  31. import org.gjt.sp.jedit.Buffer;
  32. import org.gjt.sp.util.Log;
  33. //}}}
  34. /**
  35. * A "chunk" is a run of text with a specified font style and color. This class
  36. * contains various static methods for manipulating chunks and chunk lists. It
  37. * also has a number of package-private instance methods used internally by the
  38. * text area for painting text.
  39. *
  40. * @author Slava Pestov
  41. * @version $Id: ChunkCache.java 4103 2002-03-20 08:52:20Z spestov $
  42. */
  43. public class ChunkCache
  44. {
  45. //{{{ lineToChunkList() method
  46. /**
  47. * Converts a line of text into one or more chunk lists. There will be
  48. * one chunk list if soft wrap is disabled, more than one otherwise.
  49. * @param seg The segment containing line text
  50. * @param tokens The line's syntax tokens
  51. * @param styles The styles to highlight the line with
  52. * @param fontRenderContext Text transform, anti-alias, fractional font
  53. * metrics
  54. * @param e Used for calculating tab offsets
  55. * @param wrapMargin The wrap margin width, in pixels. 0 disables
  56. * @param out All resulting chunk lists will be appended to this list
  57. * @since jEdit 4.0pre4
  58. */
  59. public static void lineToChunkList(Segment seg, Token tokens,
  60. SyntaxStyle[] styles, FontRenderContext fontRenderContext,
  61. TabExpander e, float wrapMargin, java.util.List out)
  62. {
  63. // SILLY: allow for anti-aliased characters' "fuzz"
  64. if(wrapMargin != 0.0f)
  65. wrapMargin += 2.0f;
  66. float x = 0.0f;
  67. float endX = 0.0f;
  68. boolean seenNonWhiteSpace = false;
  69. boolean addedNonWhiteSpace = false;
  70. boolean lastWasSpace = false;
  71. float firstNonWhiteSpace = 0.0f;
  72. Chunk first = null;
  73. Chunk current = null;
  74. Chunk end = null;
  75. int tokenListOffset = 0;
  76. while(tokens.id != Token.END)
  77. {
  78. int flushIndex = tokenListOffset;
  79. for(int i = tokenListOffset; i < tokenListOffset + tokens.length; i++)
  80. {
  81. char ch = seg.array[seg.offset + i];
  82. if(ch == '\t' || (ch == ' ' && wrapMargin != 0.0f))
  83. {
  84. /* Create chunk with all text from
  85. * last position up to here, and wrap
  86. * if necessary. */
  87. //{{{
  88. if(i != flushIndex)
  89. {
  90. Chunk newChunk = new Chunk(
  91. tokens.id,seg,flushIndex,
  92. i,styles,fontRenderContext);
  93. if(current != null)
  94. current.next = newChunk;
  95. current = newChunk;
  96. x += newChunk.width;
  97. lastWasSpace = false;
  98. seenNonWhiteSpace = true;
  99. }
  100. if(end != null
  101. && !lastWasSpace
  102. && addedNonWhiteSpace
  103. && wrapMargin != 0
  104. && x > wrapMargin)
  105. {
  106. if(first != null)
  107. out.add(first);
  108. first = new Chunk(firstNonWhiteSpace,end.offset + 1);
  109. first.next = end.next;
  110. end.next = null;
  111. x = x + firstNonWhiteSpace - endX;
  112. }
  113. if(first == null)
  114. first = current;
  115. //}}}
  116. //{{{ Create ' ' chunk
  117. if(ch == ' ')
  118. {
  119. Chunk newChunk = new Chunk(
  120. tokens.id,seg,i,i + 1,
  121. styles,fontRenderContext);
  122. if(first == null)
  123. first = current = newChunk;
  124. else
  125. {
  126. current.next = newChunk;
  127. current = newChunk;
  128. }
  129. x += current.width;
  130. } //}}}
  131. //{{{ Create '\t' chunk
  132. else if(ch == '\t')
  133. {
  134. Chunk newChunk = new Chunk(
  135. tokens.id,seg,i,i,
  136. styles,fontRenderContext);
  137. if(first == null)
  138. first = current = newChunk;
  139. else
  140. {
  141. current.next = newChunk;
  142. current = newChunk;
  143. }
  144. float newX = e.nextTabStop(x,i + tokenListOffset);
  145. current.width = newX - x;
  146. x = newX;
  147. current.length = 1;
  148. } //}}}
  149. if(first == null)
  150. first = current;
  151. end = current;
  152. endX = x;
  153. if(flushIndex != i + 1)
  154. flushIndex = i + 1;
  155. lastWasSpace = true;
  156. addedNonWhiteSpace = seenNonWhiteSpace;
  157. if(!seenNonWhiteSpace)
  158. firstNonWhiteSpace = x;
  159. }
  160. else if(i == tokenListOffset + tokens.length - 1)
  161. {
  162. if(flushIndex != i + 1)
  163. {
  164. Chunk newChunk = new Chunk(
  165. tokens.id,seg,flushIndex,
  166. i + 1,styles,fontRenderContext);
  167. if(current != null)
  168. current.next = newChunk;
  169. current = newChunk;
  170. x += newChunk.width;
  171. seenNonWhiteSpace = true;
  172. lastWasSpace = false;
  173. }
  174. if(i == seg.count - 1 && wrapMargin != 0
  175. && x > wrapMargin
  176. && addedNonWhiteSpace
  177. && end != null)
  178. {
  179. if(first != null)
  180. out.add(first);
  181. first = new Chunk(firstNonWhiteSpace,end.offset + 1);
  182. first.next = end.next;
  183. end.next = null;
  184. x = x + firstNonWhiteSpace - endX;
  185. }
  186. if(first == null)
  187. first = current;
  188. addedNonWhiteSpace = seenNonWhiteSpace;
  189. }
  190. }
  191. tokenListOffset += tokens.length;
  192. tokens = tokens.next;
  193. }
  194. if(first != null)
  195. out.add(first);
  196. } //}}}
  197. //{{{ paintChunkList() method
  198. /**
  199. * Paints a chunk list.
  200. * @param chunks The chunk list
  201. * @param gfx The graphics context
  202. * @param x The x co-ordinate
  203. * @param y The y co-ordinate
  204. * @param width The width of the painting area, used for a token
  205. * background color hack
  206. * @param background The background color of the painting area,
  207. * used for background color hack
  208. * @return The width of the painted text
  209. * @since jEdit 4.0pre6
  210. */
  211. public static float paintChunkList(Chunk chunks, Graphics2D gfx,
  212. float x, float y, Color background, boolean glyphVector)
  213. {
  214. FontMetrics forBackground = gfx.getFontMetrics();
  215. float _x = 0.0f;
  216. Chunk first = chunks;
  217. Font lastFont = null;
  218. Color lastColor = null;
  219. while(chunks != null)
  220. {
  221. if(!chunks.inaccessable)
  222. {
  223. Font font = chunks.style.getFont();
  224. Color bgColor = chunks.style.getBackgroundColor();
  225. if(bgColor != null)
  226. {
  227. float x2 = _x + chunks.width;
  228. // Workaround for bug in Graphics2D in
  229. // JDK1.4 under Windows; calling
  230. // setPaintMode() does not reset
  231. // graphics mode.
  232. Graphics2D xorGfx = (Graphics2D)gfx.create();
  233. xorGfx.setXORMode(background);
  234. xorGfx.setColor(bgColor);
  235. xorGfx.fill(new Rectangle2D.Float(
  236. x + _x,y - forBackground.getAscent(),
  237. x2 - _x,forBackground.getHeight()));
  238. xorGfx.dispose();
  239. }
  240. if(chunks.text != null)
  241. {
  242. gfx.setFont(font);
  243. gfx.setColor(chunks.style.getForegroundColor());
  244. if(glyphVector)
  245. gfx.drawGlyphVector(chunks.text,x + _x,y);
  246. else
  247. gfx.drawString(chunks.str,x + _x,y);
  248. // Useful for debugging purposes
  249. //gfx.draw(new Rectangle2D.Float(x + chunks.x,y - 10,
  250. // chunks.width,10));
  251. }
  252. }
  253. _x += chunks.width;
  254. chunks = chunks.next;
  255. }
  256. return _x;
  257. } //}}}
  258. //{{{ offsetToX() method
  259. /**
  260. * Converts an offset in a chunk list into an x co-ordinate.
  261. * @param chunks The chunk list
  262. * @param offset The offset
  263. * @since jEdit 4.0pre4
  264. */
  265. public static float offsetToX(Chunk chunks, int offset)
  266. {
  267. if(offset < 0)
  268. throw new ArrayIndexOutOfBoundsException(offset + " < 0");
  269. float x = 0.0f;
  270. while(chunks != null)
  271. {
  272. if(!chunks.inaccessable && offset < chunks.offset + chunks.length)
  273. {
  274. if(chunks.text == null)
  275. break;
  276. else
  277. {
  278. return x + chunks.positions[
  279. (offset - chunks.offset) * 2];
  280. }
  281. }
  282. x += chunks.width;
  283. chunks = chunks.next;
  284. }
  285. return x;
  286. } //}}}
  287. //{{{ xToOffset() method
  288. /**
  289. * Converts an x co-ordinate in a chunk list into an offset.
  290. * @param chunks The chunk list
  291. * @param x The x co-ordinate
  292. * @param round Round up to next letter if past the middle of a letter?
  293. * @return The offset within the line, or -1 if the x co-ordinate is too
  294. * far to the right
  295. * @since jEdit 4.0pre4
  296. */
  297. public static int xToOffset(Chunk chunks, float x, boolean round)
  298. {
  299. float _x = 0.0f;
  300. while(chunks != null)
  301. {
  302. if(!chunks.inaccessable && x < _x + chunks.width)
  303. {
  304. if(chunks.text == null)
  305. {
  306. if(round && _x + chunks.width - x < x - _x)
  307. return chunks.offset + chunks.length;
  308. else
  309. return chunks.offset;
  310. }
  311. else
  312. {
  313. float xInChunk = x - _x;
  314. for(int i = 0; i < chunks.length; i++)
  315. {
  316. float glyphX = chunks.positions[i*2];
  317. float nextX = (i == chunks.length - 1
  318. ? chunks.width
  319. : chunks.positions[i*2+2]);
  320. if(nextX > xInChunk)
  321. {
  322. if(!round || nextX - xInChunk > xInChunk - glyphX)
  323. return chunks.offset + i;
  324. else
  325. return chunks.offset + i + 1;
  326. }
  327. }
  328. }
  329. }
  330. _x += chunks.width;
  331. chunks = chunks.next;
  332. }
  333. return -1;
  334. } //}}}
  335. //{{{ Chunk class
  336. /**
  337. * A linked-list useful for painting syntax highlighted text and
  338. * calculating offsets.
  339. * @since jEdit 4.0pre4
  340. */
  341. public static class Chunk
  342. {
  343. // should xToOffset() ignore this chunk?
  344. public boolean inaccessable;
  345. public float width;
  346. public SyntaxStyle style;
  347. public int offset;
  348. public int length;
  349. public String str;
  350. public GlyphVector text;
  351. public float[] positions;
  352. public Chunk next;
  353. Chunk(float width, int offset)
  354. {
  355. inaccessable = true;
  356. this.width = width;
  357. this.offset = offset;
  358. }
  359. Chunk(int tokenType, Segment seg, int offset, int end,
  360. SyntaxStyle[] styles, FontRenderContext fontRenderContext)
  361. {
  362. style = styles[tokenType];
  363. if(offset != end)
  364. {
  365. length = end - offset;
  366. str = new String(seg.array,seg.offset + offset,length);
  367. text = style.getFont().createGlyphVector(
  368. fontRenderContext,str);
  369. width = (float)text.getLogicalBounds().getWidth();
  370. positions = text.getGlyphPositions(0,length,null);
  371. }
  372. this.offset = offset;
  373. }
  374. } //}}}
  375. //{{{ ChunkCache constructor
  376. ChunkCache(JEditTextArea textArea)
  377. {
  378. this.textArea = textArea;
  379. out = new ArrayList();
  380. } //}}}
  381. //{{{ getMaxHorizontalScrollWidth() method
  382. int getMaxHorizontalScrollWidth()
  383. {
  384. int max = 0;
  385. for(int i = 0; i < lineInfo.length; i++)
  386. {
  387. LineInfo info = lineInfo[i];
  388. if(info.chunksValid && info.width > max)
  389. max = info.width;
  390. }
  391. return max;
  392. } //}}}
  393. //{{{ getScreenLineOfOffset() method
  394. int getScreenLineOfOffset(int line, int offset)
  395. {
  396. if(line < textArea.getFirstPhysicalLine())
  397. {
  398. return -1;
  399. }
  400. else if(line > textArea.getLastPhysicalLine())
  401. {
  402. return -1;
  403. }
  404. else if(!textArea.softWrap)
  405. {
  406. return textArea.physicalToVirtual(line)
  407. - textArea.getFirstLine();
  408. }
  409. else
  410. {
  411. int screenLine;
  412. if(line == lastScreenLineP)
  413. {
  414. LineInfo last = lineInfo[lastScreenLine];
  415. if(offset >= last.offset
  416. && offset < last.offset + last.length)
  417. {
  418. updateChunksUpTo(lastScreenLine);
  419. return lastScreenLine;
  420. }
  421. }
  422. screenLine = -1;
  423. // Find the screen line containing this offset
  424. for(int i = 0; i < lineInfo.length; i++)
  425. {
  426. updateChunksUpTo(i);
  427. LineInfo info = getLineInfo(i);
  428. if(info.physicalLine > line)
  429. {
  430. // line is invisible?
  431. if(i == 0)
  432. screenLine = 0;
  433. else
  434. screenLine = i - 1;
  435. break;
  436. }
  437. else if(info.physicalLine == line)
  438. {
  439. if(offset >= info.offset
  440. && offset < info.offset + info.length)
  441. {
  442. screenLine = i;
  443. break;
  444. }
  445. }
  446. }
  447. if(screenLine == -1)
  448. return -1;
  449. else
  450. {
  451. lastScreenLineP = line;
  452. lastScreenLine = screenLine;
  453. return screenLine;
  454. }
  455. }
  456. } //}}}
  457. //{{{ recalculateVisibleLines() method
  458. void recalculateVisibleLines()
  459. {
  460. lineInfo = new LineInfo[textArea.getVisibleLines() + 1];
  461. for(int i = 0; i < lineInfo.length; i++)
  462. lineInfo[i] = new LineInfo();
  463. lastScreenLine = lastScreenLineP = -1;
  464. } //}}}
  465. //{{{ setFirstLine() method
  466. void setFirstLine(int firstLine)
  467. {
  468. if(textArea.softWrap || Math.abs(firstLine - this.firstLine) >= lineInfo.length)
  469. {
  470. for(int i = 0; i < lineInfo.length; i++)
  471. {
  472. lineInfo[i].chunksValid = false;
  473. }
  474. }
  475. else if(firstLine > this.firstLine)
  476. {
  477. System.arraycopy(lineInfo,firstLine - this.firstLine,
  478. lineInfo,0,lineInfo.length - firstLine
  479. + this.firstLine);
  480. for(int i = lineInfo.length - firstLine
  481. + this.firstLine; i < lineInfo.length; i++)
  482. {
  483. lineInfo[i] = new LineInfo();
  484. }
  485. }
  486. else if(this.firstLine > firstLine)
  487. {
  488. System.arraycopy(lineInfo,0,lineInfo,this.firstLine - firstLine,
  489. lineInfo.length - this.firstLine + firstLine);
  490. for(int i = 0; i < this.firstLine - firstLine; i++)
  491. {
  492. lineInfo[i] = new LineInfo();
  493. }
  494. }
  495. lastScreenLine = lastScreenLineP = -1;
  496. this.firstLine = firstLine;
  497. } //}}}
  498. //{{{ invalidateAll() method
  499. void invalidateAll()
  500. {
  501. for(int i = 0; i < lineInfo.length; i++)
  502. {
  503. lineInfo[i].chunksValid = false;
  504. }
  505. lastScreenLine = lastScreenLineP = -1;
  506. } //}}}
  507. //{{{ invalidateChunksFrom() method
  508. void invalidateChunksFrom(int screenLine)
  509. {
  510. for(int i = screenLine; i < lineInfo.length; i++)
  511. {
  512. lineInfo[i].chunksValid = false;
  513. }
  514. lastScreenLine = lastScreenLineP = -1;
  515. } //}}}
  516. //{{{ invalidateChunksFromPhys() method
  517. void invalidateChunksFromPhys(int physicalLine)
  518. {
  519. for(int i = 0; i < lineInfo.length; i++)
  520. {
  521. if(lineInfo[i].physicalLine >= physicalLine)
  522. {
  523. invalidateChunksFrom(i);
  524. break;
  525. }
  526. }
  527. } //}}}
  528. //{{{ lineToChunkList() method
  529. void lineToChunkList(int physicalLine, ArrayList out)
  530. {
  531. TextAreaPainter painter = textArea.getPainter();
  532. Buffer buffer = textArea.getBuffer();
  533. buffer.getLineText(physicalLine,textArea.lineSegment);
  534. lineToChunkList(textArea.lineSegment,
  535. buffer.markTokens(physicalLine).getFirstToken(),
  536. painter.getStyles(),painter.getFontRenderContext(),
  537. painter,textArea.softWrap
  538. ? textArea.wrapMargin
  539. : 0.0f,out);
  540. } //}}}
  541. //{{{ updateChunksUpTo() method
  542. void updateChunksUpTo(int lastScreenLine)
  543. {
  544. if(!textArea.softWrap)
  545. return;
  546. if(lineInfo[lastScreenLine].chunksValid)
  547. return;
  548. int firstScreenLine = 0;
  549. for(int i = lastScreenLine; i >= 0; i--)
  550. {
  551. if(lineInfo[i].chunksValid)
  552. {
  553. firstScreenLine = i + 1;
  554. break;
  555. }
  556. }
  557. int physicalLine;
  558. if(firstScreenLine == 0)
  559. {
  560. physicalLine = textArea.getFirstPhysicalLine();
  561. }
  562. else
  563. {
  564. int prevPhysLine = lineInfo[
  565. firstScreenLine - 1]
  566. .physicalLine;
  567. if(prevPhysLine == -1)
  568. physicalLine = -1;
  569. else
  570. {
  571. physicalLine = textArea
  572. .getFoldVisibilityManager()
  573. .getNextVisibleLine(prevPhysLine);
  574. }
  575. }
  576. // Note that we rely on the fact that when a physical line is
  577. // invalidated, all screen lines/subregions of that line are
  578. // invalidated as well. See below comment for code that tries
  579. // to uphold this assumption.
  580. out.clear();
  581. int offset = 0;
  582. int length = 0;
  583. for(int i = firstScreenLine; i <= lastScreenLine; i++)
  584. {
  585. LineInfo info = lineInfo[i];
  586. Chunk chunks;
  587. if(out.size() == 0)
  588. {
  589. if(physicalLine != -1 && i != firstScreenLine)
  590. {
  591. physicalLine = textArea.getFoldVisibilityManager()
  592. .getNextVisibleLine(physicalLine);
  593. }
  594. if(physicalLine == -1)
  595. {
  596. info.chunks = null;
  597. info.chunksValid = true;
  598. info.physicalLine = -1;
  599. continue;
  600. }
  601. lineToChunkList(physicalLine,out);
  602. info.firstSubregion = true;
  603. if(out.size() == 0)
  604. {
  605. chunks = null;
  606. offset = 0;
  607. length = 1;
  608. }
  609. else
  610. {
  611. chunks = (Chunk)out.get(0);
  612. out.remove(0);
  613. offset = 0;
  614. if(out.size() != 0)
  615. length = ((Chunk)out.get(0)).offset - offset;
  616. else
  617. length = textArea.getLineLength(physicalLine) - offset + 1;
  618. }
  619. }
  620. else
  621. {
  622. info.firstSubregion = false;
  623. chunks = (Chunk)out.get(0);
  624. out.remove(0);
  625. offset = chunks.offset;
  626. if(out.size() != 0)
  627. length = ((Chunk)out.get(0)).offset - offset;
  628. else
  629. length = textArea.getLineLength(physicalLine) - offset + 1;
  630. }
  631. boolean lastSubregion = (out.size() == 0);
  632. if(i == lastScreenLine
  633. && lastScreenLine != lineInfo.length - 1)
  634. {
  635. /* If this line has become longer or shorter
  636. * (in which case the new physical line number
  637. * is different from the cached one) we need to:
  638. * - continue updating past the last line
  639. * - advise the text area to repaint
  640. * On the other hand, if the line wraps beyond
  641. * lastScreenLine, we need to keep updating the
  642. * chunk list to ensure proper alignment of
  643. * invalidation flags (see start of method) */
  644. if(info.physicalLine != physicalLine
  645. || info.lastSubregion != lastSubregion)
  646. {
  647. lastScreenLine++;
  648. needFullRepaint = true;
  649. }
  650. else if(out.size() != 0)
  651. lastScreenLine++;
  652. }
  653. info.physicalLine = physicalLine;
  654. info.lastSubregion = lastSubregion;
  655. info.offset = offset;
  656. info.length = length;
  657. info.chunks = chunks;
  658. info.chunksValid = true;
  659. }
  660. } //}}}
  661. //{{{ getLineInfo() method
  662. LineInfo getLineInfo(int screenLine)
  663. {
  664. LineInfo info = lineInfo[screenLine];
  665. if(textArea.softWrap)
  666. {
  667. if(!info.chunksValid)
  668. Log.log(Log.ERROR,this,"Not up-to-date: " + screenLine);
  669. return info;
  670. }
  671. else
  672. {
  673. if(!info.chunksValid)
  674. {
  675. int virtLine = screenLine + firstLine;
  676. if(virtLine >= textArea.getVirtualLineCount())
  677. {
  678. info.chunks = null;
  679. info.chunksValid = true;
  680. info.physicalLine = -1;
  681. }
  682. else
  683. {
  684. info.physicalLine = textArea.getFoldVisibilityManager()
  685. .virtualToPhysical(virtLine);
  686. out.clear();
  687. lineToChunkList(info.physicalLine,out);
  688. info.firstSubregion = true;
  689. info.lastSubregion = true;
  690. info.offset = 0;
  691. info.length = textArea.getLineLength(info.physicalLine) + 1;
  692. info.chunks = (out.size() == 0 ? null :
  693. (Chunk)out.get(0));
  694. info.chunksValid = true;
  695. }
  696. }
  697. return info;
  698. }
  699. } //}}}
  700. //{{{ getLineInfosForPhysicalLine() method
  701. public LineInfo[] getLineInfosForPhysicalLine(int physicalLine)
  702. {
  703. out.clear();
  704. lineToChunkList(physicalLine,out);
  705. if(out.size() == 0)
  706. out.add(null);
  707. LineInfo[] returnValue = new LineInfo[out.size()];
  708. for(int i = 0; i < out.size(); i++)
  709. {
  710. Chunk chunks = (Chunk)out.get(i);
  711. LineInfo info = new LineInfo();
  712. info.physicalLine = physicalLine;
  713. if(i == 0)
  714. {
  715. info.firstSubregion = true;
  716. info.offset = 0;
  717. }
  718. else
  719. info.offset = chunks.offset;
  720. if(i == out.size() - 1)
  721. {
  722. info.lastSubregion = true;
  723. info.length = textArea.getLineLength(physicalLine)
  724. - info.offset + 1;
  725. }
  726. else
  727. {
  728. info.length = ((Chunk)out.get(i + 1)).offset
  729. - info.offset;
  730. }
  731. info.chunksValid = true;
  732. info.chunks = chunks;
  733. returnValue[i] = info;
  734. }
  735. return returnValue;
  736. } //}}}
  737. //{{{ needFullRepaint() method
  738. /**
  739. * The needFullRepaint variable becomes true when the number of screen
  740. * lines in a physical line changes.
  741. */
  742. boolean needFullRepaint()
  743. {
  744. boolean retVal = needFullRepaint;
  745. needFullRepaint = false;
  746. return retVal;
  747. } //}}}
  748. //{{{ getLineInfoBackwardsCompatibility() method
  749. LineInfo getLineInfoBackwardsCompatibility(int physicalLineIndex)
  750. {
  751. LineInfo info = new LineInfo();
  752. out.clear();
  753. Buffer buffer = textArea.getBuffer();
  754. buffer.getLineText(physicalLineIndex,textArea.lineSegment);
  755. TextAreaPainter painter = textArea.getPainter();
  756. lineToChunkList(textArea.lineSegment,
  757. buffer.markTokens(physicalLineIndex).getFirstToken(),
  758. painter.getStyles(),painter.getFontRenderContext(),
  759. painter,0.0f,out);
  760. if(out.size() == 0)
  761. info.chunks = null;
  762. else
  763. info.chunks = (Chunk)out.get(0);
  764. info.physicalLine = physicalLineIndex;
  765. info.chunksValid = true;
  766. return info;
  767. } //}}}
  768. //{{{ Private members
  769. private JEditTextArea textArea;
  770. private int firstLine;
  771. private LineInfo[] lineInfo;
  772. private ArrayList out;
  773. private int lastScreenLineP;
  774. private int lastScreenLine;
  775. private boolean needFullRepaint;
  776. //}}}
  777. //{{{ LineInfo class
  778. static class LineInfo
  779. {
  780. int physicalLine;
  781. int offset;
  782. int length;
  783. boolean firstSubregion;
  784. boolean lastSubregion;
  785. boolean chunksValid;
  786. Chunk chunks;
  787. int width;
  788. } //}}}
  789. }