PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/textarea/FoldVisibilityManager.java

#
Java | 867 lines | 582 code | 106 blank | 179 comment | 154 complexity | c8e2dad057fa7249ee71150ed4e8c383 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. * FoldVisibilityManager.java - Controls fold visiblity
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2001 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. import java.awt.Toolkit;
  24. import org.gjt.sp.jedit.*;
  25. /**
  26. * Controls fold visibility.
  27. * Note that a "physical" line number is a line index, numbered from the
  28. * start of the buffer. A "virtual" line number is a visible line index;
  29. * lines after a collapsed fold have a virtual line number that is less
  30. * than their physical line number, for example.<p>
  31. *
  32. * You can use the <code>physicalToVirtual()</code> and
  33. * <code>virtualToPhysical()</code> methods to convert one type of line
  34. * number to another.
  35. *
  36. * @author Slava Pestov
  37. * @version $Id: FoldVisibilityManager.java 3933 2001-12-03 10:52:27Z spestov $
  38. * @since jEdit 4.0pre1
  39. */
  40. public class FoldVisibilityManager
  41. {
  42. //{{{ FoldVisibilityManager constructor
  43. public FoldVisibilityManager(Buffer buffer, JEditTextArea textArea)
  44. {
  45. this.buffer = buffer;
  46. this.textArea = textArea;
  47. } //}}}
  48. //{{{ isNarrowed() method
  49. /**
  50. * Returns if the buffer has been narrowed.
  51. * @since jEdit 4.0pre2
  52. */
  53. public boolean isNarrowed()
  54. {
  55. return narrowed;
  56. } //}}}
  57. //{{{ getVirtualLineCount() method
  58. /**
  59. * Returns the number of virtual lines in the buffer.
  60. * @since jEdit 4.0pre1
  61. */
  62. public int getVirtualLineCount()
  63. {
  64. return buffer._getVirtualLineCount(index);
  65. } //}}}
  66. //{{{ isLineVisible() method
  67. /**
  68. * Returns if the specified line is visible.
  69. * @param line A physical line index
  70. * @since jEdit 4.0pre1
  71. */
  72. public final boolean isLineVisible(int line)
  73. {
  74. try
  75. {
  76. buffer.readLock();
  77. return buffer._isLineVisible(line,index);
  78. }
  79. finally
  80. {
  81. buffer.readUnlock();
  82. }
  83. } //}}}
  84. //{{{ getFirstVisibleLine() method
  85. /**
  86. * Returns the physical line number of the first visible line.
  87. * @since jEdit 4.0pre3
  88. */
  89. public int getFirstVisibleLine()
  90. {
  91. try
  92. {
  93. buffer.readLock();
  94. for(int i = 0; i < buffer.getLineCount(); i++)
  95. {
  96. if(isLineVisible(i))
  97. return i;
  98. }
  99. }
  100. finally
  101. {
  102. buffer.readUnlock();
  103. }
  104. // can't happen?
  105. return -1;
  106. } //}}}
  107. //{{{ getLastVisibleLine() method
  108. /**
  109. * Returns the physical line number of the last visible line.
  110. * @since jEdit 4.0pre3
  111. */
  112. public int getLastVisibleLine()
  113. {
  114. try
  115. {
  116. buffer.readLock();
  117. for(int i = buffer.getLineCount() - 1; i >= 0; i--)
  118. {
  119. if(isLineVisible(i))
  120. return i;
  121. }
  122. }
  123. finally
  124. {
  125. buffer.readUnlock();
  126. }
  127. // can't happen?
  128. return -1;
  129. } //}}}
  130. //{{{ getNextVisibleLine() method
  131. /**
  132. * Returns the next visible line after the specified line index.
  133. * @param line A physical line index
  134. * @since jEdit 4.0pre1
  135. */
  136. public int getNextVisibleLine(int line)
  137. {
  138. try
  139. {
  140. buffer.readLock();
  141. if(line == buffer.getLineCount() - 1)
  142. return -1;
  143. for(int i = line + 1; i < buffer.getLineCount(); i++)
  144. {
  145. if(buffer._isLineVisible(i,index))
  146. return i;
  147. }
  148. return -1;
  149. }
  150. finally
  151. {
  152. buffer.readUnlock();
  153. }
  154. } //}}}
  155. //{{{ getPrevVisibleLine() method
  156. /**
  157. * Returns the previous visible line before the specified line index.
  158. * @param line A physical line index
  159. * @since jEdit 4.0pre1
  160. */
  161. public int getPrevVisibleLine(int line)
  162. {
  163. try
  164. {
  165. buffer.readLock();
  166. if(line == 0)
  167. return -1;
  168. for(int i = line - 1; i >= 0; i--)
  169. {
  170. if(buffer._isLineVisible(i,index))
  171. return i;
  172. }
  173. return -1;
  174. }
  175. finally
  176. {
  177. buffer.readUnlock();
  178. }
  179. } //}}}
  180. //{{{ physicalToVirtual() method
  181. /**
  182. * Converts a physical line number to a virtual line number.
  183. * @param line A physical line index
  184. * @since jEdit 4.0pre1
  185. */
  186. public int physicalToVirtual(int line)
  187. {
  188. try
  189. {
  190. buffer.readLock();
  191. if(line < 0)
  192. throw new ArrayIndexOutOfBoundsException(line + " < 0");
  193. else if(line >= buffer.getLineCount())
  194. {
  195. throw new ArrayIndexOutOfBoundsException(line + " > "
  196. + buffer.getLineCount());
  197. }
  198. while(!buffer._isLineVisible(line,index) && line > 0)
  199. line--;
  200. if(line == 0 && !buffer._isLineVisible(line,index))
  201. {
  202. // inside the top narrow.
  203. return 0;
  204. }
  205. if(lastPhysical == line)
  206. {
  207. if(lastVirtual < 0 || lastVirtual >= buffer._getVirtualLineCount(index))
  208. {
  209. throw new ArrayIndexOutOfBoundsException(
  210. "cached: " + lastVirtual);
  211. }
  212. }
  213. else if(line > lastPhysical && lastPhysical != -1)
  214. {
  215. for(;;)
  216. {
  217. if(lastPhysical == line)
  218. break;
  219. if(buffer._isLineVisible(lastPhysical,index))
  220. lastVirtual++;
  221. if(lastPhysical == buffer.getLineCount() - 1)
  222. break;
  223. else
  224. lastPhysical++;
  225. }
  226. if(lastVirtual < 0 || lastVirtual >= buffer._getVirtualLineCount(index))
  227. {
  228. throw new ArrayIndexOutOfBoundsException(
  229. "fwd scan: " + lastVirtual);
  230. }
  231. }
  232. else if(line < lastPhysical && lastPhysical - line > line)
  233. {
  234. for(;;)
  235. {
  236. if(lastPhysical == line)
  237. break;
  238. if(buffer._isLineVisible(lastPhysical,index))
  239. lastVirtual--;
  240. if(lastPhysical == 0)
  241. break;
  242. else
  243. lastPhysical--;
  244. }
  245. if(lastVirtual < 0 || lastVirtual >= buffer._getVirtualLineCount(index))
  246. {
  247. throw new ArrayIndexOutOfBoundsException(
  248. "back scan: " + lastVirtual);
  249. }
  250. }
  251. else
  252. {
  253. lastPhysical = 0;
  254. // find first visible line
  255. while(!buffer._isLineVisible(lastPhysical,index))
  256. lastPhysical++;
  257. lastVirtual = 0;
  258. for(;;)
  259. {
  260. if(lastPhysical == line)
  261. break;
  262. if(buffer._isLineVisible(lastPhysical,index))
  263. lastVirtual++;
  264. if(lastPhysical == buffer.getLineCount() - 1)
  265. break;
  266. else
  267. lastPhysical++;
  268. }
  269. if(lastVirtual < 0 || lastVirtual >= buffer._getVirtualLineCount(index))
  270. {
  271. throw new ArrayIndexOutOfBoundsException(
  272. "zero scan: " + lastVirtual);
  273. }
  274. }
  275. return lastVirtual;
  276. }
  277. finally
  278. {
  279. buffer.readUnlock();
  280. }
  281. } //}}}
  282. //{{{ virtualToPhysical() method
  283. /**
  284. * Converts a virtual line number to a physical line number.
  285. * @param line A virtual line index
  286. * @since jEdit 4.0pre1
  287. */
  288. public int virtualToPhysical(int line)
  289. {
  290. try
  291. {
  292. buffer.readLock();
  293. if(line < 0)
  294. throw new ArrayIndexOutOfBoundsException(line + " < 0");
  295. else if(line >= buffer._getVirtualLineCount(index))
  296. {
  297. throw new ArrayIndexOutOfBoundsException(line + " > "
  298. + buffer._getVirtualLineCount(index));
  299. }
  300. if(lastVirtual == line)
  301. {
  302. if(lastPhysical < 0 || lastPhysical >= buffer.getLineCount())
  303. {
  304. throw new ArrayIndexOutOfBoundsException(
  305. "cached: " + lastPhysical);
  306. }
  307. }
  308. else if(line > lastVirtual && lastVirtual != -1)
  309. {
  310. for(;;)
  311. {
  312. if(buffer._isLineVisible(lastPhysical,index))
  313. {
  314. if(lastVirtual == line)
  315. break;
  316. else
  317. lastVirtual++;
  318. }
  319. if(lastPhysical == buffer.getLineCount() - 1)
  320. break;
  321. else
  322. lastPhysical++;
  323. }
  324. if(lastPhysical < 0 || lastPhysical >= buffer.getLineCount())
  325. {
  326. throw new ArrayIndexOutOfBoundsException(
  327. "fwd scan: " + lastPhysical);
  328. }
  329. }
  330. else if(line < lastVirtual && lastVirtual - line > line)
  331. {
  332. for(;;)
  333. {
  334. if(buffer._isLineVisible(lastPhysical,index))
  335. {
  336. if(lastVirtual == line)
  337. break;
  338. else
  339. lastVirtual--;
  340. }
  341. if(lastPhysical == 0)
  342. break;
  343. else
  344. lastPhysical--;
  345. }
  346. if(lastPhysical < 0 || lastPhysical >= buffer.getLineCount())
  347. {
  348. throw new ArrayIndexOutOfBoundsException(
  349. "back scan: " + lastPhysical);
  350. }
  351. }
  352. else
  353. {
  354. lastPhysical = 0;
  355. // find first visible line
  356. while(!buffer._isLineVisible(lastPhysical,index))
  357. lastPhysical++;
  358. lastVirtual = 0;
  359. for(;;)
  360. {
  361. if(buffer._isLineVisible(lastPhysical,index))
  362. {
  363. if(lastVirtual == line)
  364. break;
  365. else
  366. lastVirtual++;
  367. }
  368. if(lastPhysical == buffer.getLineCount() - 1)
  369. break;
  370. else
  371. lastPhysical++;
  372. }
  373. if(lastPhysical < 0 || lastPhysical >= buffer.getLineCount())
  374. {
  375. throw new ArrayIndexOutOfBoundsException(
  376. "zero scan: " + lastPhysical);
  377. }
  378. }
  379. return lastPhysical;
  380. }
  381. finally
  382. {
  383. buffer.readUnlock();
  384. }
  385. } //}}}
  386. //{{{ collapseFold() method
  387. /**
  388. * Collapses the fold at the specified physical line index.
  389. * @param line A physical line index
  390. * @since jEdit 4.0pre1
  391. */
  392. public void collapseFold(int line)
  393. {
  394. int lineCount = buffer.getLineCount();
  395. int start = 0;
  396. int end = lineCount - 1;
  397. try
  398. {
  399. buffer.writeLock();
  400. // if the caret is on a collapsed fold, collapse the
  401. // parent fold
  402. if(line != 0
  403. && line != buffer.getLineCount() - 1
  404. && buffer.isFoldStart(line)
  405. && !buffer._isLineVisible(line + 1,index))
  406. {
  407. line--;
  408. }
  409. int initialFoldLevel = buffer.getFoldLevel(line);
  410. //{{{ Find fold start and end...
  411. if(line != lineCount - 1
  412. && buffer.getFoldLevel(line + 1) > initialFoldLevel)
  413. {
  414. // this line is the start of a fold
  415. start = line + 1;
  416. for(int i = line + 1; i < lineCount; i++)
  417. {
  418. if(buffer.getFoldLevel(i) <= initialFoldLevel)
  419. {
  420. end = i - 1;
  421. break;
  422. }
  423. }
  424. }
  425. else
  426. {
  427. boolean ok = false;
  428. // scan backwards looking for the start
  429. for(int i = line - 1; i >= 0; i--)
  430. {
  431. if(buffer.getFoldLevel(i) < initialFoldLevel)
  432. {
  433. start = i + 1;
  434. ok = true;
  435. break;
  436. }
  437. }
  438. if(!ok)
  439. {
  440. // no folds in buffer
  441. return;
  442. }
  443. for(int i = line + 1; i < lineCount; i++)
  444. {
  445. if(buffer.getFoldLevel(i) < initialFoldLevel)
  446. {
  447. end = i - 1;
  448. break;
  449. }
  450. }
  451. } //}}}
  452. //{{{ Collapse the fold...
  453. int delta = (end - start + 1);
  454. for(int i = start; i <= end; i++)
  455. {
  456. if(buffer._isLineVisible(i,index))
  457. buffer._setLineVisible(i,index,false);
  458. else
  459. delta--;
  460. }
  461. if(delta == 0)
  462. {
  463. // user probably pressed A+BACK_SPACE twice
  464. return;
  465. }
  466. buffer._setVirtualLineCount(index,
  467. buffer._getVirtualLineCount(index)
  468. - delta);
  469. //}}}
  470. }
  471. finally
  472. {
  473. buffer.writeUnlock();
  474. }
  475. foldStructureChanged();
  476. int virtualLine = physicalToVirtual(start);
  477. if(textArea.getFirstLine() > virtualLine)
  478. textArea.setFirstLine(virtualLine - textArea.getElectricScroll());
  479. } //}}}
  480. //{{{ expandFold() method
  481. /**
  482. * Expands the fold at the specified physical line index.
  483. * @param line A physical line index
  484. * @param fully If true, all subfolds will also be expanded
  485. * @since jEdit 4.0pre3
  486. */
  487. public int expandFold(int line, boolean fully)
  488. {
  489. // the first sub-fold. used by JEditTextArea.expandFold().
  490. int returnValue = -1;
  491. int lineCount = buffer.getLineCount();
  492. int start = 0;
  493. int end = lineCount - 1;
  494. int delta = 0;
  495. try
  496. {
  497. buffer.writeLock();
  498. int initialFoldLevel = buffer.getFoldLevel(line);
  499. //{{{ Find fold start and fold end...
  500. if(line != lineCount - 1
  501. && buffer._isLineVisible(line,index)
  502. && !buffer._isLineVisible(line + 1,index)
  503. && buffer.getFoldLevel(line + 1) > initialFoldLevel)
  504. {
  505. // this line is the start of a fold
  506. start = line + 1;
  507. for(int i = line + 1; i < lineCount; i++)
  508. {
  509. if(buffer._isLineVisible(i,index) && buffer.getFoldLevel(i) <= initialFoldLevel)
  510. {
  511. end = i - 1;
  512. break;
  513. }
  514. }
  515. }
  516. else
  517. {
  518. boolean ok = false;
  519. // scan backwards looking for the start
  520. for(int i = line - 1; i >= 0; i--)
  521. {
  522. if(buffer._isLineVisible(i,index) && buffer.getFoldLevel(i) < initialFoldLevel)
  523. {
  524. start = i + 1;
  525. ok = true;
  526. break;
  527. }
  528. }
  529. if(!ok)
  530. {
  531. // no folds in buffer
  532. return -1;
  533. }
  534. for(int i = line + 1; i < lineCount; i++)
  535. {
  536. if(buffer._isLineVisible(i,index) && buffer.getFoldLevel(i) < initialFoldLevel)
  537. {
  538. end = i - 1;
  539. break;
  540. }
  541. }
  542. } //}}}
  543. //{{{ Expand the fold...
  544. // we need a different value of initialFoldLevel here!
  545. initialFoldLevel = buffer.getFoldLevel(start);
  546. for(int i = start; i <= end; i++)
  547. {
  548. if(buffer.getFoldLevel(i) > initialFoldLevel)
  549. {
  550. if(returnValue == -1
  551. && i != 0
  552. && buffer.isFoldStart(i - 1))
  553. {
  554. returnValue = i - 1;
  555. }
  556. if(!buffer._isLineVisible(i,index) && fully)
  557. {
  558. delta++;
  559. buffer._setLineVisible(i,index,true);
  560. }
  561. }
  562. else if(!buffer._isLineVisible(i,index))
  563. {
  564. delta++;
  565. buffer._setLineVisible(i,index,true);
  566. }
  567. }
  568. buffer._setVirtualLineCount(index,
  569. buffer._getVirtualLineCount(index)
  570. + delta);
  571. //}}}
  572. if(!fully && !buffer._isLineVisible(line,index))
  573. {
  574. // this is a hack, and really needs to be done better.
  575. expandFold(line,false);
  576. return returnValue;
  577. }
  578. }
  579. finally
  580. {
  581. buffer.writeUnlock();
  582. }
  583. foldStructureChanged();
  584. int virtualLine = physicalToVirtual(start);
  585. int firstLine = textArea.getFirstLine();
  586. int visibleLines = textArea.getVisibleLines();
  587. if(virtualLine + delta >= firstLine + visibleLines
  588. && delta < visibleLines - 1)
  589. {
  590. textArea.setFirstLine(virtualLine + delta - visibleLines + 1);
  591. }
  592. return returnValue;
  593. } //}}}
  594. //{{{ expandAllFolds() method
  595. /**
  596. * Expands all folds.
  597. * @since jEdit 4.0pre1
  598. */
  599. public void expandAllFolds()
  600. {
  601. try
  602. {
  603. buffer.writeLock();
  604. narrowed = false;
  605. buffer._setVirtualLineCount(index,buffer.getLineCount());
  606. for(int i = 0; i < buffer.getLineCount(); i++)
  607. {
  608. buffer._setLineVisible(i,index,true);
  609. }
  610. foldStructureChanged();
  611. }
  612. finally
  613. {
  614. buffer.writeUnlock();
  615. }
  616. } //}}}
  617. //{{{ expandFolds() method
  618. /**
  619. * This method should only be called from <code>actions.xml</code>.
  620. * @since jEdit 4.0pre1
  621. */
  622. public void expandFolds(char digit)
  623. {
  624. if(digit < '1' || digit > '9')
  625. {
  626. Toolkit.getDefaultToolkit().beep();
  627. return;
  628. }
  629. else
  630. expandFolds((int)(digit - '1') + 1);
  631. } //}}}
  632. //{{{ expandFolds() method
  633. /**
  634. * Expands all folds with the specified fold level.
  635. * @param foldLevel The fold level
  636. * @since jEdit 4.0pre1
  637. */
  638. public void expandFolds(int foldLevel)
  639. {
  640. try
  641. {
  642. buffer.writeLock();
  643. narrowed = false;
  644. // so that getFoldLevel() calling fireFoldLevelsChanged()
  645. // won't break
  646. buffer._setVirtualLineCount(index,buffer.getLineCount());
  647. int newVirtualLineCount = 0;
  648. foldLevel = (foldLevel - 1) * buffer.getIndentSize() + 1;
  649. /* this ensures that the first line is always visible */
  650. boolean seenVisibleLine = false;
  651. for(int i = 0; i < buffer.getLineCount(); i++)
  652. {
  653. if(!seenVisibleLine || buffer.getFoldLevel(i) < foldLevel)
  654. {
  655. seenVisibleLine = true;
  656. buffer._setLineVisible(i,index,true);
  657. newVirtualLineCount++;
  658. }
  659. else
  660. buffer._setLineVisible(i,index,false);
  661. }
  662. buffer._setVirtualLineCount(index,newVirtualLineCount);
  663. }
  664. finally
  665. {
  666. buffer.writeUnlock();
  667. }
  668. foldStructureChanged();
  669. } //}}}
  670. //{{{ narrow() method
  671. /**
  672. * Narrows the visible portion of the buffer to the specified
  673. * line range.
  674. * @param start The first line
  675. * @param end The last line
  676. * @since jEdit 4.0pre1
  677. */
  678. public void narrow(int start, int end)
  679. {
  680. int virtualLineCount = buffer._getVirtualLineCount(index);
  681. for(int i = 0; i < start; i++)
  682. {
  683. if(buffer._isLineVisible(i,index))
  684. {
  685. virtualLineCount--;
  686. buffer._setLineVisible(i,index,false);
  687. }
  688. }
  689. for(int i = end + 1; i < buffer.getLineCount(); i++)
  690. {
  691. if(buffer._isLineVisible(i,index))
  692. {
  693. virtualLineCount--;
  694. buffer._setLineVisible(i,index,false);
  695. }
  696. }
  697. buffer._setVirtualLineCount(index,virtualLineCount);
  698. narrowed = true;
  699. foldStructureChanged();
  700. // Hack... need a more direct way of obtaining a view?
  701. // JEditTextArea.getView() method?
  702. GUIUtilities.getView(textArea).getStatus().setMessageAndClear(
  703. jEdit.getProperty("view.status.narrow"));
  704. } //}}}
  705. //{{{ Methods for Buffer class to call
  706. //{{{ _grab() method
  707. /**
  708. * Do not call this method. The only reason it is public is so
  709. * that the <code>Buffer</code> class can call it.
  710. */
  711. public final void _grab(int index)
  712. {
  713. this.index = index;
  714. lastPhysical = lastVirtual = -1;
  715. } //}}}
  716. //{{{ _release() method
  717. /**
  718. * Do not call this method. The only reason it is public is so
  719. * that the <code>Buffer</code> class can call it.
  720. */
  721. public final void _release()
  722. {
  723. index = -1;
  724. } //}}}
  725. //{{{ _getIndex() method
  726. /**
  727. * Do not call this method. The only reason it is public is so
  728. * that the <code>Buffer</code> class can call it.
  729. */
  730. public final int _getIndex()
  731. {
  732. return index;
  733. } //}}}
  734. //{{{ _invalidate() method
  735. /**
  736. * Do not call this method. The only reason it is public is so
  737. * that the <code>Buffer</code> class can call it.
  738. */
  739. public void _invalidate(int startLine)
  740. {
  741. if(lastPhysical >= startLine)
  742. lastPhysical = lastVirtual = 0;
  743. } //}}}
  744. //}}}
  745. //{{{ Private members
  746. //{{{ Instance variables
  747. private Buffer buffer;
  748. private JEditTextArea textArea;
  749. private int index;
  750. private int lastPhysical;
  751. private int lastVirtual;
  752. private boolean narrowed;
  753. //}}}
  754. //{{{ foldStructureChanged() method
  755. private void foldStructureChanged()
  756. {
  757. lastPhysical = lastVirtual = -1;
  758. textArea.foldStructureChanged();
  759. } //}}}
  760. //}}}
  761. }