PageRenderTime 29ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/textarea/DisplayManager.java

#
Java | 1614 lines | 1168 code | 190 blank | 256 comment | 275 complexity | a1a065b05c09ea9e60f0406096999142 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * DisplayManager.java - Low-level text display
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001, 2003 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit.textarea;
  23. //{{{ Imports
  24. import java.awt.Toolkit;
  25. import java.util.*;
  26. import org.gjt.sp.jedit.buffer.*;
  27. import org.gjt.sp.jedit.*;
  28. import org.gjt.sp.util.Log;
  29. //}}}
  30. /**
  31. * Manages low-level text display tasks.
  32. * @since jEdit 4.2pre1
  33. * @author Slava Pestov
  34. * @version $Id: DisplayManager.java 4803 2003-06-21 00:28:50Z spestov $
  35. */
  36. public class DisplayManager
  37. {
  38. //{{{ Static part
  39. public static long scanCount, scannedLines;
  40. //{{{ getDisplayManager() method
  41. static DisplayManager getDisplayManager(Buffer buffer,
  42. JEditTextArea textArea)
  43. {
  44. List l = (List)bufferMap.get(buffer);
  45. DisplayManager dmgr;
  46. if(l == null)
  47. {
  48. l = new LinkedList();
  49. bufferMap.put(buffer,l);
  50. }
  51. Iterator liter = l.iterator();
  52. while(liter.hasNext())
  53. {
  54. dmgr = (DisplayManager)liter.next();
  55. if(!dmgr.inUse && dmgr.textArea == textArea)
  56. {
  57. dmgr.inUse = true;
  58. return dmgr;
  59. }
  60. }
  61. // if we got here, no unused display manager in list
  62. dmgr = new DisplayManager(buffer,textArea);
  63. dmgr.inUse = true;
  64. l.add(dmgr);
  65. return dmgr;
  66. } //}}}
  67. //{{{ releaseDisplayManager() method
  68. static void releaseDisplayManager(DisplayManager dmgr)
  69. {
  70. dmgr.inUse = false;
  71. } //}}}
  72. //{{{ bufferClosed() method
  73. public static void bufferClosed(Buffer buffer)
  74. {
  75. bufferMap.remove(buffer);
  76. } //}}}
  77. //{{{ textAreaDisposed() method
  78. static void textAreaDisposed(JEditTextArea textArea)
  79. {
  80. Iterator biter = bufferMap.values().iterator();
  81. while(biter.hasNext())
  82. {
  83. List l = (List)biter.next();
  84. Iterator liter = l.iterator();
  85. while(liter.hasNext())
  86. {
  87. DisplayManager dmgr = (DisplayManager)
  88. liter.next();
  89. if(dmgr.textArea == textArea)
  90. {
  91. dmgr.dispose();
  92. liter.remove();
  93. }
  94. }
  95. }
  96. } //}}}
  97. //{{{ _setScreenLineCount() method
  98. private static void _setScreenLineCount(Buffer buffer, int line,
  99. int oldCount, int count)
  100. {
  101. Iterator iter = ((List)bufferMap.get(buffer)).iterator();
  102. while(iter.hasNext())
  103. {
  104. ((DisplayManager)iter.next())._setScreenLineCount(line,oldCount,count);
  105. }
  106. } //}}}
  107. //{{{ _notifyScreenLineChanges() method
  108. /* static void _notifyScreenLineChanges(Buffer buffer)
  109. {
  110. Iterator iter = ((List)bufferMap.get(buffer)).iterator();
  111. while(iter.hasNext())
  112. {
  113. ((DisplayManager)iter.next())._notifyScreenLineChanges();
  114. }
  115. } */ //}}}
  116. private static Map bufferMap = new HashMap();
  117. //}}}
  118. //{{{ isLineVisible() method
  119. /**
  120. * Returns if the specified line is visible.
  121. * @param line A physical line index
  122. * @since jEdit 4.2pre1
  123. */
  124. public final boolean isLineVisible(int line)
  125. {
  126. return fvmget(line) % 2 == 0;
  127. } //}}}
  128. //{{{ getFirstVisibleLine() method
  129. /**
  130. * Returns the physical line number of the first visible line.
  131. * @since jEdit 4.2pre1
  132. */
  133. public int getFirstVisibleLine()
  134. {
  135. return fvm[0];
  136. } //}}}
  137. //{{{ getLastVisibleLine() method
  138. /**
  139. * Returns the physical line number of the last visible line.
  140. * @since jEdit 4.2pre1
  141. */
  142. public int getLastVisibleLine()
  143. {
  144. return fvm[fvmcount - 1] - 1;
  145. } //}}}
  146. //{{{ getNextVisibleLine() method
  147. /**
  148. * Returns the next visible line after the specified line index.
  149. * @param line A physical line index
  150. * @since jEdit 4.0pre1
  151. */
  152. public int getNextVisibleLine(int line)
  153. {
  154. int index = fvmget(line);
  155. /* in collapsed range */
  156. if(index % 2 != 0)
  157. {
  158. /* beyond last visible line */
  159. if(fvmcount == index + 1)
  160. return - 1;
  161. /* start of next expanded range */
  162. else
  163. return fvm[index + 1];
  164. }
  165. /* last in expanded range */
  166. else if(line == fvm[index + 1] - 1)
  167. {
  168. /* equal to last visible line */
  169. if(fvmcount == index + 2)
  170. return -1;
  171. /* start of next expanded range */
  172. else
  173. return fvm[index + 2];
  174. }
  175. /* next in expanded range */
  176. else
  177. return line + 1;
  178. } //}}}
  179. //{{{ getPrevVisibleLine() method
  180. /**
  181. * Returns the previous visible line before the specified line index.
  182. * @param line A physical line index
  183. * @since jEdit 4.0pre1
  184. */
  185. public int getPrevVisibleLine(int line)
  186. {
  187. int index = fvmget(line);
  188. /* before first visible line */
  189. if(index == -1)
  190. return -1;
  191. /* in collapsed range */
  192. else if(index % 2 == 1)
  193. {
  194. /* end of prev expanded range */
  195. return fvm[index] - 1;
  196. }
  197. /* first in expanded range */
  198. else if(line == fvm[index])
  199. {
  200. /* equal to first visible line */
  201. if(index == 0)
  202. return -1;
  203. /* end of prev expanded range */
  204. else
  205. return fvm[index - 1] - 1;
  206. }
  207. /* prev in expanded range */
  208. else
  209. return line - 1;
  210. } //}}}
  211. //{{{ getScreenLineCount() method
  212. public final int getScreenLineCount(int line)
  213. {
  214. if(lineMgr.isScreenLineCountValid(line))
  215. return lineMgr.getScreenLineCount(line);
  216. else
  217. {
  218. int newCount = textArea.chunkCache
  219. .getLineSubregionCount(line);
  220. setScreenLineCount(line,newCount);
  221. return newCount;
  222. }
  223. } //}}}
  224. //{{{ getScrollLineCount() method
  225. public final int getScrollLineCount()
  226. {
  227. return scrollLineCount.scrollLine;
  228. } //}}}
  229. //{{{ collapseFold() method
  230. /**
  231. * Collapses the fold at the specified physical line index.
  232. * @param line A physical line index
  233. * @since jEdit 4.2pre1
  234. */
  235. public void collapseFold(int line)
  236. {
  237. int lineCount = buffer.getLineCount();
  238. int start = 0;
  239. int end = lineCount - 1;
  240. // if the caret is on a collapsed fold, collapse the
  241. // parent fold
  242. if(line != 0
  243. && line != buffer.getLineCount() - 1
  244. && buffer.isFoldStart(line)
  245. && !isLineVisible(line + 1))
  246. {
  247. line--;
  248. }
  249. int initialFoldLevel = buffer.getFoldLevel(line);
  250. //{{{ Find fold start and end...
  251. if(line != lineCount - 1
  252. && buffer.getFoldLevel(line + 1) > initialFoldLevel)
  253. {
  254. // this line is the start of a fold
  255. start = line + 1;
  256. for(int i = line + 1; i < lineCount; i++)
  257. {
  258. if(buffer.getFoldLevel(i) <= initialFoldLevel)
  259. {
  260. end = i - 1;
  261. break;
  262. }
  263. }
  264. }
  265. else
  266. {
  267. boolean ok = false;
  268. // scan backwards looking for the start
  269. for(int i = line - 1; i >= 0; i--)
  270. {
  271. if(buffer.getFoldLevel(i) < initialFoldLevel)
  272. {
  273. start = i + 1;
  274. ok = true;
  275. break;
  276. }
  277. }
  278. if(!ok)
  279. {
  280. // no folds in buffer
  281. return;
  282. }
  283. for(int i = line + 1; i < lineCount; i++)
  284. {
  285. if(buffer.getFoldLevel(i) < initialFoldLevel)
  286. {
  287. end = i - 1;
  288. break;
  289. }
  290. }
  291. } //}}}
  292. // Collapse the fold...
  293. hideLineRange(start,end);
  294. _notifyScreenLineChanges();
  295. textArea.foldStructureChanged();
  296. } //}}}
  297. //{{{ expandFold() method
  298. /**
  299. * Expands the fold at the specified physical line index.
  300. * @param line A physical line index
  301. * @param fully If true, all subfolds will also be expanded
  302. * @since jEdit 4.2pre1
  303. */
  304. public int expandFold(int line, boolean fully)
  305. {
  306. // the first sub-fold. used by JEditTextArea.expandFold().
  307. int returnValue = -1;
  308. int lineCount = buffer.getLineCount();
  309. int start = 0;
  310. int end = lineCount - 1;
  311. int initialFoldLevel = buffer.getFoldLevel(line);
  312. //{{{ Find fold start and fold end...
  313. if(line != lineCount - 1
  314. && isLineVisible(line)
  315. && !isLineVisible(line + 1)
  316. && buffer.getFoldLevel(line + 1) > initialFoldLevel)
  317. {
  318. // this line is the start of a fold
  319. start = line + 1;
  320. for(int i = line + 1; i < lineCount; i++)
  321. {
  322. if(/* isLineVisible(i) && */
  323. buffer.getFoldLevel(i) <= initialFoldLevel)
  324. {
  325. end = i - 1;
  326. break;
  327. }
  328. }
  329. }
  330. else
  331. {
  332. boolean ok = false;
  333. // scan backwards looking for the start
  334. for(int i = line - 1; i >= 0; i--)
  335. {
  336. //XXX
  337. if(isLineVisible(i) && buffer.getFoldLevel(i) < initialFoldLevel)
  338. {
  339. start = i + 1;
  340. ok = true;
  341. break;
  342. }
  343. }
  344. if(!ok)
  345. {
  346. // no folds in buffer
  347. return -1;
  348. }
  349. for(int i = line + 1; i < lineCount; i++)
  350. {
  351. //XXX
  352. if((isLineVisible(i) &&
  353. buffer.getFoldLevel(i) < initialFoldLevel)
  354. || i == getLastVisibleLine())
  355. {
  356. end = i - 1;
  357. break;
  358. }
  359. }
  360. } //}}}
  361. //{{{ Expand the fold...
  362. if(fully)
  363. {
  364. showLineRange(start,end);
  365. }
  366. else
  367. {
  368. // we need a different value of initialFoldLevel here!
  369. initialFoldLevel = buffer.getFoldLevel(start);
  370. int firstVisible = start;
  371. for(int i = start; i <= end; i++)
  372. {
  373. if(buffer.getFoldLevel(i) > initialFoldLevel)
  374. {
  375. if(returnValue == -1
  376. && i != 0
  377. && buffer.isFoldStart(i - 1))
  378. {
  379. returnValue = i - 1;
  380. }
  381. if(firstVisible != i)
  382. {
  383. showLineRange(firstVisible,i - 1);
  384. }
  385. firstVisible = i + 1;
  386. }
  387. }
  388. if(firstVisible != end + 1)
  389. showLineRange(firstVisible,end);
  390. if(!isLineVisible(line))
  391. {
  392. // this is a hack, and really needs to be done better.
  393. expandFold(line,false);
  394. return returnValue;
  395. }
  396. } //}}}
  397. _notifyScreenLineChanges();
  398. textArea.foldStructureChanged();
  399. return returnValue;
  400. } //}}}
  401. //{{{ expandAllFolds() method
  402. /**
  403. * Expands all folds.
  404. * @since jEdit 4.2pre1
  405. */
  406. public void expandAllFolds()
  407. {
  408. showLineRange(0,buffer.getLineCount() - 1);
  409. _notifyScreenLineChanges();
  410. textArea.foldStructureChanged();
  411. } //}}}
  412. //{{{ expandFolds() method
  413. /**
  414. * This method should only be called from <code>actions.xml</code>.
  415. * @since jEdit 4.2pre1
  416. */
  417. public void expandFolds(char digit)
  418. {
  419. if(digit < '1' || digit > '9')
  420. {
  421. Toolkit.getDefaultToolkit().beep();
  422. return;
  423. }
  424. else
  425. expandFolds((int)(digit - '1') + 1);
  426. } //}}}
  427. //{{{ expandFolds() method
  428. /**
  429. * Expands all folds with the specified fold level.
  430. * @param foldLevel The fold level
  431. * @since jEdit 4.2pre1
  432. */
  433. public void expandFolds(int foldLevel)
  434. {
  435. if(buffer.getFoldHandler() instanceof IndentFoldHandler)
  436. foldLevel = (foldLevel - 1) * buffer.getIndentSize() + 1;
  437. showLineRange(0,buffer.getLineCount() - 1);
  438. /* this ensures that the first line is always visible */
  439. boolean seenVisibleLine = false;
  440. int firstInvisible = 0;
  441. for(int i = 0; i < buffer.getLineCount(); i++)
  442. {
  443. if(!seenVisibleLine || buffer.getFoldLevel(i) < foldLevel)
  444. {
  445. if(firstInvisible != i)
  446. {
  447. hideLineRange(firstInvisible,
  448. i - 1);
  449. }
  450. firstInvisible = i + 1;
  451. seenVisibleLine = true;
  452. }
  453. }
  454. if(firstInvisible != buffer.getLineCount())
  455. hideLineRange(firstInvisible,buffer.getLineCount() - 1);
  456. _notifyScreenLineChanges();
  457. textArea.foldStructureChanged();
  458. } //}}}
  459. //{{{ narrow() method
  460. /**
  461. * Narrows the visible portion of the buffer to the specified
  462. * line range.
  463. * @param start The first line
  464. * @param end The last line
  465. * @since jEdit 4.2pre1
  466. */
  467. public void narrow(int start, int end)
  468. {
  469. if(start > end || start < 0 || end >= buffer.getLineCount())
  470. throw new ArrayIndexOutOfBoundsException(start + ", " + end);
  471. if(start < getFirstVisibleLine() || end > getLastVisibleLine())
  472. expandAllFolds();
  473. hideLineRange(0,start - 1);
  474. hideLineRange(end + 1,buffer.getLineCount() - 1);
  475. // if we narrowed to a single collapsed fold
  476. if(start != buffer.getLineCount() - 1
  477. && !isLineVisible(start + 1))
  478. expandFold(start,false);
  479. // Hack... need a more direct way of obtaining a view?
  480. // JEditTextArea.getView() method?
  481. GUIUtilities.getView(textArea).getStatus().setMessageAndClear(
  482. jEdit.getProperty("view.status.narrow"));
  483. _notifyScreenLineChanges();
  484. textArea.foldStructureChanged();
  485. } //}}}
  486. //{{{ Package-private members
  487. boolean softWrap;
  488. int wrapMargin;
  489. FirstLine firstLine;
  490. ScrollLineCount scrollLineCount;
  491. //{{{ init() method
  492. void init()
  493. {
  494. if(!initialized)
  495. {
  496. initialized = true;
  497. fvm = new int[2];
  498. if(buffer.isLoaded())
  499. bufferChangeHandler.foldHandlerChanged(buffer);
  500. else
  501. fvmreset();
  502. _notifyScreenLineChanges();
  503. }
  504. else
  505. {
  506. _notifyScreenLineChanges();
  507. textArea.updateScrollBars();
  508. textArea.recalculateLastPhysicalLine();
  509. }
  510. } //}}}
  511. //{{{ setScreenLineCount() method
  512. /**
  513. * Sets the number of screen lines that the specified physical line
  514. * is split into.
  515. * @since jEdit 4.2pre1
  516. */
  517. void setScreenLineCount(int line, int count)
  518. {
  519. int oldCount = lineMgr.getScreenLineCount(line);
  520. // still have to call this even if it equals the
  521. // old one so that the offset manager sets the
  522. // validity flag!
  523. lineMgr.setScreenLineCount(line,count);
  524. // this notifies each display manager editing this
  525. // buffer of the screen line count change
  526. if(count != oldCount)
  527. _setScreenLineCount(buffer,line,oldCount,count);
  528. } //}}}
  529. //{{{ updateWrapSettings() method
  530. void updateWrapSettings()
  531. {
  532. String wrap = buffer.getStringProperty("wrap");
  533. softWrap = wrap.equals("soft");
  534. if(textArea.maxLineLen <= 0)
  535. {
  536. softWrap = false;
  537. wrapMargin = 0;
  538. }
  539. else
  540. {
  541. // stupidity
  542. char[] foo = new char[textArea.maxLineLen];
  543. for(int i = 0; i < foo.length; i++)
  544. {
  545. foo[i] = ' ';
  546. }
  547. TextAreaPainter painter = textArea.getPainter();
  548. wrapMargin = (int)painter.getFont().getStringBounds(
  549. foo,0,foo.length,
  550. painter.getFontRenderContext())
  551. .getWidth();
  552. }
  553. } //}}}
  554. //{{{ _notifyScreenLineChanges() method
  555. void _notifyScreenLineChanges()
  556. {
  557. if(Debug.SCROLL_DEBUG)
  558. Log.log(Log.DEBUG,this,"_notifyScreenLineChanges()");
  559. // when the text area switches to us, it will do
  560. // a reset anyway
  561. if(textArea.getDisplayManager() == this)
  562. {
  563. if(firstLine.callReset)
  564. firstLine.reset();
  565. else if(firstLine.callChanged)
  566. firstLine.changed();
  567. if(scrollLineCount.callReset)
  568. scrollLineCount.reset();
  569. else if(scrollLineCount.callChanged)
  570. scrollLineCount.changed();
  571. firstLine.callReset = firstLine.callChanged = false;
  572. scrollLineCount.callReset = scrollLineCount.callChanged = false;
  573. }
  574. } //}}}
  575. //}}}
  576. //{{{ Private members
  577. private boolean initialized;
  578. private boolean inUse;
  579. private Buffer buffer;
  580. private LineManager lineMgr;
  581. private JEditTextArea textArea;
  582. private BufferChangeHandler bufferChangeHandler;
  583. /**
  584. * The fold visibility map.
  585. *
  586. * All lines from fvm[2*n] to fvm[2*n+1]-1 inclusive are visible.
  587. * All lines from position fvm[2*n+1] to fvm[2*n+2]-1 inclusive are
  588. * invisible.
  589. *
  590. * Examples:
  591. * ---------
  592. * All lines visible: { 0, buffer.getLineCount() }
  593. * Narrow from a to b: { a, b + 1 }
  594. * Collapsed fold from a to b: { 0, a + 1, b, buffer.getLineCount() }
  595. *
  596. * Note: length is always even.
  597. */
  598. private int[] fvm;
  599. private int fvmcount;
  600. private int lastfvmget = -1;
  601. //{{{ DisplayManager constructor
  602. private DisplayManager(Buffer buffer, JEditTextArea textArea)
  603. {
  604. this.buffer = buffer;
  605. this.lineMgr = buffer._getLineManager();
  606. this.textArea = textArea;
  607. scrollLineCount = new ScrollLineCount();
  608. firstLine = new FirstLine();
  609. bufferChangeHandler = new BufferChangeHandler();
  610. // this listener priority thing is a bad hack...
  611. buffer.addBufferChangeListener(bufferChangeHandler,
  612. Buffer.HIGH_PRIORITY);
  613. } //}}}
  614. //{{{ dispose() method
  615. private void dispose()
  616. {
  617. buffer.removeBufferChangeListener(bufferChangeHandler);
  618. } //}}}
  619. //{{{ fvmreset() method
  620. private void fvmreset()
  621. {
  622. lastfvmget = -1;
  623. fvmcount = 2;
  624. fvm[0] = 0;
  625. fvm[1] = buffer.getLineCount();
  626. } //}}}
  627. //{{{ fvmtest() method
  628. /**
  629. * Debug.
  630. */
  631. private void fvmtest()
  632. {
  633. boolean[] map = new boolean[buffer.getLineCount()];
  634. java.util.Random random = new java.util.Random();
  635. // randomly show and hide lines, both in the fvm structure and
  636. // a simple boolean map
  637. for(int i = 0; i < 1000; i++)
  638. {
  639. int random1 = Math.abs(random.nextInt() % map.length);
  640. int random2 = Math.abs(random.nextInt() % map.length);
  641. if(random2 < random1)
  642. {
  643. int tmp = random1;
  644. random1 = random2;
  645. random2 = tmp;
  646. }
  647. boolean vis = (random1 + random2 % 2 == 0);
  648. if(vis)
  649. showLineRange(random1,random2);
  650. else
  651. hideLineRange(random1,random2);
  652. for(int j = random1; j <= random2; j++)
  653. {
  654. map[j] = vis;
  655. }
  656. }
  657. // check for the fvm structure is correct
  658. for(int i = 0; i < map.length; i++)
  659. {
  660. if(isLineVisible(i) != map[i])
  661. Log.log(Log.ERROR,this,"Mismatch: " + i);
  662. }
  663. } //}}}
  664. //{{{ fvmget() method
  665. /**
  666. * Returns the fold visibility map index for the given line.
  667. */
  668. private int fvmget(int line)
  669. {
  670. scanCount++;
  671. if(line < fvm[0])
  672. return -1;
  673. if(line >= fvm[fvmcount - 1])
  674. return fvmcount - 1;
  675. if(lastfvmget != -1)
  676. {
  677. if(line >= fvm[lastfvmget])
  678. {
  679. if(lastfvmget == fvmcount - 1
  680. || line < fvm[lastfvmget + 1])
  681. {
  682. return lastfvmget;
  683. }
  684. }
  685. }
  686. int start = 0;
  687. int end = fvmcount - 1;
  688. loop: for(;;)
  689. {
  690. scannedLines++;
  691. switch(end - start)
  692. {
  693. case 0:
  694. lastfvmget = start;
  695. break loop;
  696. case 1:
  697. int value = fvm[end];
  698. if(value <= line)
  699. lastfvmget = end;
  700. else
  701. lastfvmget = start;
  702. break loop;
  703. default:
  704. int pivot = (end + start) / 2;
  705. value = fvm[pivot];
  706. if(value == line)
  707. {
  708. lastfvmget = pivot;
  709. break loop;
  710. }
  711. else if(value < line)
  712. start = pivot;
  713. else
  714. end = pivot - 1;
  715. break;
  716. }
  717. }
  718. return lastfvmget;
  719. } //}}}
  720. //{{{ fvmput() method
  721. /**
  722. * Replaces from <code>start</code> to <code>end-1</code> inclusive with
  723. * <code>put</code>. Update <code>fvmcount</code>.
  724. */
  725. private void fvmput(int start, int end, int[] put)
  726. {
  727. if(Debug.FOLD_VIS_DEBUG)
  728. {
  729. StringBuffer buf = new StringBuffer("{");
  730. if(put != null)
  731. {
  732. for(int i = 0; i < put.length; i++)
  733. {
  734. if(i != 0)
  735. buf.append(',');
  736. buf.append(put[i]);
  737. }
  738. }
  739. buf.append("}");
  740. Log.log(Log.DEBUG,this,"fvmput(" + start + ","
  741. + end + "," + buf + ")");
  742. }
  743. int putl = (put == null ? 0 : put.length);
  744. int delta = putl - (end - start);
  745. if(fvmcount + delta > fvm.length)
  746. {
  747. int[] newfvm = new int[fvm.length * 2 + 1];
  748. System.arraycopy(fvm,0,newfvm,0,fvmcount);
  749. fvm = newfvm;
  750. }
  751. if(delta != 0)
  752. {
  753. System.arraycopy(fvm,end,fvm,start + putl,
  754. fvmcount - end);
  755. }
  756. if(putl != 0)
  757. {
  758. System.arraycopy(put,0,fvm,start,put.length);
  759. }
  760. fvmcount += delta;
  761. if(fvmcount == 0)
  762. throw new InternalError();
  763. } //}}}
  764. //{{{ fvmput2() method
  765. /**
  766. * Merge previous and next entry if necessary.
  767. */
  768. private void fvmput2(int starti, int endi, int start, int end)
  769. {
  770. if(Debug.FOLD_VIS_DEBUG)
  771. {
  772. Log.log(Log.DEBUG,this,"*fvmput2(" + starti + ","
  773. + endi + "," + start + "," + end + ")");
  774. }
  775. if(starti != -1 && fvm[starti] == start)
  776. {
  777. if(endi <= fvmcount - 2 && fvm[endi + 1]
  778. == end + 1)
  779. {
  780. fvmput(starti,endi + 2,null);
  781. }
  782. else
  783. {
  784. fvmput(starti,endi + 1,
  785. new int[] { end + 1 });
  786. }
  787. }
  788. else
  789. {
  790. if(endi != fvmcount - 1 && fvm[endi + 1]
  791. == end + 1)
  792. {
  793. fvmput(starti + 1,endi + 2,
  794. new int[] { start });
  795. }
  796. else
  797. {
  798. fvmput(starti + 1,endi + 1,
  799. new int[] { start,
  800. end + 1 });
  801. }
  802. }
  803. } //}}}
  804. //{{{ showLineRange() method
  805. private void showLineRange(int start, int end)
  806. {
  807. if(Debug.FOLD_VIS_DEBUG)
  808. {
  809. Log.log(Log.DEBUG,this,"showLineRange(" + start
  810. + "," + end + ")");
  811. }
  812. for(int i = start; i <= end; i++)
  813. {
  814. //XXX
  815. if(!isLineVisible(i))
  816. {
  817. // important: not lineMgr.getScreenLineCount()
  818. int screenLines = getScreenLineCount(i);
  819. if(firstLine.physicalLine >= i)
  820. {
  821. firstLine.scrollLine += screenLines;
  822. firstLine.callChanged = true;
  823. }
  824. scrollLineCount.scrollLine += screenLines;
  825. scrollLineCount.callChanged = true;
  826. }
  827. }
  828. /* update fold visibility map. */
  829. int starti = fvmget(start);
  830. int endi = fvmget(end);
  831. if(starti % 2 == 0)
  832. {
  833. if(endi % 2 == 0)
  834. fvmput(starti + 1,endi + 1,null);
  835. else
  836. {
  837. if(end != fvmcount - 1
  838. && fvm[endi + 1] == end + 1)
  839. fvmput(starti + 1,endi + 2,null);
  840. else
  841. {
  842. fvmput(starti + 1,endi,null);
  843. fvm[starti + 1] = end + 1;
  844. }
  845. }
  846. }
  847. else
  848. {
  849. if(endi % 2 == 0)
  850. {
  851. if(starti != -1 && fvm[starti] == start)
  852. fvmput(starti,endi + 1,null);
  853. else
  854. {
  855. fvmput(starti + 1,endi,null);
  856. fvm[starti + 1] = start;
  857. }
  858. }
  859. else
  860. fvmput2(starti,endi,start,end);
  861. }
  862. lastfvmget = -1;
  863. } //}}}
  864. //{{{ hideLineRange() method
  865. private void hideLineRange(int start, int end)
  866. {
  867. if(Debug.FOLD_VIS_DEBUG)
  868. {
  869. Log.log(Log.DEBUG,this,"hideLineRange(" + start
  870. + "," + end + ")");
  871. }
  872. for(int i = start; i <= end; i++)
  873. {
  874. //XXX
  875. if(isLineVisible(i))
  876. {
  877. int screenLines = lineMgr
  878. .getScreenLineCount(i);
  879. if(firstLine.physicalLine >= i)
  880. {
  881. firstLine.scrollLine -= screenLines;
  882. firstLine.callChanged = true;
  883. }
  884. scrollLineCount.scrollLine -= screenLines;
  885. scrollLineCount.callChanged = true;
  886. }
  887. }
  888. /* update fold visibility map. */
  889. int starti = fvmget(start);
  890. int endi = fvmget(end);
  891. if(starti % 2 == 0)
  892. {
  893. if(endi % 2 == 0)
  894. fvmput2(starti,endi,start,end);
  895. else
  896. {
  897. fvmput(starti + 1,endi,null);
  898. fvm[starti + 1] = start;
  899. }
  900. }
  901. else
  902. {
  903. if(endi % 2 == 0)
  904. {
  905. fvmput(starti + 1,endi,null);
  906. fvm[starti + 1] = end + 1;
  907. }
  908. else
  909. fvmput(starti + 1,endi + 1,null);
  910. }
  911. lastfvmget = -1;
  912. } //}}}
  913. //{{{ _setScreenLineCount() method
  914. private void _setScreenLineCount(int line, int oldCount, int count)
  915. {
  916. if(!isLineVisible(line))
  917. return;
  918. if(firstLine.physicalLine >= line)
  919. {
  920. if(firstLine.physicalLine == line)
  921. firstLine.callChanged = true;
  922. else
  923. {
  924. firstLine.scrollLine += (count - oldCount);
  925. firstLine.callChanged = true;
  926. }
  927. }
  928. scrollLineCount.scrollLine += (count - oldCount);
  929. scrollLineCount.callChanged = true;
  930. } //}}}
  931. //}}}
  932. //{{{ Anchor class
  933. static abstract class Anchor
  934. {
  935. int physicalLine;
  936. int scrollLine;
  937. boolean callChanged;
  938. boolean callReset;
  939. abstract void reset();
  940. abstract void changed();
  941. } //}}}
  942. //{{{ ScrollLineCount class
  943. class ScrollLineCount extends Anchor
  944. {
  945. //{{{ changed() method
  946. public void changed()
  947. {
  948. if(Debug.SCROLL_DEBUG)
  949. Log.log(Log.DEBUG,this,"changed()");
  950. textArea.updateScrollBars();
  951. textArea.recalculateLastPhysicalLine();
  952. } //}}}
  953. //{{{ reset() method
  954. public void reset()
  955. {
  956. if(Debug.SCROLL_DEBUG)
  957. Log.log(Log.DEBUG,this,"reset()");
  958. updateWrapSettings();
  959. physicalLine = getFirstVisibleLine();
  960. scrollLine = 0;
  961. while(physicalLine != -1)
  962. {
  963. scrollLine += getScreenLineCount(physicalLine);
  964. physicalLine = getNextVisibleLine(physicalLine);
  965. }
  966. physicalLine = buffer.getLineCount();
  967. firstLine.ensurePhysicalLineIsVisible();
  968. textArea.recalculateLastPhysicalLine();
  969. textArea.updateScrollBars();
  970. } //}}}
  971. } //}}}
  972. //{{{ FirstLine class
  973. class FirstLine extends Anchor
  974. {
  975. int skew;
  976. //{{{ changed() method
  977. public void changed()
  978. {
  979. //{{{ Debug code
  980. if(Debug.SCROLL_DEBUG)
  981. {
  982. Log.log(Log.DEBUG,this,"changed() before: "
  983. + physicalLine + ":" + scrollLine);
  984. } //}}}
  985. ensurePhysicalLineIsVisible();
  986. int screenLines = getScreenLineCount(physicalLine);
  987. if(skew >= screenLines)
  988. skew = screenLines - 1;
  989. //{{{ Debug code
  990. if(Debug.VERIFY_FIRST_LINE)
  991. {
  992. int verifyScrollLine = 0;
  993. for(int i = 0; i < buffer.getLineCount(); i++)
  994. {
  995. if(!isLineVisible(i))
  996. continue;
  997. if(i >= physicalLine)
  998. break;
  999. verifyScrollLine += getScreenLineCount(i);
  1000. }
  1001. if(verifyScrollLine != scrollLine)
  1002. {
  1003. Exception ex = new Exception(scrollLine + ":" + verifyScrollLine);
  1004. Log.log(Log.ERROR,this,ex);
  1005. new org.gjt.sp.jedit.gui.BeanShellErrorDialog(null,ex);
  1006. }
  1007. }
  1008. if(Debug.SCROLL_DEBUG)
  1009. {
  1010. Log.log(Log.DEBUG,this,"changed() after: "
  1011. + physicalLine + ":" + scrollLine);
  1012. } //}}}
  1013. textArea.updateScrollBars();
  1014. textArea.recalculateLastPhysicalLine();
  1015. } //}}}
  1016. //{{{ reset() method
  1017. public void reset()
  1018. {
  1019. if(Debug.SCROLL_DEBUG)
  1020. Log.log(Log.DEBUG,this,"reset()");
  1021. String wrap = buffer.getStringProperty("wrap");
  1022. softWrap = wrap.equals("soft");
  1023. if(textArea.maxLineLen <= 0)
  1024. {
  1025. softWrap = false;
  1026. wrapMargin = 0;
  1027. }
  1028. else
  1029. {
  1030. // stupidity
  1031. char[] foo = new char[textArea.maxLineLen];
  1032. for(int i = 0; i < foo.length; i++)
  1033. {
  1034. foo[i] = ' ';
  1035. }
  1036. TextAreaPainter painter = textArea.getPainter();
  1037. wrapMargin = (int)painter.getFont().getStringBounds(
  1038. foo,0,foo.length,
  1039. painter.getFontRenderContext())
  1040. .getWidth();
  1041. }
  1042. scrollLine = 0;
  1043. int i = getFirstVisibleLine();
  1044. for(;;)
  1045. {
  1046. if(i >= physicalLine)
  1047. break;
  1048. scrollLine += getScreenLineCount(i);
  1049. int nextLine = getNextVisibleLine(i);
  1050. if(nextLine == -1)
  1051. break;
  1052. else
  1053. i = nextLine;
  1054. }
  1055. physicalLine = i;
  1056. int screenLines = getScreenLineCount(physicalLine);
  1057. if(skew >= screenLines)
  1058. skew = screenLines - 1;
  1059. textArea.updateScrollBars();
  1060. } //}}}
  1061. //{{{ physDown() method
  1062. // scroll down by physical line amount
  1063. void physDown(int amount, int screenAmount)
  1064. {
  1065. if(Debug.SCROLL_DEBUG)
  1066. {
  1067. Log.log(Log.DEBUG,this,"physDown() start: "
  1068. + physicalLine + ":" + scrollLine);
  1069. }
  1070. skew = 0;
  1071. if(!isLineVisible(physicalLine))
  1072. {
  1073. int lastVisibleLine = getLastVisibleLine();
  1074. if(physicalLine > lastVisibleLine)
  1075. physicalLine = lastVisibleLine;
  1076. else
  1077. {
  1078. int nextPhysicalLine = getNextVisibleLine(physicalLine);
  1079. amount -= (nextPhysicalLine - physicalLine);
  1080. scrollLine += getScreenLineCount(physicalLine);
  1081. physicalLine = nextPhysicalLine;
  1082. }
  1083. }
  1084. for(;;)
  1085. {
  1086. int nextPhysicalLine = getNextVisibleLine(
  1087. physicalLine);
  1088. if(nextPhysicalLine == -1)
  1089. break;
  1090. else if(nextPhysicalLine > physicalLine + amount)
  1091. break;
  1092. else
  1093. {
  1094. scrollLine += getScreenLineCount(physicalLine);
  1095. amount -= (nextPhysicalLine - physicalLine);
  1096. physicalLine = nextPhysicalLine;
  1097. }
  1098. }
  1099. if(Debug.SCROLL_DEBUG)
  1100. {
  1101. Log.log(Log.DEBUG,this,"physDown() end: "
  1102. + physicalLine + ":" + scrollLine);
  1103. }
  1104. // JEditTextArea.scrollTo() needs this to simplify
  1105. // its code
  1106. if(screenAmount < 0)
  1107. scrollUp(-screenAmount);
  1108. else if(screenAmount > 0)
  1109. scrollDown(screenAmount);
  1110. } //}}}
  1111. //{{{ physUp() method
  1112. // scroll up by physical line amount
  1113. void physUp(int amount, int screenAmount)
  1114. {
  1115. if(Debug.SCROLL_DEBUG)
  1116. {
  1117. Log.log(Log.DEBUG,this,"physUp() start: "
  1118. + physicalLine + ":" + scrollLine);
  1119. }
  1120. skew = 0;
  1121. if(!isLineVisible(physicalLine))
  1122. {
  1123. int firstVisibleLine = getFirstVisibleLine();
  1124. if(physicalLine < firstVisibleLine)
  1125. physicalLine = firstVisibleLine;
  1126. else
  1127. {
  1128. int prevPhysicalLine = getPrevVisibleLine(physicalLine);
  1129. amount -= (physicalLine - prevPhysicalLine);
  1130. }
  1131. }
  1132. for(;;)
  1133. {
  1134. int prevPhysicalLine = getPrevVisibleLine(
  1135. physicalLine);
  1136. if(prevPhysicalLine == -1)
  1137. break;
  1138. else if(prevPhysicalLine < physicalLine - amount)
  1139. break;
  1140. else
  1141. {
  1142. amount -= (physicalLine - prevPhysicalLine);
  1143. physicalLine = prevPhysicalLine;
  1144. scrollLine -= getScreenLineCount(
  1145. prevPhysicalLine);
  1146. }
  1147. }
  1148. if(Debug.SCROLL_DEBUG)
  1149. {
  1150. Log.log(Log.DEBUG,this,"physUp() end: "
  1151. + physicalLine + ":" + scrollLine);
  1152. }
  1153. // JEditTextArea.scrollTo() needs this to simplify
  1154. // its code
  1155. if(screenAmount < 0)
  1156. scrollUp(-screenAmount);
  1157. else if(screenAmount > 0)
  1158. scrollDown(screenAmount);
  1159. } //}}}
  1160. //{{{ scrollDown() method
  1161. // scroll down by screen line amount
  1162. void scrollDown(int amount)
  1163. {
  1164. if(Debug.SCROLL_DEBUG)
  1165. Log.log(Log.DEBUG,this,"scrollDown()");
  1166. ensurePhysicalLineIsVisible();
  1167. amount += skew;
  1168. skew = 0;
  1169. while(amount > 0)
  1170. {
  1171. int screenLines = getScreenLineCount(physicalLine);
  1172. if(amount < screenLines)
  1173. {
  1174. skew = amount;
  1175. break;
  1176. }
  1177. else
  1178. {
  1179. int nextLine = getNextVisibleLine(physicalLine);
  1180. if(nextLine == -1)
  1181. break;
  1182. boolean visible = isLineVisible(physicalLine);
  1183. physicalLine = nextLine;
  1184. if(visible)
  1185. {
  1186. amount -= screenLines;
  1187. scrollLine += screenLines;
  1188. }
  1189. }
  1190. }
  1191. } //}}}
  1192. //{{{ scrollUp() method
  1193. // scroll up by screen line amount
  1194. void scrollUp(int amount)
  1195. {
  1196. if(Debug.SCROLL_DEBUG)
  1197. Log.log(Log.DEBUG,this,"scrollUp()");
  1198. ensurePhysicalLineIsVisible();
  1199. if(amount <= skew)
  1200. {
  1201. skew -= amount;
  1202. }
  1203. else
  1204. {
  1205. amount -= skew;
  1206. skew = 0;
  1207. while(amount > 0)
  1208. {
  1209. int prevLine = getPrevVisibleLine(physicalLine);
  1210. if(prevLine == -1)
  1211. break;
  1212. physicalLine = prevLine;
  1213. int screenLines = getScreenLineCount(physicalLine);
  1214. scrollLine -= screenLines;
  1215. if(amount < screenLines)
  1216. {
  1217. skew = screenLines - amount;
  1218. break;
  1219. }
  1220. else
  1221. amount -= screenLines;
  1222. }
  1223. }
  1224. } //}}}
  1225. //{{{ ensurePhysicalLineIsVisible() method
  1226. private void ensurePhysicalLineIsVisible()
  1227. {
  1228. if(!isLineVisible(physicalLine))
  1229. {
  1230. if(physicalLine > getLastVisibleLine())
  1231. {
  1232. physicalLine = getPrevVisibleLine(physicalLine);
  1233. scrollLine -= getScreenLineCount(physicalLine);
  1234. }
  1235. else
  1236. {
  1237. physicalLine = getNextVisibleLine(physicalLine);
  1238. scrollLine += getScreenLineCount(physicalLine);
  1239. }
  1240. }
  1241. } //}}}
  1242. } //}}}
  1243. //{{{ BufferChangeHandler class
  1244. class BufferChangeHandler extends BufferChangeAdapter
  1245. {
  1246. boolean delayedUpdate;
  1247. int delayedUpdateStart;
  1248. int delayedUpdateEnd;
  1249. //{{{ foldHandlerChanged() method
  1250. public void foldHandlerChanged(Buffer buffer)
  1251. {
  1252. fvmreset();
  1253. firstLine.callReset = true;
  1254. scrollLineCount.callReset = true;
  1255. int collapseFolds = buffer.getIntegerProperty(
  1256. "collapseFolds",0);
  1257. if(collapseFolds != 0)
  1258. expandFolds(collapseFolds);
  1259. _notifyScreenLineChanges();
  1260. } //}}}
  1261. //{{{ wrapModeChanged() method
  1262. public void wrapModeChanged(Buffer buffer)
  1263. {
  1264. firstLine.reset();
  1265. scrollLineCount.reset();
  1266. } //}}}
  1267. //{{{ contentInserted() method
  1268. public void contentInserted(Buffer buffer, int startLine,
  1269. int offset, int numLines, int length)
  1270. {
  1271. if(!buffer.isLoaded())
  1272. return;
  1273. delayedUpdate(startLine,startLine + numLines);
  1274. if(numLines != 0)
  1275. {
  1276. contentInserted(firstLine,startLine,numLines);
  1277. contentInserted(scrollLineCount,startLine,numLines);
  1278. int index = fvmget(startLine);
  1279. for(int i = index + 1; i < fvmcount; i++)
  1280. fvm[i] += numLines;
  1281. lastfvmget = -1;
  1282. }
  1283. if(!buffer.isTransactionInProgress())
  1284. transactionComplete(buffer);
  1285. } //}}}
  1286. //{{{ preContentRemoved() method
  1287. public void preContentRemoved(Buffer buffer, int startLine,
  1288. int offset, int numLines, int length)
  1289. {
  1290. if(!buffer.isLoaded())
  1291. return;
  1292. if(numLines != 0)
  1293. {
  1294. preContentRemoved(firstLine,startLine,numLines);
  1295. preContentRemoved(scrollLineCount,startLine,numLines);
  1296. /* update fold visibility map. */
  1297. int starti = fvmget(startLine);
  1298. int endi = fvmget(startLine + numLines);
  1299. /* both have same visibility; just remove
  1300. * anything in between. */
  1301. if(Math.abs(starti % 2) == Math.abs(endi % 2))
  1302. {
  1303. if(endi - starti == fvmcount)
  1304. {
  1305. // we're removing from before
  1306. // the first visible to after
  1307. // the last visible
  1308. fvmreset();
  1309. firstLine.callReset = true;
  1310. scrollLineCount.callReset = true;
  1311. }
  1312. else
  1313. {
  1314. fvmput(starti + 1,endi + 1,null);
  1315. starti++;
  1316. }
  1317. }
  1318. else
  1319. {
  1320. fvmput(starti + 1,endi,null);
  1321. fvm[starti + 1] = startLine;
  1322. starti += 2;
  1323. }
  1324. for(int i = starti; i < fvmcount; i++)
  1325. fvm[i] -= numLines;
  1326. lastfvmget = -1;
  1327. }
  1328. } //}}}
  1329. //{{{ contentRemoved() method
  1330. public void contentRemoved(Buffer buffer, int startLine,
  1331. int offset, int numLines, int length)
  1332. {
  1333. if(!buffer.isLoaded())
  1334. return;
  1335. delayedUpdate(startLine,startLine);
  1336. if(!buffer.isTransactionInProgress())
  1337. transactionComplete(buffer);
  1338. } //}}}
  1339. //{{{ transactionComplete() method
  1340. public void transactionComplete(Buffer buffer)
  1341. {
  1342. if(delayedUpdate)
  1343. {
  1344. if(textArea.getDisplayManager() == DisplayManager.this)
  1345. {
  1346. int firstLine = textArea.getFirstPhysicalLine();
  1347. int lastLine = textArea.getLastPhysicalLine();
  1348. int line = delayedUpdateStart;
  1349. if(!isLineVisible(line))
  1350. line = getNextVisibleLine(line);
  1351. while(line != -1 && line <= delayedUpdateEnd)
  1352. {
  1353. if(line < firstLine
  1354. || line > lastLine)
  1355. {
  1356. getScreenLineCount(line);
  1357. }
  1358. line = getNextVisibleLine(line);
  1359. }
  1360. }
  1361. _notifyScreenLineChanges();
  1362. delayedUpdate = false;
  1363. }
  1364. } //}}}
  1365. //{{{ contentInserted() method
  1366. private void contentInserted(Anchor anchor, int startLine,
  1367. int numLines)
  1368. {
  1369. if(anchor.physicalLine >= startLine)
  1370. {
  1371. if(anchor.physicalLine != startLine)
  1372. anchor.physicalLine += numLines;
  1373. anchor.callChanged = true;
  1374. }
  1375. } //}}}
  1376. //{{{ preContentRemoved() method
  1377. private void preContentRemoved(Anchor anchor, int startLine,
  1378. int numLines)
  1379. {
  1380. if(anchor.physicalLine >= startLine)
  1381. {
  1382. if(anchor.physicalLine == startLine)
  1383. anchor.callChanged = true;
  1384. else
  1385. {
  1386. int end = Math.min(startLine + numLines,
  1387. anchor.physicalLine);
  1388. for(int i = startLine; i < end; i++)
  1389. {
  1390. //XXX
  1391. if(isLineVisible(i))
  1392. {
  1393. anchor.scrollLine -=
  1394. lineMgr
  1395. .getScreenLineCount(i);
  1396. }
  1397. }
  1398. anchor.physicalLine -= (end - startLine);
  1399. anchor.callChanged = true;
  1400. }
  1401. }
  1402. } //}}}
  1403. //{{{ delayedUpdate() method
  1404. private void delayedUpdate(int startLine, int endLine)
  1405. {
  1406. textArea.chunkCache.invalidateChunksFromPhys(startLine);
  1407. if(!delayedUpdate)
  1408. {
  1409. delayedUpdateStart = startLine;
  1410. delayedUpdateEnd = endLine;
  1411. delayedUpdate = true;
  1412. }
  1413. else
  1414. {
  1415. delayedUpdateStart = Math.min(
  1416. delayedUpdateStart,
  1417. startLine);
  1418. delayedUpdateEnd = Math.max(
  1419. delayedUpdateEnd,
  1420. endLine);
  1421. }
  1422. } //}}}
  1423. } //}}}
  1424. }