PageRenderTime 60ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
Java | 1942 lines | 1404 code | 219 blank | 319 comment | 340 complexity | 6a1ec83ce4117daa766edaf3e251c515 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, 2004 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 5057 2004-06-03 21:11:08Z 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. //{{{ _notifyScreenLineChanges() method
  98. /* static void _notifyScreenLineChanges(Buffer buffer)
  99. {
  100. Iterator iter = ((List)bufferMap.get(buffer)).iterator();
  101. while(iter.hasNext())
  102. {
  103. ((DisplayManager)iter.next())._notifyScreenLineChanges();
  104. }
  105. } */ //}}}
  106. private static Map bufferMap = new HashMap();
  107. //}}}
  108. //{{{ isLineVisible() method
  109. /**
  110. * Returns if the specified line is visible.
  111. * @param line A physical line index
  112. * @since jEdit 4.2pre1
  113. */
  114. public final boolean isLineVisible(int line)
  115. {
  116. return fvmget(line) % 2 == 0;
  117. } //}}}
  118. //{{{ getFirstVisibleLine() method
  119. /**
  120. * Returns the physical line number of the first visible line.
  121. * @since jEdit 4.2pre1
  122. */
  123. public int getFirstVisibleLine()
  124. {
  125. return fvm[0];
  126. } //}}}
  127. //{{{ getLastVisibleLine() method
  128. /**
  129. * Returns the physical line number of the last visible line.
  130. * @since jEdit 4.2pre1
  131. */
  132. public int getLastVisibleLine()
  133. {
  134. return fvm[fvmcount - 1] - 1;
  135. } //}}}
  136. //{{{ getNextVisibleLine() method
  137. /**
  138. * Returns the next visible line after the specified line index.
  139. * @param line A physical line index
  140. * @since jEdit 4.0pre1
  141. */
  142. public int getNextVisibleLine(int line)
  143. {
  144. if(line < 0 || line >= buffer.getLineCount())
  145. throw new ArrayIndexOutOfBoundsException(line);
  146. int index = fvmget(line);
  147. /* in collapsed range */
  148. if(index % 2 != 0)
  149. {
  150. /* beyond last visible line */
  151. if(fvmcount == index + 1)
  152. return - 1;
  153. /* start of next expanded range */
  154. else
  155. return fvm[index + 1];
  156. }
  157. /* last in expanded range */
  158. else if(line == fvm[index + 1] - 1)
  159. {
  160. /* equal to last visible line */
  161. if(fvmcount == index + 2)
  162. return -1;
  163. /* start of next expanded range */
  164. else
  165. return fvm[index + 2];
  166. }
  167. /* next in expanded range */
  168. else
  169. return line + 1;
  170. } //}}}
  171. //{{{ getPrevVisibleLine() method
  172. /**
  173. * Returns the previous visible line before the specified line index.
  174. * @param line A physical line index
  175. * @since jEdit 4.0pre1
  176. */
  177. public int getPrevVisibleLine(int line)
  178. {
  179. if(line < 0 || line >= buffer.getLineCount())
  180. throw new ArrayIndexOutOfBoundsException(line);
  181. int index = fvmget(line);
  182. /* before first visible line */
  183. if(index == -1)
  184. return -1;
  185. /* in collapsed range */
  186. else if(index % 2 == 1)
  187. {
  188. /* end of prev expanded range */
  189. return fvm[index] - 1;
  190. }
  191. /* first in expanded range */
  192. else if(line == fvm[index])
  193. {
  194. /* equal to first visible line */
  195. if(index == 0)
  196. return -1;
  197. /* end of prev expanded range */
  198. else
  199. return fvm[index - 1] - 1;
  200. }
  201. /* prev in expanded range */
  202. else
  203. return line - 1;
  204. } //}}}
  205. //{{{ getScreenLineCount() method
  206. public final int getScreenLineCount(int line)
  207. {
  208. if(lineMgr.isScreenLineCountValid(line))
  209. return lineMgr.getScreenLineCount(line);
  210. else
  211. {
  212. int newCount = textArea.chunkCache
  213. .getLineSubregionCount(line);
  214. setScreenLineCount(line,newCount);
  215. return newCount;
  216. }
  217. } //}}}
  218. //{{{ getScrollLineCount() method
  219. public final int getScrollLineCount()
  220. {
  221. return scrollLineCount.scrollLine;
  222. } //}}}
  223. //{{{ collapseFold() method
  224. /**
  225. * Collapses the fold at the specified physical line index.
  226. * @param line A physical line index
  227. * @since jEdit 4.2pre1
  228. */
  229. public void collapseFold(int line)
  230. {
  231. int lineCount = buffer.getLineCount();
  232. int start = 0;
  233. int end = lineCount - 1;
  234. // if the caret is on a collapsed fold, collapse the
  235. // parent fold
  236. if(line != 0
  237. && line != buffer.getLineCount() - 1
  238. && buffer.isFoldStart(line)
  239. && !isLineVisible(line + 1))
  240. {
  241. line--;
  242. }
  243. int initialFoldLevel = buffer.getFoldLevel(line);
  244. //{{{ Find fold start and end...
  245. if(line != lineCount - 1
  246. && buffer.getFoldLevel(line + 1) > initialFoldLevel)
  247. {
  248. // this line is the start of a fold
  249. start = line + 1;
  250. for(int i = line + 1; i < lineCount; i++)
  251. {
  252. if(buffer.getFoldLevel(i) <= initialFoldLevel)
  253. {
  254. end = i - 1;
  255. break;
  256. }
  257. }
  258. }
  259. else
  260. {
  261. boolean ok = false;
  262. // scan backwards looking for the start
  263. for(int i = line - 1; i >= 0; i--)
  264. {
  265. if(buffer.getFoldLevel(i) < initialFoldLevel)
  266. {
  267. start = i + 1;
  268. ok = true;
  269. break;
  270. }
  271. }
  272. if(!ok)
  273. {
  274. // no folds in buffer
  275. return;
  276. }
  277. for(int i = line + 1; i < lineCount; i++)
  278. {
  279. if(buffer.getFoldLevel(i) < initialFoldLevel)
  280. {
  281. end = i - 1;
  282. break;
  283. }
  284. }
  285. } //}}}
  286. // Collapse the fold...
  287. hideLineRange(start,end);
  288. _notifyScreenLineChanges();
  289. textArea.foldStructureChanged();
  290. } //}}}
  291. //{{{ expandFold() method
  292. /**
  293. * Expands the fold at the specified physical line index.
  294. * @param line A physical line index
  295. * @param fully If true, all subfolds will also be expanded
  296. * @since jEdit 4.2pre1
  297. */
  298. public int expandFold(int line, boolean fully)
  299. {
  300. // the first sub-fold. used by JEditTextArea.expandFold().
  301. int returnValue = -1;
  302. int lineCount = buffer.getLineCount();
  303. int start = 0;
  304. int end = lineCount - 1;
  305. int initialFoldLevel = buffer.getFoldLevel(line);
  306. //{{{ Find fold start and fold end...
  307. if(line != lineCount - 1
  308. && isLineVisible(line)
  309. && !isLineVisible(line + 1)
  310. && buffer.getFoldLevel(line + 1) > initialFoldLevel)
  311. {
  312. // this line is the start of a fold
  313. int index = fvmget(line + 1);
  314. if(index == -1)
  315. {
  316. expandAllFolds();
  317. return -1;
  318. }
  319. start = fvm[index];
  320. if(index != fvmcount - 1)
  321. end = fvm[index + 1] - 1;
  322. else
  323. {
  324. start = line + 1;
  325. for(int i = line + 1; i < lineCount; i++)
  326. {
  327. if(/* isLineVisible(i) && */
  328. buffer.getFoldLevel(i) <= initialFoldLevel)
  329. {
  330. end = i - 1;
  331. break;
  332. }
  333. }
  334. }
  335. }
  336. else
  337. {
  338. int index = fvmget(line);
  339. if(index == -1)
  340. {
  341. expandAllFolds();
  342. return -1;
  343. }
  344. start = fvm[index];
  345. if(index != fvmcount - 1)
  346. end = fvm[index + 1] - 1;
  347. else
  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. } //}}}
  362. //{{{ Expand the fold...
  363. if(fully)
  364. {
  365. showLineRange(start,end);
  366. }
  367. else
  368. {
  369. // we need a different value of initialFoldLevel here!
  370. initialFoldLevel = buffer.getFoldLevel(start);
  371. int firstVisible = start;
  372. for(int i = start; i <= end; i++)
  373. {
  374. if(buffer.getFoldLevel(i) > initialFoldLevel)
  375. {
  376. if(returnValue == -1
  377. && i != 0
  378. && buffer.isFoldStart(i - 1))
  379. {
  380. returnValue = i - 1;
  381. }
  382. if(firstVisible != i)
  383. {
  384. showLineRange(firstVisible,i - 1);
  385. }
  386. firstVisible = i + 1;
  387. }
  388. }
  389. if(firstVisible != end + 1)
  390. showLineRange(firstVisible,end);
  391. if(!isLineVisible(line))
  392. {
  393. // this is a hack, and really needs to be done better.
  394. expandFold(line,false);
  395. return returnValue;
  396. }
  397. } //}}}
  398. _notifyScreenLineChanges();
  399. textArea.foldStructureChanged();
  400. return returnValue;
  401. } //}}}
  402. //{{{ expandAllFolds() method
  403. /**
  404. * Expands all folds.
  405. * @since jEdit 4.2pre1
  406. */
  407. public void expandAllFolds()
  408. {
  409. showLineRange(0,buffer.getLineCount() - 1);
  410. _notifyScreenLineChanges();
  411. textArea.foldStructureChanged();
  412. } //}}}
  413. //{{{ expandFolds() method
  414. /**
  415. * This method should only be called from <code>actions.xml</code>.
  416. * @since jEdit 4.2pre1
  417. */
  418. public void expandFolds(char digit)
  419. {
  420. if(digit < '1' || digit > '9')
  421. {
  422. Toolkit.getDefaultToolkit().beep();
  423. return;
  424. }
  425. else
  426. expandFolds((int)(digit - '1') + 1);
  427. } //}}}
  428. //{{{ expandFolds() method
  429. /**
  430. * Expands all folds with the specified fold level.
  431. * @param foldLevel The fold level
  432. * @since jEdit 4.2pre1
  433. */
  434. public void expandFolds(int foldLevel)
  435. {
  436. if(buffer.getFoldHandler() instanceof IndentFoldHandler)
  437. foldLevel = (foldLevel - 1) * buffer.getIndentSize() + 1;
  438. showLineRange(0,buffer.getLineCount() - 1);
  439. /* this ensures that the first line is always visible */
  440. boolean seenVisibleLine = false;
  441. int firstInvisible = 0;
  442. for(int i = 0; i < buffer.getLineCount(); i++)
  443. {
  444. if(!seenVisibleLine || buffer.getFoldLevel(i) < foldLevel)
  445. {
  446. if(firstInvisible != i)
  447. {
  448. hideLineRange(firstInvisible,
  449. i - 1);
  450. }
  451. firstInvisible = i + 1;
  452. seenVisibleLine = true;
  453. }
  454. }
  455. if(firstInvisible != buffer.getLineCount())
  456. hideLineRange(firstInvisible,buffer.getLineCount() - 1);
  457. _notifyScreenLineChanges();
  458. textArea.foldStructureChanged();
  459. } //}}}
  460. //{{{ narrow() method
  461. /**
  462. * Narrows the visible portion of the buffer to the specified
  463. * line range.
  464. * @param start The first line
  465. * @param end The last line
  466. * @since jEdit 4.2pre1
  467. */
  468. public void narrow(int start, int end)
  469. {
  470. if(start > end || start < 0 || end >= buffer.getLineCount())
  471. throw new ArrayIndexOutOfBoundsException(start + ", " + end);
  472. if(start < getFirstVisibleLine() || end > getLastVisibleLine())
  473. expandAllFolds();
  474. if(start != 0)
  475. hideLineRange(0,start - 1);
  476. if(end != buffer.getLineCount() - 1)
  477. hideLineRange(end + 1,buffer.getLineCount() - 1);
  478. // if we narrowed to a single collapsed fold
  479. if(start != buffer.getLineCount() - 1
  480. && !isLineVisible(start + 1))
  481. expandFold(start,false);
  482. // Hack... need a more direct way of obtaining a view?
  483. // JEditTextArea.getView() method?
  484. GUIUtilities.getView(textArea).getStatus().setMessageAndClear(
  485. jEdit.getProperty("view.status.narrow"));
  486. _notifyScreenLineChanges();
  487. textArea.foldStructureChanged();
  488. } //}}}
  489. //{{{ Package-private members
  490. boolean softWrap;
  491. int wrapMargin;
  492. FirstLine firstLine;
  493. ScrollLineCount scrollLineCount;
  494. //{{{ init() method
  495. void init()
  496. {
  497. if(!initialized)
  498. {
  499. initialized = true;
  500. fvm = new int[2];
  501. if(buffer.isLoaded())
  502. bufferChangeHandler.foldHandlerChanged(buffer);
  503. else
  504. fvmreset();
  505. _notifyScreenLineChanges();
  506. }
  507. else
  508. {
  509. updateWrapSettings();
  510. if(buffer.isLoaded())
  511. {
  512. _notifyScreenLineChanges();
  513. textArea.updateScrollBars();
  514. textArea.recalculateLastPhysicalLine();
  515. }
  516. }
  517. } //}}}
  518. //{{{ setScreenLineCount() method
  519. /**
  520. * Sets the number of screen lines that the specified physical line
  521. * is split into.
  522. * @since jEdit 4.2pre1
  523. */
  524. void setScreenLineCount(int line, int count)
  525. {
  526. int oldCount = lineMgr.getScreenLineCount(line);
  527. // still have to call this even if it equals the
  528. // old one so that the offset manager sets the
  529. // validity flag!
  530. lineMgr.setScreenLineCount(line,count);
  531. // this notifies each display manager editing this
  532. // buffer of the screen line count change
  533. if(count != oldCount)
  534. {
  535. Iterator iter = ((List)bufferMap.get(buffer))
  536. .iterator();
  537. while(iter.hasNext())
  538. {
  539. ((DisplayManager)iter.next())._setScreenLineCount(
  540. line,oldCount,count);
  541. }
  542. }
  543. } //}}}
  544. //{{{ updateWrapSettings() method
  545. void updateWrapSettings()
  546. {
  547. String wrap = buffer.getStringProperty("wrap");
  548. softWrap = wrap.equals("soft");
  549. if(textArea.maxLineLen <= 0)
  550. {
  551. softWrap = false;
  552. wrapMargin = 0;
  553. }
  554. else
  555. {
  556. // stupidity
  557. char[] foo = new char[textArea.maxLineLen];
  558. for(int i = 0; i < foo.length; i++)
  559. {
  560. foo[i] = ' ';
  561. }
  562. TextAreaPainter painter = textArea.getPainter();
  563. wrapMargin = (int)painter.getFont().getStringBounds(
  564. foo,0,foo.length,
  565. painter.getFontRenderContext())
  566. .getWidth();
  567. }
  568. } //}}}
  569. //{{{ _notifyScreenLineChanges() method
  570. void _notifyScreenLineChanges()
  571. {
  572. if(Debug.SCROLL_DEBUG)
  573. Log.log(Log.DEBUG,this,"_notifyScreenLineChanges()");
  574. // when the text area switches to us, it will do
  575. // a reset anyway
  576. if(textArea.getDisplayManager() == this)
  577. {
  578. try
  579. {
  580. if(firstLine.callReset)
  581. firstLine.reset();
  582. else if(firstLine.callChanged)
  583. firstLine.changed();
  584. if(scrollLineCount.callReset)
  585. scrollLineCount.reset();
  586. else if(scrollLineCount.callChanged)
  587. scrollLineCount.changed();
  588. }
  589. finally
  590. {
  591. firstLine.callReset = firstLine.callChanged = false;
  592. scrollLineCount.callReset = scrollLineCount.callChanged = false;
  593. }
  594. }
  595. } //}}}
  596. //}}}
  597. //{{{ Private members
  598. private boolean initialized;
  599. private boolean inUse;
  600. private Buffer buffer;
  601. private LineManager lineMgr;
  602. private JEditTextArea textArea;
  603. private BufferChangeHandler bufferChangeHandler;
  604. /**
  605. * The fold visibility map.
  606. *
  607. * All lines from fvm[2*n] to fvm[2*n+1]-1 inclusive are visible.
  608. * All lines from position fvm[2*n+1] to fvm[2*n+2]-1 inclusive are
  609. * invisible.
  610. *
  611. * Examples:
  612. * ---------
  613. * All lines visible: { 0, buffer.getLineCount() }
  614. * Narrow from a to b: { a, b + 1 }
  615. * Collapsed fold from a to b: { 0, a + 1, b, buffer.getLineCount() }
  616. *
  617. * Note: length is always even.
  618. */
  619. private int[] fvm;
  620. private int fvmcount;
  621. private int lastfvmget = -1;
  622. //{{{ DisplayManager constructor
  623. private DisplayManager(Buffer buffer, JEditTextArea textArea)
  624. {
  625. this.buffer = buffer;
  626. this.lineMgr = buffer._getLineManager();
  627. this.textArea = textArea;
  628. scrollLineCount = new ScrollLineCount();
  629. firstLine = new FirstLine();
  630. bufferChangeHandler = new BufferChangeHandler();
  631. // this listener priority thing is a bad hack...
  632. buffer.addBufferChangeListener(bufferChangeHandler,
  633. Buffer.HIGH_PRIORITY);
  634. } //}}}
  635. //{{{ dispose() method
  636. private void dispose()
  637. {
  638. buffer.removeBufferChangeListener(bufferChangeHandler);
  639. } //}}}
  640. //{{{ fvmreset() method
  641. private void fvmreset()
  642. {
  643. lastfvmget = -1;
  644. fvmcount = 2;
  645. fvm[0] = 0;
  646. fvm[1] = buffer.getLineCount();
  647. } //}}}
  648. //{{{ fvmget() method
  649. /**
  650. * Returns the fold visibility map index for the given line.
  651. */
  652. private int fvmget(int line)
  653. {
  654. scanCount++;
  655. if(line < fvm[0])
  656. return -1;
  657. if(line >= fvm[fvmcount - 1])
  658. return fvmcount - 1;
  659. if(lastfvmget != -1)
  660. {
  661. if(line >= fvm[lastfvmget])
  662. {
  663. if(lastfvmget == fvmcount - 1
  664. || line < fvm[lastfvmget + 1])
  665. {
  666. return lastfvmget;
  667. }
  668. }
  669. }
  670. int start = 0;
  671. int end = fvmcount - 1;
  672. loop: for(;;)
  673. {
  674. scannedLines++;
  675. switch(end - start)
  676. {
  677. case 0:
  678. lastfvmget = start;
  679. break loop;
  680. case 1:
  681. int value = fvm[end];
  682. if(value <= line)
  683. lastfvmget = end;
  684. else
  685. lastfvmget = start;
  686. break loop;
  687. default:
  688. int pivot = (end + start) / 2;
  689. value = fvm[pivot];
  690. if(value == line)
  691. {
  692. lastfvmget = pivot;
  693. break loop;
  694. }
  695. else if(value < line)
  696. start = pivot;
  697. else
  698. end = pivot - 1;
  699. break;
  700. }
  701. }
  702. return lastfvmget;
  703. } //}}}
  704. //{{{ fvmput() method
  705. /**
  706. * Replaces from <code>start</code> to <code>end-1</code> inclusive with
  707. * <code>put</code>. Update <code>fvmcount</code>.
  708. */
  709. private void fvmput(int start, int end, int[] put)
  710. {
  711. if(Debug.FOLD_VIS_DEBUG)
  712. {
  713. StringBuffer buf = new StringBuffer("{");
  714. if(put != null)
  715. {
  716. for(int i = 0; i < put.length; i++)
  717. {
  718. if(i != 0)
  719. buf.append(',');
  720. buf.append(put[i]);
  721. }
  722. }
  723. buf.append("}");
  724. Log.log(Log.DEBUG,this,"fvmput(" + start + ","
  725. + end + "," + buf + ")");
  726. }
  727. int putl = (put == null ? 0 : put.length);
  728. int delta = putl - (end - start);
  729. if(fvmcount + delta > fvm.length)
  730. {
  731. int[] newfvm = new int[fvm.length * 2 + 1];
  732. System.arraycopy(fvm,0,newfvm,0,fvmcount);
  733. fvm = newfvm;
  734. }
  735. if(delta != 0)
  736. {
  737. System.arraycopy(fvm,end,fvm,start + putl,
  738. fvmcount - end);
  739. }
  740. if(putl != 0)
  741. {
  742. System.arraycopy(put,0,fvm,start,put.length);
  743. }
  744. fvmcount += delta;
  745. fvmdump();
  746. if(fvmcount == 0)
  747. throw new InternalError();
  748. } //}}}
  749. //{{{ fvmput2() method
  750. /**
  751. * Merge previous and next entry if necessary.
  752. */
  753. private void fvmput2(int starti, int endi, int start, int end)
  754. {
  755. if(Debug.FOLD_VIS_DEBUG)
  756. {
  757. Log.log(Log.DEBUG,this,"*fvmput2(" + starti + ","
  758. + endi + "," + start + "," + end + ")");
  759. }
  760. if(starti != -1 && fvm[starti] == start)
  761. {
  762. if(endi <= fvmcount - 2 && fvm[endi + 1]
  763. == end + 1)
  764. {
  765. fvmput(starti,endi + 2,null);
  766. }
  767. else
  768. {
  769. fvmput(starti,endi + 1,
  770. new int[] { end + 1 });
  771. }
  772. }
  773. else
  774. {
  775. if(endi != fvmcount - 1 && fvm[endi + 1]
  776. == end + 1)
  777. {
  778. fvmput(starti + 1,endi + 2,
  779. new int[] { start });
  780. }
  781. else
  782. {
  783. fvmput(starti + 1,endi + 1,
  784. new int[] { start,
  785. end + 1 });
  786. }
  787. }
  788. } //}}}
  789. //{{{ fvmdump() method
  790. private void fvmdump()
  791. {
  792. if(Debug.FOLD_VIS_DEBUG)
  793. {
  794. StringBuffer buf = new StringBuffer("{");
  795. for(int i = 0; i < fvmcount; i++)
  796. {
  797. if(i != 0)
  798. buf.append(',');
  799. buf.append(fvm[i]);
  800. }
  801. buf.append("}");
  802. Log.log(Log.DEBUG,this,"fvm = " + buf);
  803. }
  804. } //}}}
  805. //{{{ showLineRange() method
  806. private void showLineRange(int start, int end)
  807. {
  808. if(Debug.FOLD_VIS_DEBUG)
  809. {
  810. Log.log(Log.DEBUG,this,"showLineRange(" + start
  811. + "," + end + ")");
  812. }
  813. for(int i = start; i <= end; i++)
  814. {
  815. //XXX
  816. if(!isLineVisible(i))
  817. {
  818. // important: not lineMgr.getScreenLineCount()
  819. int screenLines = getScreenLineCount(i);
  820. if(firstLine.physicalLine >= i)
  821. {
  822. firstLine.scrollLine += screenLines;
  823. firstLine.callChanged = true;
  824. }
  825. scrollLineCount.scrollLine += screenLines;
  826. scrollLineCount.callChanged = true;
  827. }
  828. }
  829. /* update fold visibility map. */
  830. int starti = fvmget(start);
  831. int endi = fvmget(end);
  832. if(starti % 2 == 0)
  833. {
  834. if(endi % 2 == 0)
  835. fvmput(starti + 1,endi + 1,null);
  836. else
  837. {
  838. if(endi != fvmcount - 1
  839. && fvm[endi + 1] == end + 1)
  840. fvmput(starti + 1,endi + 2,null);
  841. else
  842. {
  843. fvmput(starti + 1,endi,null);
  844. fvm[starti + 1] = end + 1;
  845. }
  846. }
  847. }
  848. else
  849. {
  850. if(endi % 2 == 0)
  851. {
  852. if(starti != -1 && fvm[starti] == start)
  853. fvmput(starti,endi + 1,null);
  854. else
  855. {
  856. fvmput(starti + 1,endi,null);
  857. fvm[starti + 1] = start;
  858. }
  859. }
  860. else
  861. fvmput2(starti,endi,start,end);
  862. }
  863. lastfvmget = -1;
  864. } //}}}
  865. //{{{ hideLineRange() method
  866. private void hideLineRange(int start, int end)
  867. {
  868. if(Debug.FOLD_VIS_DEBUG)
  869. {
  870. Log.log(Log.DEBUG,this,"hideLineRange(" + start
  871. + "," + end + ")");
  872. }
  873. int i = start;
  874. if(!isLineVisible(i))
  875. i = getNextVisibleLine(i);
  876. while(i != -1 && i <= end)
  877. {
  878. int screenLines = lineMgr.getScreenLineCount(i);
  879. if(i < firstLine.physicalLine)
  880. {
  881. firstLine.scrollLine -= screenLines;
  882. firstLine.skew = 0;
  883. firstLine.callChanged = true;
  884. }
  885. scrollLineCount.scrollLine -= screenLines;
  886. scrollLineCount.callChanged = true;
  887. i = getNextVisibleLine(i);
  888. }
  889. /* update fold visibility map. */
  890. int starti = fvmget(start);
  891. int endi = fvmget(end);
  892. if(starti % 2 == 0)
  893. {
  894. if(endi % 2 == 0)
  895. fvmput2(starti,endi,start,end);
  896. else
  897. {
  898. if(start == fvm[0])
  899. fvmput(starti,endi + 1,null);
  900. else
  901. {
  902. fvmput(starti + 1,endi,null);
  903. fvm[starti + 1] = start;
  904. }
  905. }
  906. }
  907. else
  908. {
  909. if(endi % 2 == 0)
  910. {
  911. if(end + 1 == fvm[fvmcount - 1])
  912. fvmput(starti + 1,endi + 2,null);
  913. else
  914. {
  915. fvmput(starti + 1,endi,null);
  916. fvm[starti + 1] = end + 1;
  917. }
  918. }
  919. else
  920. fvmput(starti + 1,endi + 1,null);
  921. }
  922. lastfvmget = -1;
  923. if(!isLineVisible(firstLine.physicalLine))
  924. {
  925. int firstVisible = getFirstVisibleLine();
  926. if(firstLine.physicalLine < firstVisible)
  927. {
  928. firstLine.physicalLine = firstVisible;
  929. firstLine.scrollLine = 0;
  930. }
  931. else
  932. {
  933. firstLine.physicalLine = getPrevVisibleLine(
  934. firstLine.physicalLine);
  935. firstLine.scrollLine -=
  936. lineMgr.getScreenLineCount(
  937. firstLine.physicalLine);
  938. }
  939. firstLine.callChanged = true;
  940. }
  941. } //}}}
  942. //{{{ _setScreenLineCount() method
  943. private void _setScreenLineCount(int line, int oldCount, int count)
  944. {
  945. if(!isLineVisible(line))
  946. return;
  947. if(firstLine.physicalLine >= line)
  948. {
  949. if(firstLine.physicalLine == line)
  950. firstLine.callChanged = true;
  951. else
  952. {
  953. firstLine.scrollLine += (count - oldCount);
  954. firstLine.callChanged = true;
  955. }
  956. }
  957. scrollLineCount.scrollLine += (count - oldCount);
  958. scrollLineCount.callChanged = true;
  959. } //}}}
  960. //}}}
  961. //{{{ Anchor class
  962. static abstract class Anchor
  963. {
  964. int physicalLine;
  965. int scrollLine;
  966. boolean callChanged;
  967. boolean callReset;
  968. abstract void reset();
  969. abstract void changed();
  970. public String toString()
  971. {
  972. return getClass().getName() + "[" + physicalLine + ","
  973. + scrollLine + "]";
  974. }
  975. } //}}}
  976. //{{{ ScrollLineCount class
  977. class ScrollLineCount extends Anchor
  978. {
  979. //{{{ changed() method
  980. public void changed()
  981. {
  982. if(Debug.SCROLL_DEBUG)
  983. Log.log(Log.DEBUG,this,"changed()");
  984. textArea.updateScrollBars();
  985. textArea.recalculateLastPhysicalLine();
  986. } //}}}
  987. //{{{ reset() method
  988. public void reset()
  989. {
  990. if(Debug.SCROLL_DEBUG)
  991. Log.log(Log.DEBUG,this,"reset()");
  992. physicalLine = getFirstVisibleLine();
  993. scrollLine = 0;
  994. while(physicalLine != -1)
  995. {
  996. scrollLine += getScreenLineCount(physicalLine);
  997. physicalLine = getNextVisibleLine(physicalLine);
  998. }
  999. physicalLine = buffer.getLineCount();
  1000. firstLine.ensurePhysicalLineIsVisible();
  1001. textArea.recalculateLastPhysicalLine();
  1002. textArea.updateScrollBars();
  1003. } //}}}
  1004. } //}}}
  1005. //{{{ FirstLine class
  1006. class FirstLine extends Anchor
  1007. {
  1008. int skew;
  1009. //{{{ changed() method
  1010. public void changed()
  1011. {
  1012. //{{{ Debug code
  1013. if(Debug.SCROLL_DEBUG)
  1014. {
  1015. Log.log(Log.DEBUG,this,"changed() before: "
  1016. + physicalLine + ":" + scrollLine);
  1017. } //}}}
  1018. ensurePhysicalLineIsVisible();
  1019. int screenLines = getScreenLineCount(physicalLine);
  1020. if(skew >= screenLines)
  1021. skew = screenLines - 1;
  1022. //{{{ Debug code
  1023. if(Debug.SCROLL_VERIFY)
  1024. {
  1025. int verifyScrollLine = 0;
  1026. for(int i = 0; i < buffer.getLineCount(); i++)
  1027. {
  1028. if(!isLineVisible(i))
  1029. continue;
  1030. if(i >= physicalLine)
  1031. break;
  1032. verifyScrollLine += getScreenLineCount(i);
  1033. }
  1034. if(verifyScrollLine != scrollLine)
  1035. {
  1036. Exception ex = new Exception(scrollLine + ":" + verifyScrollLine);
  1037. Log.log(Log.ERROR,this,ex);
  1038. new org.gjt.sp.jedit.gui.BeanShellErrorDialog(null,ex);
  1039. }
  1040. }
  1041. if(Debug.SCROLL_DEBUG)
  1042. {
  1043. Log.log(Log.DEBUG,this,"changed() after: "
  1044. + physicalLine + ":" + scrollLine);
  1045. } //}}}
  1046. if(!scrollLineCount.callChanged
  1047. && !scrollLineCount.callReset)
  1048. {
  1049. textArea.updateScrollBars();
  1050. textArea.recalculateLastPhysicalLine();
  1051. }
  1052. else
  1053. {
  1054. // ScrollLineCount.changed() does the same
  1055. // thing
  1056. }
  1057. } //}}}
  1058. //{{{ reset() method
  1059. public void reset()
  1060. {
  1061. if(Debug.SCROLL_DEBUG)
  1062. Log.log(Log.DEBUG,this,"reset()");
  1063. String wrap = buffer.getStringProperty("wrap");
  1064. softWrap = wrap.equals("soft");
  1065. if(textArea.maxLineLen <= 0)
  1066. {
  1067. softWrap = false;
  1068. wrapMargin = 0;
  1069. }
  1070. else
  1071. {
  1072. // stupidity
  1073. char[] foo = new char[textArea.maxLineLen];
  1074. for(int i = 0; i < foo.length; i++)
  1075. {
  1076. foo[i] = ' ';
  1077. }
  1078. TextAreaPainter painter = textArea.getPainter();
  1079. wrapMargin = (int)painter.getFont().getStringBounds(
  1080. foo,0,foo.length,
  1081. painter.getFontRenderContext())
  1082. .getWidth();
  1083. }
  1084. scrollLine = 0;
  1085. int i = getFirstVisibleLine();
  1086. for(;;)
  1087. {
  1088. if(i >= physicalLine)
  1089. break;
  1090. scrollLine += getScreenLineCount(i);
  1091. int nextLine = getNextVisibleLine(i);
  1092. if(nextLine == -1)
  1093. break;
  1094. else
  1095. i = nextLine;
  1096. }
  1097. physicalLine = i;
  1098. int screenLines = getScreenLineCount(physicalLine);
  1099. if(skew >= screenLines)
  1100. skew = screenLines - 1;
  1101. textArea.updateScrollBars();
  1102. } //}}}
  1103. //{{{ physDown() method
  1104. // scroll down by physical line amount
  1105. void physDown(int amount, int screenAmount)
  1106. {
  1107. if(Debug.SCROLL_DEBUG)
  1108. {
  1109. Log.log(Log.DEBUG,this,"physDown() start: "
  1110. + physicalLine + ":" + scrollLine);
  1111. }
  1112. skew = 0;
  1113. if(!isLineVisible(physicalLine))
  1114. {
  1115. int lastVisibleLine = getLastVisibleLine();
  1116. if(physicalLine > lastVisibleLine)
  1117. physicalLine = lastVisibleLine;
  1118. else
  1119. {
  1120. int nextPhysicalLine = getNextVisibleLine(physicalLine);
  1121. amount -= (nextPhysicalLine - physicalLine);
  1122. scrollLine += getScreenLineCount(physicalLine);
  1123. physicalLine = nextPhysicalLine;
  1124. }
  1125. }
  1126. for(;;)
  1127. {
  1128. int nextPhysicalLine = getNextVisibleLine(
  1129. physicalLine);
  1130. if(nextPhysicalLine == -1)
  1131. break;
  1132. else if(nextPhysicalLine > physicalLine + amount)
  1133. break;
  1134. else
  1135. {
  1136. scrollLine += getScreenLineCount(physicalLine);
  1137. amount -= (nextPhysicalLine - physicalLine);
  1138. physicalLine = nextPhysicalLine;
  1139. }
  1140. }
  1141. if(Debug.SCROLL_DEBUG)
  1142. {
  1143. Log.log(Log.DEBUG,this,"physDown() end: "
  1144. + physicalLine + ":" + scrollLine);
  1145. }
  1146. callChanged = true;
  1147. // JEditTextArea.scrollTo() needs this to simplify
  1148. // its code
  1149. if(screenAmount < 0)
  1150. scrollUp(-screenAmount);
  1151. else if(screenAmount > 0)
  1152. scrollDown(screenAmount);
  1153. } //}}}
  1154. //{{{ physUp() method
  1155. // scroll up by physical line amount
  1156. void physUp(int amount, int screenAmount)
  1157. {
  1158. if(Debug.SCROLL_DEBUG)
  1159. {
  1160. Log.log(Log.DEBUG,this,"physUp() start: "
  1161. + physicalLine + ":" + scrollLine);
  1162. }
  1163. skew = 0;
  1164. if(!isLineVisible(physicalLine))
  1165. {
  1166. int firstVisibleLine = getFirstVisibleLine();
  1167. if(physicalLine < firstVisibleLine)
  1168. physicalLine = firstVisibleLine;
  1169. else
  1170. {
  1171. int prevPhysicalLine = getPrevVisibleLine(physicalLine);
  1172. amount -= (physicalLine - prevPhysicalLine);
  1173. }
  1174. }
  1175. for(;;)
  1176. {
  1177. int prevPhysicalLine = getPrevVisibleLine(
  1178. physicalLine);
  1179. if(prevPhysicalLine == -1)
  1180. break;
  1181. else if(prevPhysicalLine < physicalLine - amount)
  1182. break;
  1183. else
  1184. {
  1185. amount -= (physicalLine - prevPhysicalLine);
  1186. physicalLine = prevPhysicalLine;
  1187. scrollLine -= getScreenLineCount(
  1188. prevPhysicalLine);
  1189. }
  1190. }
  1191. if(Debug.SCROLL_DEBUG)
  1192. {
  1193. Log.log(Log.DEBUG,this,"physUp() end: "
  1194. + physicalLine + ":" + scrollLine);
  1195. }
  1196. callChanged = true;
  1197. // JEditTextArea.scrollTo() needs this to simplify
  1198. // its code
  1199. if(screenAmount < 0)
  1200. scrollUp(-screenAmount);
  1201. else if(screenAmount > 0)
  1202. scrollDown(screenAmount);
  1203. } //}}}
  1204. //{{{ scrollDown() method
  1205. // scroll down by screen line amount
  1206. void scrollDown(int amount)
  1207. {
  1208. if(Debug.SCROLL_DEBUG)
  1209. Log.log(Log.DEBUG,this,"scrollDown()");
  1210. ensurePhysicalLineIsVisible();
  1211. amount += skew;
  1212. skew = 0;
  1213. while(amount > 0)
  1214. {
  1215. int screenLines = getScreenLineCount(physicalLine);
  1216. if(amount < screenLines)
  1217. {
  1218. skew = amount;
  1219. break;
  1220. }
  1221. else
  1222. {
  1223. int nextLine = getNextVisibleLine(physicalLine);
  1224. if(nextLine == -1)
  1225. break;
  1226. boolean visible = isLineVisible(physicalLine);
  1227. physicalLine = nextLine;
  1228. if(visible)
  1229. {
  1230. amount -= screenLines;
  1231. scrollLine += screenLines;
  1232. }
  1233. }
  1234. }
  1235. callChanged = true;
  1236. } //}}}
  1237. //{{{ scrollUp() method
  1238. // scroll up by screen line amount
  1239. void scrollUp(int amount)
  1240. {
  1241. if(Debug.SCROLL_DEBUG)
  1242. Log.log(Log.DEBUG,this,"scrollUp()");
  1243. ensurePhysicalLineIsVisible();
  1244. if(amount <= skew)
  1245. {
  1246. skew -= amount;
  1247. }
  1248. else
  1249. {
  1250. amount -= skew;
  1251. skew = 0;
  1252. while(amount > 0)
  1253. {
  1254. int prevLine = getPrevVisibleLine(physicalLine);
  1255. if(prevLine == -1)
  1256. break;
  1257. physicalLine = prevLine;
  1258. int screenLines = getScreenLineCount(physicalLine);
  1259. scrollLine -= screenLines;
  1260. if(amount < screenLines)
  1261. {
  1262. skew = screenLines - amount;
  1263. break;
  1264. }
  1265. else
  1266. amount -= screenLines;
  1267. }
  1268. }
  1269. callChanged = true;
  1270. } //}}}
  1271. //{{{ ensurePhysicalLineIsVisible() method
  1272. private void ensurePhysicalLineIsVisible()
  1273. {
  1274. if(!isLineVisible(physicalLine))
  1275. {
  1276. if(physicalLine > getLastVisibleLine())
  1277. {
  1278. physicalLine = getLastVisibleLine();
  1279. scrollLine = getScrollLineCount() - 1;
  1280. }
  1281. else if(physicalLine < getFirstVisibleLine())
  1282. {
  1283. physicalLine = getFirstVisibleLine();
  1284. scrollLine = 0;
  1285. }
  1286. else
  1287. {
  1288. physicalLine = getNextVisibleLine(physicalLine);
  1289. scrollLine += getScreenLineCount(physicalLine);
  1290. }
  1291. }
  1292. } //}}}
  1293. } //}}}
  1294. //{{{ BufferChangeHandler class
  1295. /**
  1296. * Note that in this class we take great care to defer complicated
  1297. * calculations to the end of the current transaction if the buffer
  1298. * informs us a compound edit is in progress
  1299. * (<code>isTransactionInProgress()</code>).
  1300. *
  1301. * This greatly speeds up replace all for example, by only doing certain
  1302. * things once, particularly in <code>moveCaretPosition()</code>.
  1303. *
  1304. * Try doing a replace all in a large file, for example. It is very slow
  1305. * in 3.2, faster in 4.0 (where the transaction optimization was
  1306. * introduced) and faster still in 4.1 (where it was further improved).
  1307. *
  1308. * There is still work to do; see TODO.txt.
  1309. */
  1310. class BufferChangeHandler extends BufferChangeAdapter
  1311. {
  1312. boolean delayedUpdate;
  1313. boolean delayedMultilineUpdate;
  1314. int delayedUpdateStart;
  1315. int delayedUpdateEnd;
  1316. //{{{ foldHandlerChanged() method
  1317. public void foldHandlerChanged(Buffer buffer)
  1318. {
  1319. fvmreset();
  1320. firstLine.callReset = true;
  1321. scrollLineCount.callReset = true;
  1322. int collapseFolds = buffer.getIntegerProperty(
  1323. "collapseFolds",0);
  1324. if(collapseFolds != 0)
  1325. expandFolds(collapseFolds);
  1326. _notifyScreenLineChanges();
  1327. } //}}}
  1328. //{{{ foldLevelChanged() method
  1329. public void foldLevelChanged(Buffer buffer, int start, int end)
  1330. {
  1331. System.err.println("Invalidate " + (start-1) + " to " + textArea.getLastPhysicalLine() + "," + end);
  1332. if(textArea.getDisplayManager() == DisplayManager.this
  1333. && end != 0 && buffer.isLoaded())
  1334. {
  1335. textArea.invalidateLineRange(start - 1,
  1336. textArea.getLastPhysicalLine());
  1337. }
  1338. } //}}}
  1339. //{{{ contentInserted() method
  1340. public void contentInserted(Buffer buffer, int startLine,
  1341. int offset, int numLines, int length)
  1342. {
  1343. if(!buffer.isLoaded())
  1344. {
  1345. fvmreset();
  1346. return;
  1347. }
  1348. int endLine = startLine + numLines;
  1349. if(numLines != 0)
  1350. {
  1351. delayedMultilineUpdate = true;
  1352. /* this is a sloppy hack to fix bug
  1353. "[ 677902 ] hitting return after collapsed
  1354. fold"
  1355. the idea is that if we extend the range then
  1356. the problem described in the bug happends, so
  1357. if the insert is at the very end of the range
  1358. we don't extend it, instead we push the
  1359. insert into the next range, however for this
  1360. to work properly we also have to mess with
  1361. screen line counts. */
  1362. int index = fvmget(startLine);
  1363. int start = index + 1;
  1364. /* if(start + 1 < fvmcount && fvm[start]
  1365. == startLine + 1)
  1366. {
  1367. if(index % 2 == 0)
  1368. {
  1369. System.err.println("case 1");
  1370. scrollLineCount.scrollLine -=
  1371. getScreenLineCount(
  1372. startLine + 1);
  1373. start++;
  1374. }
  1375. } */
  1376. for(int i = start; i < fvmcount; i++)
  1377. {
  1378. fvm[i] += numLines;
  1379. }
  1380. lastfvmget = -1;
  1381. fvmdump();
  1382. }
  1383. if(textArea.getDisplayManager() == DisplayManager.this)
  1384. {
  1385. if(numLines != 0)
  1386. {
  1387. contentInserted(firstLine,startLine,numLines);
  1388. contentInserted(scrollLineCount,startLine,numLines);
  1389. }
  1390. if(delayedUpdateEnd >= startLine)
  1391. delayedUpdateEnd += numLines;
  1392. delayedUpdate(startLine,endLine);
  1393. //{{{ resize selections if necessary
  1394. for(int i = 0; i < textArea.selection.size(); i++)
  1395. {
  1396. Selection s = (Selection)textArea
  1397. .selection.elementAt(i);
  1398. if(s.contentInserted(buffer,startLine,offset,
  1399. numLines,length))
  1400. {
  1401. delayedUpdate(s.startLine,s.endLine);
  1402. }
  1403. } //}}}
  1404. int caret = textArea.getCaretPosition();
  1405. if(caret >= offset)
  1406. {
  1407. int scrollMode = (caretAutoScroll()
  1408. ? JEditTextArea.ELECTRIC_SCROLL
  1409. : JEditTextArea.NO_SCROLL);
  1410. textArea.moveCaretPosition(
  1411. caret + length,scrollMode);
  1412. }
  1413. else
  1414. {
  1415. int scrollMode = (caretAutoScroll()
  1416. ? JEditTextArea.NORMAL_SCROLL
  1417. : JEditTextArea.NO_SCROLL);
  1418. textArea.moveCaretPosition(
  1419. caret,scrollMode);
  1420. }
  1421. }
  1422. else
  1423. {
  1424. firstLine.callReset = true;
  1425. scrollLineCount.callReset = true;
  1426. }
  1427. } //}}}
  1428. //{{{ preContentRemoved() method
  1429. public void preContentRemoved(Buffer buffer, int startLine,
  1430. int offset, int numLines, int length)
  1431. {
  1432. if(!buffer.isLoaded())
  1433. return;
  1434. if(textArea.getDisplayManager() == DisplayManager.this)
  1435. {
  1436. if(numLines != 0)
  1437. {
  1438. preContentRemoved(firstLine,startLine,numLines);
  1439. preContentRemoved(scrollLineCount,startLine,numLines);
  1440. }
  1441. if(delayedUpdateEnd >= startLine)
  1442. delayedUpdateEnd -= numLines;
  1443. delayedUpdate(startLine,startLine);
  1444. }
  1445. else
  1446. {
  1447. firstLine.callReset = true;
  1448. scrollLineCount.callReset = true;
  1449. }
  1450. if(numLines != 0)
  1451. {
  1452. delayedMultilineUpdate = true;
  1453. int endLine = startLine + numLines;
  1454. /* update fold visibility map. */
  1455. int starti = fvmget(startLine);
  1456. int endi = fvmget(endLine);
  1457. /* both have same visibility; just remove
  1458. * anything in between. */
  1459. if(Math.abs(starti % 2) == Math.abs(endi % 2))
  1460. {
  1461. if(endi - starti == fvmcount)
  1462. {
  1463. // we're removing from before
  1464. // the first visible to after
  1465. // the last visible
  1466. fvmreset();
  1467. firstLine.callReset = true;
  1468. scrollLineCount.callReset = true;
  1469. }
  1470. else
  1471. {
  1472. fvmput(starti + 1,endi + 1,null);
  1473. starti++;
  1474. }
  1475. }
  1476. /* collapse 2 */
  1477. else if(starti != -1 && fvm[starti] == startLine)
  1478. {
  1479. //int newStart = fvm[endi + 1] - 1;
  1480. fvmput(starti,endi + 1,null);
  1481. //fvm[starti] = newStart;
  1482. //starti++;
  1483. }
  1484. /* shift */
  1485. else
  1486. {
  1487. fvmput(starti + 1,endi,null);
  1488. fvm[starti + 1] = startLine;
  1489. starti += 2;
  1490. }
  1491. /* update */
  1492. for(int i = starti; i < fvmcount; i++)
  1493. fvm[i] -= numLines;
  1494. if(firstLine.physicalLine
  1495. > getLastVisibleLine()
  1496. || firstLine.physicalLine
  1497. < getFirstVisibleLine())
  1498. {
  1499. // will be handled later.
  1500. // see comments at the end of
  1501. // transactionComplete().
  1502. }
  1503. // very subtle... if we leave this for
  1504. // ensurePhysicalLineIsVisible(), an
  1505. // extra line will be added to the
  1506. // scroll line count.
  1507. else if(!isLineVisible(
  1508. firstLine.physicalLine))
  1509. {
  1510. firstLine.physicalLine =
  1511. getNextVisibleLine(
  1512. firstLine.physicalLine);
  1513. }
  1514. lastfvmget = -1;
  1515. fvmdump();
  1516. }
  1517. } //}}}
  1518. //{{{ contentRemoved() method
  1519. public void contentRemoved(Buffer buffer, int startLine,
  1520. int start, int numLines, int length)
  1521. {
  1522. if(!buffer.isLoaded())
  1523. return;
  1524. if(textArea.getDisplayManager() == DisplayManager.this)
  1525. {
  1526. int endLine = startLine + numLines;
  1527. //{{{ resize selections if necessary
  1528. for(int i = 0; i < textArea.selection.size(); i++)
  1529. {
  1530. Selection s = (Selection)textArea
  1531. .selection.elementAt(i);
  1532. if(s.contentRemoved(buffer,startLine,
  1533. start,numLines,length))
  1534. {
  1535. delayedUpdate(s.startLine,s.endLine);
  1536. if(s.start == s.end)
  1537. {
  1538. textArea.selection.removeElementAt(i);
  1539. i--;
  1540. }
  1541. }
  1542. } //}}}
  1543. int caret = textArea.getCaretPosition();
  1544. if(caret >= start + length)
  1545. {
  1546. int scrollMode = (caretAutoScroll()
  1547. ? JEditTextArea.ELECTRIC_SCROLL
  1548. : JEditTextArea.NO_SCROLL);
  1549. textArea.moveCaretPosition(
  1550. caret - length,
  1551. scrollMode);
  1552. }
  1553. else if(caret >= start)
  1554. {
  1555. int scrollMode = (caretAutoScroll()
  1556. ? JEditTextArea.ELECTRIC_SCROLL
  1557. : JEditTextArea.NO_SCROLL);
  1558. textArea.moveCaretPosition(
  1559. start,scrollMode);
  1560. }
  1561. else
  1562. {
  1563. int scrollMode = (caretAutoScroll()
  1564. ? JEditTextArea.NORMAL_SCROLL
  1565. : JEditTextArea.NO_SCROLL);
  1566. textArea.moveCaretPosition(caret,scrollMode);
  1567. }
  1568. }
  1569. }
  1570. //}}}
  1571. //{{{ transactionComplete() method
  1572. public void transactionComplete(Buffer buffer)
  1573. {
  1574. if(textArea.getDisplayManager() == DisplayManager.this)
  1575. {
  1576. if(delayedUpdate)
  1577. {
  1578. // must be before the below call
  1579. // so that the chunk cache is not
  1580. // updated with an invisible first
  1581. // line (see above)
  1582. _notifyScreenLineChanges();
  1583. if(delayedMultilineUpdate)
  1584. {
  1585. textArea.invalidateScreenLineRange(
  1586. textArea.chunkCache
  1587. .getScreenLineOfOffset(
  1588. delayedUpdateStart,0),
  1589. textArea.getVisibleLines());
  1590. delayedMultilineUpdate = false;
  1591. }
  1592. else
  1593. {
  1594. textArea.invalidateLineRange(
  1595. delayedUpdateStart,
  1596. delayedUpdateEnd);
  1597. }
  1598. int _firstLine = textArea.getFirstPhysicalLine();
  1599. int _lastLine = textArea.getLastPhysicalLine();
  1600. int line = delayedUpdateStart;
  1601. if(!isLineVisible(line))
  1602. line = getNextVisibleLine(line);
  1603. while(line != -1 && line <= delayedUpdateEnd)
  1604. {
  1605. if(line < _firstLine
  1606. || line > _lastLine)
  1607. {
  1608. getScreenLineCount(line);
  1609. }
  1610. line = getNextVisibleLine(line);
  1611. }
  1612. // update visible lines
  1613. int visibleLines = textArea
  1614. .getVisibleLines();
  1615. if(visibleLines != 0)
  1616. {
  1617. textArea.chunkCache.getLineInfo(
  1618. visibleLines - 1);
  1619. }
  1620. // force the fold levels to be
  1621. // updated.
  1622. // when painting the last line of
  1623. // a buffer, Buffer.isFoldStart()
  1624. // doesn't call getFoldLevel(),
  1625. // hence the foldLevelChanged()
  1626. // event might not be sent for the
  1627. // previous line.
  1628. buffer.getFoldLevel(delayedUpdateEnd);
  1629. }
  1630. textArea._finishCaretUpdate();
  1631. }
  1632. //{{{ Debug code
  1633. if(Debug.SCROLL_VERIFY)
  1634. {
  1635. int scrollLineCount = 0;
  1636. for(int i = 0; i < textArea.getLineCount(); i++)
  1637. {
  1638. if(isLineVisible(i))
  1639. {
  1640. scrollLineCount +=
  1641. getScreenLineCount(i);
  1642. }
  1643. }
  1644. if(scrollLineCount != getScrollLineCount())
  1645. {
  1646. throw new InternalError(scrollLineCount
  1647. + " != "
  1648. + getScrollLineCount());
  1649. }
  1650. } //}}}
  1651. delayedUpdate = false;
  1652. } //}}}
  1653. //{{{ contentInserted() method
  1654. private void contentInserted(Anchor anchor, int startLine,
  1655. int numLines)
  1656. {
  1657. if(anchor.physicalLine >= startLine)
  1658. {
  1659. if(anchor.physicalLine != startLine)
  1660. anchor.physicalLine += numLines;
  1661. anchor.callChanged = true;
  1662. }
  1663. } //}}}
  1664. //{{{ preContentRemoved() method
  1665. private void preContentRemoved(Anchor anchor, int startLine,
  1666. int numLines)
  1667. {
  1668. if(anchor.physicalLine >= startLine)
  1669. {
  1670. if(anchor.physicalLine == startLine)
  1671. anchor.callChanged = true;
  1672. else
  1673. {
  1674. int end = Math.min(startLine + numLines,
  1675. anchor.physicalLine);
  1676. for(int i = startLine; i < end; i++)
  1677. {
  1678. //XXX
  1679. if(isLineVisible(i))
  1680. {
  1681. anchor.scrollLine -=
  1682. lineMgr
  1683. .getScreenLineCount(i);
  1684. }
  1685. }
  1686. anchor.physicalLine -= (end - startLine);
  1687. anchor.callChanged = true;
  1688. }
  1689. }
  1690. } //}}}
  1691. //{{{ delayedUpdate() method
  1692. private void delayedUpdate(int startLine, int endLine)
  1693. {
  1694. textArea.chunkCache.invalidateChunksFromPhys(startLine);
  1695. if(!delayedUpdate)
  1696. {
  1697. delayedUpdateStart = startLine;
  1698. delayedUpdateEnd = endLine;
  1699. delayedUpdate = true;
  1700. }
  1701. else
  1702. {
  1703. delayedUpdateStart = Math.min(
  1704. delayedUpdateStart,
  1705. startLine);
  1706. delayedUpdateEnd = Math.max(
  1707. delayedUpdateEnd,
  1708. endLine);
  1709. }
  1710. } //}}}
  1711. //{{{ caretAutoScroll() method
  1712. /**
  1713. * Return if change in buffer should scroll this text area.
  1714. */
  1715. private boolean caretAutoScroll()
  1716. {
  1717. View view = textArea.getView();
  1718. return view == jEdit.getActiveView()
  1719. && view.getTextArea() == textArea;
  1720. } //}}}
  1721. } //}}}
  1722. }