/jEdit/tags/jedit-4-0-pre6/org/gjt/sp/jedit/textarea/JEditTextArea.java

# · Java · 2926 lines · 1789 code · 370 blank · 767 comment · 384 complexity · 3b3dc6e95475aa8918ed0d1be6b35ecf MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * JEditTextArea.java - jEdit's text component
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2000, 2001, 2002 Slava Pestov
  7. * Portions copyright (C) 2000 Ollie Rutherfurd
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.textarea;
  24. //{{{ Imports
  25. import javax.swing.border.*;
  26. import javax.swing.event.*;
  27. import javax.swing.plaf.metal.MetalLookAndFeel;
  28. import javax.swing.text.Segment;
  29. import javax.swing.undo.*;
  30. import javax.swing.*;
  31. import java.awt.event.*;
  32. import java.awt.font.*;
  33. import java.awt.geom.*;
  34. import java.awt.*;
  35. import java.util.Arrays;
  36. import java.util.ArrayList;
  37. import java.util.Enumeration;
  38. import java.util.Hashtable;
  39. import java.util.Vector;
  40. import org.gjt.sp.jedit.buffer.*;
  41. import org.gjt.sp.jedit.gui.*;
  42. import org.gjt.sp.jedit.syntax.*;
  43. import org.gjt.sp.jedit.*;
  44. import org.gjt.sp.util.Log;
  45. //}}}
  46. /**
  47. * jEdit's text component.
  48. *
  49. * @author Slava Pestov
  50. * @version $Id: JEditTextArea.java 4050 2002-02-19 06:51:12Z spestov $
  51. */
  52. public class JEditTextArea extends JComponent
  53. {
  54. //{{{ JEditTextArea constructor
  55. /**
  56. * Creates a new JEditTextArea.
  57. */
  58. public JEditTextArea(View view)
  59. {
  60. enableEvents(AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
  61. this.view = view;
  62. //{{{ Initialize some misc. stuff
  63. selection = new Vector();
  64. chunkCache = new ChunkCache(this);
  65. painter = new TextAreaPainter(this);
  66. gutter = new Gutter(view,this);
  67. bufferHandler = new BufferChangeHandler();
  68. listenerList = new EventListenerList();
  69. caretEvent = new MutableCaretEvent();
  70. bracketLine = bracketPosition = -1;
  71. blink = true;
  72. lineSegment = new Segment();
  73. returnValue = new Point();
  74. runnables = new ArrayList();
  75. //}}}
  76. //{{{ Initialize the GUI
  77. setLayout(new ScrollLayout());
  78. add(LEFT,gutter);
  79. add(CENTER,painter);
  80. add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL));
  81. add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL));
  82. horizontal.setValues(0,0,0,0);
  83. //}}}
  84. //{{{ this ensures that the text area's look is slightly
  85. // more consistent with the rest of the metal l&f.
  86. // while it depends on not-so-well-documented portions
  87. // of Swing, it only affects appearance, so future
  88. // breakage shouldn't matter
  89. if(UIManager.getLookAndFeel() instanceof MetalLookAndFeel)
  90. {
  91. setBorder(new TextAreaBorder());
  92. vertical.putClientProperty("JScrollBar.isFreeStanding",
  93. Boolean.FALSE);
  94. horizontal.putClientProperty("JScrollBar.isFreeStanding",
  95. Boolean.FALSE);
  96. //horizontal.setBorder(null);
  97. }
  98. //}}}
  99. //{{{ Add some event listeners
  100. vertical.addAdjustmentListener(new AdjustHandler());
  101. horizontal.addAdjustmentListener(new AdjustHandler());
  102. mouseHandler = new MouseHandler();
  103. painter.addMouseListener(mouseHandler);
  104. painter.addMouseMotionListener(mouseHandler);
  105. addFocusListener(new FocusHandler());
  106. //}}}
  107. // This doesn't seem very correct, but it fixes a problem
  108. // when setting the initial caret position for a buffer
  109. // (eg, from the recent file list)
  110. focusedComponent = this;
  111. } //}}}
  112. //{{{ Getters and setters
  113. //{{{ getPainter() method
  114. /**
  115. * Returns the object responsible for painting this text area.
  116. */
  117. public final TextAreaPainter getPainter()
  118. {
  119. return painter;
  120. } //}}}
  121. //{{{ getGutter() method
  122. /**
  123. * Returns the gutter to the left of the text area or null if the gutter
  124. * is disabled
  125. */
  126. public final Gutter getGutter()
  127. {
  128. return gutter;
  129. } //}}}
  130. //{{{ getFoldVisibilityManager() method
  131. /**
  132. * Returns the fold visibility manager used by this text area.
  133. * @since jEdit 4.0pre1
  134. */
  135. public FoldVisibilityManager getFoldVisibilityManager()
  136. {
  137. return foldVisibilityManager;
  138. } //}}}
  139. //{{{ isCaretBlinkEnabled() method
  140. /**
  141. * Returns true if the caret is blinking, false otherwise.
  142. */
  143. public final boolean isCaretBlinkEnabled()
  144. {
  145. return caretBlinks;
  146. } //}}}
  147. //{{{ setCaretBlinkEnabled() method
  148. /**
  149. * Toggles caret blinking.
  150. * @param caretBlinks True if the caret should blink, false otherwise
  151. */
  152. public void setCaretBlinkEnabled(boolean caretBlinks)
  153. {
  154. this.caretBlinks = caretBlinks;
  155. if(!caretBlinks)
  156. blink = false;
  157. if(buffer != null)
  158. invalidateLine(caretLine);
  159. } //}}}
  160. //{{{ getElectricScroll() method
  161. /**
  162. * Returns the number of lines from the top and button of the
  163. * text area that are always visible.
  164. */
  165. public final int getElectricScroll()
  166. {
  167. return electricScroll;
  168. } //}}}
  169. //{{{ setElectricScroll() method
  170. /**
  171. * Sets the number of lines from the top and bottom of the text
  172. * area that are always visible
  173. * @param electricScroll The number of lines always visible from
  174. * the top or bottom
  175. */
  176. public final void setElectricScroll(int electricScroll)
  177. {
  178. this.electricScroll = electricScroll;
  179. } //}}}
  180. //{{{ isQuickCopyEnabled() method
  181. /**
  182. * Returns if clicking the middle mouse button pastes the most
  183. * recent selection (% register), and if Control-dragging inserts
  184. * the selection at the caret.
  185. */
  186. public final boolean isQuickCopyEnabled()
  187. {
  188. return quickCopy;
  189. } //}}}
  190. //{{{ setQuickCopyEnabled() method
  191. /**
  192. * Sets if clicking the middle mouse button pastes the most
  193. * recent selection (% register), and if Control-dragging inserts
  194. * the selection at the caret.
  195. * @param quickCopy A boolean flag
  196. */
  197. public final void setQuickCopyEnabled(boolean quickCopy)
  198. {
  199. this.quickCopy = quickCopy;
  200. } //}}}
  201. //{{{ getBuffer() method
  202. /**
  203. * Returns the buffer this text area is editing.
  204. */
  205. public final Buffer getBuffer()
  206. {
  207. return buffer;
  208. } //}}}
  209. //{{{ setBuffer() method
  210. /**
  211. * Sets the buffer this text area is editing.
  212. * @param buffer The buffer
  213. */
  214. public void setBuffer(Buffer buffer)
  215. {
  216. if(this.buffer == buffer)
  217. return;
  218. try
  219. {
  220. bufferChanging = true;
  221. if(this.buffer != null)
  222. {
  223. setFirstLine(0);
  224. selectNone();
  225. caretLine = caret = caretScreenLine = 0;
  226. bracketLine = bracketPosition = -1;
  227. this.buffer._releaseFoldVisibilityManager(foldVisibilityManager);
  228. this.buffer.removeBufferChangeListener(bufferHandler);
  229. }
  230. this.buffer = buffer;
  231. buffer.addBufferChangeListener(bufferHandler);
  232. bufferHandlerInstalled = true;
  233. foldVisibilityManager = buffer._getFoldVisibilityManager(this);
  234. chunkCache.setFirstLine(0);
  235. physFirstLine = foldVisibilityManager.getFirstVisibleLine();
  236. propertiesChanged();
  237. recalculateLastPhysicalLine();
  238. }
  239. finally
  240. {
  241. bufferChanging = false;
  242. }
  243. } //}}}
  244. //{{{ isEditable() method
  245. /**
  246. * Returns true if this text area is editable, false otherwise.
  247. */
  248. public final boolean isEditable()
  249. {
  250. return buffer.isEditable();
  251. } //}}}
  252. //{{{ getRightClickPopup() method
  253. /**
  254. * Returns the right click popup menu.
  255. */
  256. public final JPopupMenu getRightClickPopup()
  257. {
  258. return popup;
  259. } //}}}
  260. //{{{ setRightClickPopup() method
  261. /**
  262. * Sets the right click popup menu.
  263. * @param popup The popup
  264. */
  265. public final void setRightClickPopup(JPopupMenu popup)
  266. {
  267. this.popup = popup;
  268. } //}}}
  269. //}}}
  270. //{{{ Scrolling
  271. //{{{ getFirstLine() method
  272. /**
  273. * Returns the line displayed at the text area's origin. This is
  274. * a virtual, not a physical, line number.
  275. */
  276. public final int getFirstLine()
  277. {
  278. return firstLine;
  279. } //}}}
  280. //{{{ setFirstLine() method
  281. /**
  282. * Sets the line displayed at the text area's origin. This is
  283. * a virtual, not a physical, line number.
  284. */
  285. public void setFirstLine(int firstLine)
  286. {
  287. if(firstLine == this.firstLine)
  288. return;
  289. _setFirstLine(firstLine);
  290. view.synchroScrollVertical(this,firstLine);
  291. } //}}}
  292. //{{{ _setFirstLine() method
  293. public void _setFirstLine(int firstLine)
  294. {
  295. firstLine = Math.max(0,Math.min(getVirtualLineCount() - 1,firstLine));
  296. this.firstLine = firstLine;
  297. physFirstLine = virtualToPhysical(firstLine);
  298. maxHorizontalScrollWidth = 0;
  299. chunkCache.setFirstLine(firstLine);
  300. recalculateLastPhysicalLine();
  301. if(this.firstLine != vertical.getValue())
  302. updateScrollBars();
  303. painter.repaint();
  304. gutter.repaint();
  305. fireScrollEvent(true);
  306. } //}}}
  307. //{{{ getVisibleLines() method
  308. /**
  309. * Returns the number of lines visible in this text area.
  310. */
  311. public final int getVisibleLines()
  312. {
  313. return visibleLines;
  314. } //}}}
  315. //{{{ getFirstPhysicalLine() method
  316. /**
  317. * Returns the first visible physical line index.
  318. * @since jEdit 4.0pre4
  319. */
  320. public final int getFirstPhysicalLine()
  321. {
  322. return physFirstLine;
  323. } //}}}
  324. //{{{ getLastPhysicalLine() method
  325. /**
  326. * Returns the last visible physical line index.
  327. * @since jEdit 4.0pre4
  328. */
  329. public final int getLastPhysicalLine()
  330. {
  331. return physLastLine;
  332. } //}}}
  333. //{{{ getHorizontalOffset() method
  334. /**
  335. * Returns the horizontal offset of drawn lines.
  336. */
  337. public final int getHorizontalOffset()
  338. {
  339. return horizontalOffset;
  340. } //}}}
  341. //{{{ setHorizontalOffset() method
  342. /**
  343. * Sets the horizontal offset of drawn lines. This can be used to
  344. * implement horizontal scrolling.
  345. * @param horizontalOffset offset The new horizontal offset
  346. */
  347. public void setHorizontalOffset(int horizontalOffset)
  348. {
  349. if(horizontalOffset == this.horizontalOffset)
  350. return;
  351. _setHorizontalOffset(horizontalOffset);
  352. view.synchroScrollHorizontal(this,horizontalOffset);
  353. } //}}}
  354. //{{{ _setHorizontalOffset() method
  355. public void _setHorizontalOffset(int horizontalOffset)
  356. {
  357. this.horizontalOffset = horizontalOffset;
  358. if(horizontalOffset != horizontal.getValue())
  359. updateScrollBars();
  360. painter.repaint();
  361. fireScrollEvent(false);
  362. } //}}}
  363. //{{{ updateScrollBars() method
  364. /**
  365. * Updates the state of the scroll bars. This should be called
  366. * if the number of lines in the buffer changes, or when the
  367. * size of the text are changes.
  368. */
  369. public void updateScrollBars()
  370. {
  371. if(vertical != null && visibleLines != 0)
  372. {
  373. // don't display stuff past the end of the buffer if
  374. // we can help it
  375. int lineCount = getVirtualLineCount();
  376. if(lineCount < firstLine + visibleLines)
  377. {
  378. // this will call updateScrollBars(), so
  379. // just return...
  380. int newFirstLine = Math.max(0,lineCount - visibleLines);
  381. if(newFirstLine != firstLine)
  382. {
  383. setFirstLine(newFirstLine);
  384. return;
  385. }
  386. }
  387. vertical.setValues(firstLine,visibleLines,0,lineCount);
  388. vertical.setUnitIncrement(2);
  389. vertical.setBlockIncrement(visibleLines);
  390. }
  391. int width = painter.getWidth();
  392. if(horizontal != null && width != 0)
  393. {
  394. maxHorizontalScrollWidth = 0;
  395. painter.repaint();
  396. horizontal.setUnitIncrement(painter.getFontMetrics()
  397. .charWidth('w'));
  398. horizontal.setBlockIncrement(width / 2);
  399. }
  400. } //}}}
  401. //{{{ scrollUpLine() method
  402. /**
  403. * Scrolls up by one line.
  404. * @since jEdit 2.7pre2
  405. */
  406. public void scrollUpLine()
  407. {
  408. if(firstLine > 0)
  409. setFirstLine(firstLine-1);
  410. else
  411. getToolkit().beep();
  412. } //}}}
  413. //{{{ scrollUpPage() method
  414. /**
  415. * Scrolls up by one page.
  416. * @since jEdit 2.7pre2
  417. */
  418. public void scrollUpPage()
  419. {
  420. if(firstLine > 0)
  421. {
  422. int newFirstLine = firstLine - visibleLines;
  423. setFirstLine(newFirstLine);
  424. }
  425. else
  426. {
  427. getToolkit().beep();
  428. }
  429. } //}}}
  430. //{{{ scrollDownLine() method
  431. /**
  432. * Scrolls down by one line.
  433. * @since jEdit 2.7pre2
  434. */
  435. public void scrollDownLine()
  436. {
  437. int numLines = getVirtualLineCount();
  438. if(firstLine + visibleLines < numLines)
  439. setFirstLine(firstLine + 1);
  440. else
  441. getToolkit().beep();
  442. } //}}}
  443. //{{{ scrollDownPage() method
  444. /**
  445. * Scrolls down by one page.
  446. * @since jEdit 2.7pre2
  447. */
  448. public void scrollDownPage()
  449. {
  450. int numLines = getVirtualLineCount();
  451. if(firstLine + visibleLines < numLines)
  452. {
  453. int newFirstLine = firstLine + visibleLines;
  454. setFirstLine(newFirstLine + visibleLines < numLines
  455. ? newFirstLine : numLines - visibleLines);
  456. }
  457. else
  458. {
  459. getToolkit().beep();
  460. }
  461. } //}}}
  462. //{{{ scrollToCaret() method
  463. /**
  464. * Ensures that the caret is visible by scrolling the text area if
  465. * necessary.
  466. * @param doElectricScroll If true, electric scrolling will be performed
  467. */
  468. public void scrollToCaret(boolean doElectricScroll)
  469. {
  470. scrollTo(caretLine,caret - buffer.getLineStartOffset(caretLine),
  471. doElectricScroll);
  472. } //}}}
  473. //{{{ scrollTo() method
  474. /**
  475. * Ensures that the specified location in the buffer is visible.
  476. * @param line The line number
  477. * @param offset The offset from the start of the line
  478. * @param doElectricScroll If true, electric scrolling will be performed
  479. * @since jEdit 4.0pre6
  480. */
  481. public void scrollTo(int line, int offset, boolean doElectricScroll)
  482. {
  483. int _electricScroll = (doElectricScroll ? electricScroll : 0);
  484. // visibleLines == 0 before the component is realized
  485. // we can't do any proper scrolling then, so we have
  486. // this hack...
  487. if(visibleLines == 0)
  488. {
  489. setFirstLine(physicalToVirtual(
  490. Math.max(0,line - _electricScroll)));
  491. return;
  492. }
  493. //{{{ STAGE 1 -- determine if the caret is visible.
  494. int screenLine = getScreenLineOfOffset(buffer.getLineStartOffset(line) + offset);
  495. Point point;
  496. if(screenLine != -1)
  497. {
  498. // It's visible, but is it too close to the borders?
  499. int height = painter.getFontMetrics().getHeight();
  500. Rectangle rect = new Rectangle(0,height * _electricScroll,
  501. painter.getWidth() - 5,visibleLines * height
  502. - height * _electricScroll * 2);
  503. point = offsetToXY(line,offset,returnValue);
  504. if(rect.contains(point))
  505. return;
  506. }
  507. else
  508. point = null;
  509. //}}}
  510. //{{{ STAGE 2 -- scroll vertically
  511. if(screenLine == -1)
  512. {
  513. int virtualLine = foldVisibilityManager.physicalToVirtual(line);
  514. if(virtualLine == firstLine - 1)
  515. {
  516. firstLine = Math.max(0,firstLine - _electricScroll - 1);
  517. physFirstLine = foldVisibilityManager.virtualToPhysical(firstLine);
  518. }
  519. else if(virtualLine == firstLine + visibleLines + 1)
  520. {
  521. firstLine = Math.max(0,
  522. Math.min(
  523. foldVisibilityManager.getVirtualLineCount() - visibleLines,
  524. firstLine + _electricScroll + 2));
  525. physFirstLine = foldVisibilityManager.virtualToPhysical(firstLine);
  526. }
  527. else
  528. {
  529. // keep chunking lines until we have visibleLines / 2
  530. if(virtualLine >= foldVisibilityManager.getVirtualLineCount()
  531. - visibleLines / 2)
  532. {
  533. firstLine = foldVisibilityManager.getVirtualLineCount()
  534. - visibleLines;
  535. physFirstLine = foldVisibilityManager
  536. .virtualToPhysical(firstLine);
  537. }
  538. else
  539. {
  540. physFirstLine = line;
  541. int count = 0;
  542. while(count <= visibleLines / 2)
  543. {
  544. if(foldVisibilityManager.isLineVisible(physFirstLine))
  545. count += chunkCache.getLineInfosForPhysicalLine(physFirstLine).length;
  546. if(physFirstLine == 0)
  547. break;
  548. else
  549. {
  550. physFirstLine = foldVisibilityManager
  551. .getPrevVisibleLine(physFirstLine);
  552. }
  553. }
  554. firstLine = physicalToVirtual(physFirstLine);
  555. }
  556. }
  557. }
  558. else if(screenLine < _electricScroll)
  559. {
  560. firstLine = Math.max(0,firstLine - _electricScroll + screenLine);
  561. physFirstLine = virtualToPhysical(firstLine);
  562. }
  563. else if(screenLine >= visibleLines - _electricScroll)
  564. {
  565. firstLine = Math.min(
  566. Math.max(0,foldVisibilityManager.getVirtualLineCount() - visibleLines),
  567. firstLine + screenLine - visibleLines + _electricScroll + 1);
  568. physFirstLine = virtualToPhysical(firstLine);
  569. }
  570. chunkCache.setFirstLine(firstLine);
  571. recalculateLastPhysicalLine();
  572. if(point == null)
  573. point = offsetToXY(line,offset,returnValue);
  574. //}}}
  575. //{{{ STAGE 3 -- scroll horizontally
  576. if(point.x < 0)
  577. {
  578. horizontalOffset = Math.min(0,horizontalOffset
  579. - point.x + charWidth + 5);
  580. }
  581. else if(point.x >= painter.getWidth() - charWidth - 5)
  582. {
  583. horizontalOffset = horizontalOffset +
  584. (painter.getWidth() - point.x)
  585. - charWidth - 5;
  586. } //}}}
  587. //{{{ STAGE 4 -- update some stuff
  588. updateScrollBars();
  589. painter.repaint();
  590. gutter.repaint();
  591. view.synchroScrollVertical(this,firstLine);
  592. view.synchroScrollHorizontal(this,horizontalOffset);
  593. // fire events for both a horizontal and vertical scroll
  594. fireScrollEvent(true);
  595. fireScrollEvent(false);
  596. //}}}
  597. } //}}}
  598. //{{{ addScrollListener() method
  599. /**
  600. * Adds a scroll listener to this text area.
  601. * @param listener The listener
  602. * @since jEdit 3.2pre2
  603. */
  604. public final void addScrollListener(ScrollListener listener)
  605. {
  606. listenerList.add(ScrollListener.class,listener);
  607. } //}}}
  608. //{{{ removeScrollListener() method
  609. /**
  610. * Removes a scroll listener from this text area.
  611. * @param listener The listener
  612. * @since jEdit 3.2pre2
  613. */
  614. public final void removeScrollListener(ScrollListener listener)
  615. {
  616. listenerList.remove(ScrollListener.class,listener);
  617. } //}}}
  618. //}}}
  619. //{{{ Screen line stuff
  620. //{{{ getPhysicalLineOfScreenLine() method
  621. /**
  622. * Returns the physical line number that contains the specified screen
  623. * line.
  624. * @param screenLine The screen line
  625. * @since jEdit 4.0pre6
  626. */
  627. public int getPhysicalLineOfScreenLine(int screenLine)
  628. {
  629. return chunkCache.getLineInfo(screenLine).physicalLine;
  630. } //}}}
  631. //{{{ getScreenLineOfOffset() method
  632. /**
  633. * Returns the screen (wrapped) line containing the specified offset.
  634. * @param offset The offset
  635. * @since jEdit 4.0pre4
  636. */
  637. public int getScreenLineOfOffset(int offset)
  638. {
  639. int line = buffer.getLineOfOffset(offset);
  640. offset -= buffer.getLineStartOffset(line);
  641. return chunkCache.getScreenLineOfOffset(line,offset);
  642. } //}}}
  643. //{{{ getScreenLineStartOffset() method
  644. /**
  645. * Returns the start offset of the specified screen (wrapped) line.
  646. * @param line The line
  647. * @since jEdit 4.0pre4
  648. */
  649. public int getScreenLineStartOffset(int line)
  650. {
  651. chunkCache.updateChunksUpTo(line);
  652. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  653. if(lineInfo.physicalLine == -1)
  654. return -1;
  655. return buffer.getLineStartOffset(lineInfo.physicalLine)
  656. + lineInfo.offset;
  657. } //}}}
  658. //{{{ getScreenLineEndOffset() method
  659. /**
  660. * Returns the end offset of the specified screen (wrapped) line.
  661. * @param line The line
  662. * @since jEdit 4.0pre4
  663. */
  664. public int getScreenLineEndOffset(int line)
  665. {
  666. chunkCache.updateChunksUpTo(line);
  667. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  668. if(lineInfo.physicalLine == -1)
  669. return -1;
  670. return buffer.getLineStartOffset(lineInfo.physicalLine)
  671. + lineInfo.offset + lineInfo.length;
  672. } //}}}
  673. //}}}
  674. //{{{ Offset conversion
  675. //{{{ xyToOffset() method
  676. /**
  677. * Converts a point to an offset.
  678. * Note that unlike in previous jEdit versions, this method now returns
  679. * -1 if the y co-ordinate is out of bounds.
  680. *
  681. * @param x The x co-ordinate of the point
  682. * @param y The y co-ordinate of the point
  683. */
  684. public int xyToOffset(int x, int y)
  685. {
  686. return xyToOffset(x,y,true);
  687. } //}}}
  688. //{{{ xyToOffset() method
  689. /**
  690. * Converts a point to an offset.
  691. * Note that unlike in previous jEdit versions, this method now returns
  692. * -1 if the y co-ordinate is out of bounds.
  693. *
  694. * @param x The x co-ordinate of the point
  695. * @param y The y co-ordinate of the point
  696. * @param round Round up to next letter if past the middle of a letter?
  697. * @since jEdit 3.2pre6
  698. */
  699. public int xyToOffset(int x, int y, boolean round)
  700. {
  701. FontMetrics fm = painter.getFontMetrics();
  702. int height = fm.getHeight();
  703. int line = y / height;
  704. if(line < 0 || line > visibleLines)
  705. return -1;
  706. chunkCache.updateChunksUpTo(line);
  707. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  708. if(!lineInfo.chunksValid)
  709. System.err.println("xy to offset: not valid");
  710. if(lineInfo.physicalLine == -1)
  711. {
  712. return getLineEndOffset(foldVisibilityManager
  713. .getLastVisibleLine()) - 1;
  714. }
  715. else
  716. {
  717. int offset = chunkCache.xToOffset(lineInfo.chunks,
  718. x - horizontalOffset,round);
  719. if(offset == -1 || offset == lineInfo.offset + lineInfo.length)
  720. offset = lineInfo.offset + lineInfo.length - 1;
  721. return getLineStartOffset(lineInfo.physicalLine) + offset;
  722. }
  723. } //}}}
  724. //{{{ offsetToXY() method
  725. /**
  726. * Converts an offset into a point in the text area painter's
  727. * co-ordinate space.
  728. * @param offset The offset
  729. */
  730. public Point offsetToXY(int offset)
  731. {
  732. int line = buffer.getLineOfOffset(offset);
  733. offset -= buffer.getLineStartOffset(line);
  734. Point retVal = new Point();
  735. return offsetToXY(line,offset,retVal);
  736. } //}}}
  737. //{{{ offsetToXY() method
  738. /**
  739. * Converts an offset into a point in the text area painter's
  740. * co-ordinate space.
  741. * @param line The physical line number
  742. * @param offset The offset, from the start of the line
  743. * @param retVal The point to store the return value in
  744. * @return <code>retVal</code> for convenience
  745. * @since jEdit 4.0pre4
  746. */
  747. public Point offsetToXY(int line, int offset, Point retVal)
  748. {
  749. int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
  750. if(screenLine == -1)
  751. {
  752. if(line < firstLine)
  753. screenLine = 0;
  754. else if(line > physLastLine)
  755. screenLine = visibleLines;
  756. chunkCache.updateChunksUpTo(screenLine);
  757. }
  758. FontMetrics fm = painter.getFontMetrics();
  759. retVal.y = screenLine * fm.getHeight();
  760. ChunkCache.LineInfo info = chunkCache.getLineInfo(screenLine);
  761. if(!info.chunksValid)
  762. System.err.println("offset to xy: not valid");
  763. retVal.x = (int)(horizontalOffset + chunkCache.offsetToX(
  764. info.chunks,offset));
  765. return retVal;
  766. } //}}}
  767. //}}}
  768. //{{{ Painting
  769. //{{{ invalidateScreenLineRange() method
  770. /**
  771. * Marks a range of screen lines as needing a repaint.
  772. * @param start The first line
  773. * @param end The last line
  774. * @since jEdit 4.0pre4
  775. */
  776. public void invalidateScreenLineRange(int start, int end)
  777. {
  778. if(chunkCache.needFullRepaint())
  779. {
  780. recalculateLastPhysicalLine();
  781. gutter.repaint();
  782. painter.repaint();
  783. return;
  784. }
  785. if(start > end)
  786. {
  787. int tmp = end;
  788. end = start;
  789. start = tmp;
  790. }
  791. FontMetrics fm = painter.getFontMetrics();
  792. int y = start * fm.getHeight();
  793. int height = (end - start + 1) * fm.getHeight();
  794. painter.repaint(0,y,painter.getWidth(),height);
  795. gutter.repaint(0,y,gutter.getWidth(),height);
  796. } //}}}
  797. //{{{ invalidateLine() method
  798. /**
  799. * Marks a line as needing a repaint.
  800. * @param line The physical line to invalidate
  801. */
  802. public void invalidateLine(int line)
  803. {
  804. if(line < physFirstLine || line > physLastLine
  805. || !foldVisibilityManager.isLineVisible(line))
  806. return;
  807. int startLine = -1;
  808. int endLine = -1;
  809. for(int i = 0; i <= visibleLines; i++)
  810. {
  811. chunkCache.updateChunksUpTo(i);
  812. ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
  813. if((info.physicalLine >= line || info.physicalLine == -1)
  814. && startLine == -1)
  815. {
  816. startLine = i;
  817. }
  818. if((info.physicalLine >= line && info.lastSubregion)
  819. || info.physicalLine == -1)
  820. {
  821. endLine = i;
  822. break;
  823. }
  824. }
  825. if(chunkCache.needFullRepaint())
  826. {
  827. recalculateLastPhysicalLine();
  828. endLine = visibleLines;
  829. }
  830. else if(endLine == -1)
  831. endLine = visibleLines;
  832. //if(startLine != endLine)
  833. // System.err.println(startLine + ":" + endLine);
  834. invalidateScreenLineRange(startLine,endLine);
  835. } //}}}
  836. //{{{ invalidateLineRange() method
  837. /**
  838. * Marks a range of physical lines as needing a repaint.
  839. * @param start The first line to invalidate
  840. * @param end The last line to invalidate
  841. */
  842. public void invalidateLineRange(int start, int end)
  843. {
  844. if(end < start)
  845. {
  846. int tmp = end;
  847. end = start;
  848. start = tmp;
  849. }
  850. if(end < physFirstLine || start > physLastLine)
  851. return;
  852. int startScreenLine = -1;
  853. int endScreenLine = -1;
  854. for(int i = 0; i <= visibleLines; i++)
  855. {
  856. chunkCache.updateChunksUpTo(i);
  857. ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
  858. if((info.physicalLine >= start || info.physicalLine == -1)
  859. && startScreenLine == -1)
  860. {
  861. startScreenLine = i;
  862. }
  863. if((info.physicalLine >= end && info.lastSubregion)
  864. || info.physicalLine == -1)
  865. {
  866. endScreenLine = i;
  867. break;
  868. }
  869. }
  870. if(startScreenLine == -1)
  871. startScreenLine = 0;
  872. if(chunkCache.needFullRepaint())
  873. {
  874. recalculateLastPhysicalLine();
  875. endScreenLine = visibleLines;
  876. }
  877. else if(endScreenLine == -1)
  878. endScreenLine = visibleLines;
  879. invalidateScreenLineRange(startScreenLine,endScreenLine);
  880. } //}}}
  881. //{{{ invalidateSelectedLines() method
  882. /**
  883. * Repaints the lines containing the selection.
  884. */
  885. public void invalidateSelectedLines()
  886. {
  887. // to hide line highlight if selections are being added later on
  888. invalidateLine(caretLine);
  889. for(int i = 0; i < selection.size(); i++)
  890. {
  891. Selection s = (Selection)selection.elementAt(i);
  892. invalidateLineRange(s.startLine,s.endLine);
  893. }
  894. } //}}}
  895. //}}}
  896. //{{{ Convenience methods
  897. //{{{ physicalToVirtual() method
  898. /**
  899. * Converts a physical line number to a virtual line number.
  900. * @param line A physical line index
  901. * @since jEdit 4.0pre1
  902. */
  903. public int physicalToVirtual(int line)
  904. {
  905. return foldVisibilityManager.physicalToVirtual(line);
  906. } //}}}
  907. //{{{ virtualToPhysical() method
  908. /**
  909. * Converts a virtual line number to a physical line number.
  910. * @param line A virtual line index
  911. * @since jEdit 4.0pre1
  912. */
  913. public int virtualToPhysical(int line)
  914. {
  915. return foldVisibilityManager.virtualToPhysical(line);
  916. } //}}}
  917. //{{{ getBufferLength() method
  918. /**
  919. * Returns the length of the buffer.
  920. */
  921. public final int getBufferLength()
  922. {
  923. return buffer.getLength();
  924. } //}}}
  925. //{{{ getLineCount() method
  926. /**
  927. * Returns the number of physical lines in the buffer.
  928. */
  929. public final int getLineCount()
  930. {
  931. return buffer.getLineCount();
  932. } //}}}
  933. //{{{ getVirtualLineCount() method
  934. /**
  935. * Returns the number of virtual lines in the buffer.
  936. */
  937. public final int getVirtualLineCount()
  938. {
  939. return foldVisibilityManager.getVirtualLineCount();
  940. } //}}}
  941. //{{{ getLineOfOffset() method
  942. /**
  943. * Returns the line containing the specified offset.
  944. * @param offset The offset
  945. */
  946. public final int getLineOfOffset(int offset)
  947. {
  948. return buffer.getLineOfOffset(offset);
  949. } //}}}
  950. //{{{ getLineStartOffset() method
  951. /**
  952. * Returns the start offset of the specified line.
  953. * @param line The line
  954. * @return The start offset of the specified line, or -1 if the line is
  955. * invalid
  956. */
  957. public int getLineStartOffset(int line)
  958. {
  959. return buffer.getLineStartOffset(line);
  960. } //}}}
  961. //{{{ getLineEndOffset() method
  962. /**
  963. * Returns the end offset of the specified line.
  964. * @param line The line
  965. * @return The end offset of the specified line, or -1 if the line is
  966. * invalid.
  967. */
  968. public int getLineEndOffset(int line)
  969. {
  970. return buffer.getLineEndOffset(line);
  971. } //}}}
  972. //{{{ getLineLength() method
  973. /**
  974. * Returns the length of the specified line.
  975. * @param line The line
  976. */
  977. public int getLineLength(int line)
  978. {
  979. return buffer.getLineLength(line);
  980. } //}}}
  981. //{{{ getText() method
  982. /**
  983. * Returns the specified substring of the buffer.
  984. * @param start The start offset
  985. * @param len The length of the substring
  986. * @return The substring
  987. */
  988. public final String getText(int start, int len)
  989. {
  990. return buffer.getText(start,len);
  991. } //}}}
  992. //{{{ getText() method
  993. /**
  994. * Copies the specified substring of the buffer into a segment.
  995. * @param start The start offset
  996. * @param len The length of the substring
  997. * @param segment The segment
  998. */
  999. public final void getText(int start, int len, Segment segment)
  1000. {
  1001. buffer.getText(start,len,segment);
  1002. } //}}}
  1003. //{{{ getLineText() method
  1004. /**
  1005. * Returns the text on the specified line.
  1006. * @param lineIndex The line
  1007. * @return The text, or null if the line is invalid
  1008. */
  1009. public final String getLineText(int lineIndex)
  1010. {
  1011. return buffer.getLineText(lineIndex);
  1012. } //}}}
  1013. //{{{ getLineText() method
  1014. /**
  1015. * Copies the text on the specified line into a segment. If the line
  1016. * is invalid, the segment will contain a null string.
  1017. * @param lineIndex The line
  1018. */
  1019. public final void getLineText(int lineIndex, Segment segment)
  1020. {
  1021. buffer.getLineText(lineIndex,segment);
  1022. } //}}}
  1023. //{{{ getText() method
  1024. /**
  1025. * Returns the entire text of this text area.
  1026. */
  1027. public String getText()
  1028. {
  1029. return buffer.getText(0,buffer.getLength());
  1030. } //}}}
  1031. //{{{ setText() method
  1032. /**
  1033. * Sets the entire text of this text area.
  1034. */
  1035. public void setText(String text)
  1036. {
  1037. try
  1038. {
  1039. buffer.beginCompoundEdit();
  1040. buffer.remove(0,buffer.getLength());
  1041. buffer.insert(0,text);
  1042. }
  1043. finally
  1044. {
  1045. buffer.endCompoundEdit();
  1046. }
  1047. } //}}}
  1048. //}}}
  1049. //{{{ Selection
  1050. //{{{ selectAll() method
  1051. /**
  1052. * Selects all text in the buffer.
  1053. */
  1054. public final void selectAll()
  1055. {
  1056. setSelection(new Selection.Range(0,buffer.getLength()));
  1057. moveCaretPosition(buffer.getLength(),true);
  1058. } //}}}
  1059. //{{{ selectLine() method
  1060. /**
  1061. * Selects the current line.
  1062. * @since jEdit 2.7pre2
  1063. */
  1064. public void selectLine()
  1065. {
  1066. int caretLine = getCaretLine();
  1067. int start = getLineStartOffset(caretLine);
  1068. int end = getLineEndOffset(caretLine) - 1;
  1069. Selection s = new Selection.Range(start,end);
  1070. if(multi)
  1071. addToSelection(s);
  1072. else
  1073. setSelection(s);
  1074. moveCaretPosition(end);
  1075. } //}}}
  1076. //{{{ selectParagraph() method
  1077. /**
  1078. * Selects the paragraph at the caret position.
  1079. * @since jEdit 2.7pre2
  1080. */
  1081. public void selectParagraph()
  1082. {
  1083. int caretLine = getCaretLine();
  1084. if(getLineLength(caretLine) == 0)
  1085. {
  1086. view.getToolkit().beep();
  1087. return;
  1088. }
  1089. int start = caretLine;
  1090. int end = caretLine;
  1091. while(start >= 0)
  1092. {
  1093. if(getLineLength(start) == 0)
  1094. break;
  1095. else
  1096. start--;
  1097. }
  1098. while(end < getLineCount())
  1099. {
  1100. if(getLineLength(end) == 0)
  1101. break;
  1102. else
  1103. end++;
  1104. }
  1105. int selectionStart = getLineStartOffset(start + 1);
  1106. int selectionEnd = getLineEndOffset(end - 1) - 1;
  1107. Selection s = new Selection.Range(selectionStart,selectionEnd);
  1108. if(multi)
  1109. addToSelection(s);
  1110. else
  1111. setSelection(s);
  1112. moveCaretPosition(selectionEnd);
  1113. } //}}}
  1114. //{{{ selectWord() method
  1115. /**
  1116. * Selects the word at the caret position.
  1117. * @since jEdit 2.7pre2
  1118. */
  1119. public void selectWord()
  1120. {
  1121. int line = getCaretLine();
  1122. int lineStart = getLineStartOffset(line);
  1123. int offset = getCaretPosition() - lineStart;
  1124. if(getLineLength(line) == 0)
  1125. return;
  1126. String lineText = getLineText(line);
  1127. String noWordSep = buffer.getStringProperty("noWordSep");
  1128. if(offset == getLineLength(line))
  1129. offset--;
  1130. int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep);
  1131. int wordEnd = TextUtilities.findWordEnd(lineText,offset+1,noWordSep);
  1132. Selection s = new Selection.Range(lineStart + wordStart,
  1133. lineStart + wordEnd);
  1134. if(multi)
  1135. addToSelection(s);
  1136. else
  1137. setSelection(s);
  1138. moveCaretPosition(lineStart + wordEnd);
  1139. } //}}}
  1140. //{{{ selectToMatchingBracket() method
  1141. /**
  1142. * Selects from the bracket at the caret position to the corresponding
  1143. * bracket.
  1144. * @since jEdit 4.0pre2
  1145. */
  1146. public void selectToMatchingBracket()
  1147. {
  1148. int bracket = TextUtilities.findMatchingBracket(buffer,caretLine,
  1149. Math.max(0,caret - buffer.getLineStartOffset(caretLine)));
  1150. if(bracket != -1)
  1151. {
  1152. Selection s;
  1153. if(bracket < caret)
  1154. s = new Selection.Range(++bracket,caret);
  1155. else
  1156. {
  1157. moveCaretPosition(caret + 1,false);
  1158. s = new Selection.Range(caret,bracket);
  1159. }
  1160. addToSelection(s);
  1161. return;
  1162. }
  1163. } //}}}
  1164. //{{{ selectBlock() method
  1165. /**
  1166. * Selects the code block surrounding the caret.
  1167. * @since jEdit 2.7pre2
  1168. */
  1169. public void selectBlock()
  1170. {
  1171. String openBrackets = "([{";
  1172. String closeBrackets = ")]}";
  1173. Selection s = getSelectionAtOffset(caret);
  1174. int start, end;
  1175. if(s == null)
  1176. start = end = caret;
  1177. else
  1178. {
  1179. start = s.start;
  1180. end = s.end;
  1181. }
  1182. String text = getText(0,buffer.getLength());
  1183. // Scan backwards, trying to find a bracket
  1184. int count = 1;
  1185. char openBracket = '\0';
  1186. char closeBracket = '\0';
  1187. // We can't do the backward scan if start == 0
  1188. if(start == 0)
  1189. {
  1190. view.getToolkit().beep();
  1191. return;
  1192. }
  1193. backward_scan: while(--start > 0)
  1194. {
  1195. char c = text.charAt(start);
  1196. int index = openBrackets.indexOf(c);
  1197. if(index != -1)
  1198. {
  1199. if(--count == 0)
  1200. {
  1201. openBracket = c;
  1202. closeBracket = closeBrackets.charAt(index);
  1203. break backward_scan;
  1204. }
  1205. }
  1206. else if(closeBrackets.indexOf(c) != -1)
  1207. count++;
  1208. }
  1209. // Reset count
  1210. count = 1;
  1211. // Scan forward, matching that bracket
  1212. if(openBracket == '\0')
  1213. {
  1214. getToolkit().beep();
  1215. return;
  1216. }
  1217. else
  1218. {
  1219. forward_scan: do
  1220. {
  1221. char c = text.charAt(end);
  1222. if(c == closeBracket)
  1223. {
  1224. if(--count == 0)
  1225. {
  1226. end++;
  1227. break forward_scan;
  1228. }
  1229. }
  1230. else if(c == openBracket)
  1231. count++;
  1232. }
  1233. while(++end < buffer.getLength());
  1234. }
  1235. s = new Selection.Range(start,end);
  1236. if(multi)
  1237. addToSelection(s);
  1238. else
  1239. setSelection(s);
  1240. moveCaretPosition(end);
  1241. } //}}}
  1242. //{{{ invertSelection() method
  1243. /**
  1244. * Inverts the selection.
  1245. * @since jEdit 4.0pre1
  1246. */
  1247. public final void invertSelection()
  1248. {
  1249. Selection[] newSelection = new Selection[selection.size() + 1];
  1250. int lastOffset = 0;
  1251. for(int i = 0; i < selection.size(); i++)
  1252. {
  1253. Selection s = (Selection)selection.elementAt(i);
  1254. newSelection[i] = new Selection.Range(lastOffset,
  1255. s.getStart());
  1256. lastOffset = s.getEnd();
  1257. }
  1258. newSelection[selection.size()] = new Selection.Range(
  1259. lastOffset,buffer.getLength());
  1260. setSelection(newSelection);
  1261. } //}}}
  1262. //{{{ getSelectionCount() method
  1263. /**
  1264. * Returns the number of selections. This is primarily for use by the
  1265. * the status bar.
  1266. * @since jEdit 3.2pre2
  1267. */
  1268. public int getSelectionCount()
  1269. {
  1270. return selection.size();
  1271. } //}}}
  1272. //{{{ getSelection() method
  1273. /**
  1274. * Returns the current selection.
  1275. * @since jEdit 3.2pre1
  1276. */
  1277. public Selection[] getSelection()
  1278. {
  1279. Selection[] sel = new Selection[selection.size()];
  1280. selection.copyInto(sel);
  1281. return sel;
  1282. } //}}}
  1283. //{{{ selectNone() method
  1284. /**
  1285. * Deselects everything.
  1286. */
  1287. public void selectNone()
  1288. {
  1289. setSelection((Selection)null);
  1290. } //}}}
  1291. //{{{ setSelection() method
  1292. /**
  1293. * Sets the selection.
  1294. * @param selection The new selection
  1295. * since jEdit 3.2pre1
  1296. */
  1297. public void setSelection(Selection[] selection)
  1298. {
  1299. // invalidate the old selection
  1300. invalidateSelectedLines();
  1301. this.selection.removeAllElements();
  1302. if(selection != null)
  1303. {
  1304. for(int i = 0; i < selection.length; i++)
  1305. _addToSelection(selection[i]);
  1306. }
  1307. fireCaretEvent();
  1308. } //}}}
  1309. //{{{ setSelection() method
  1310. /**
  1311. * Sets the selection.
  1312. * @param selection The new selection
  1313. * since jEdit 3.2pre1
  1314. */
  1315. public void setSelection(Selection selection)
  1316. {
  1317. invalidateSelectedLines();
  1318. this.selection.removeAllElements();
  1319. if(selection != null)
  1320. _addToSelection(selection);
  1321. fireCaretEvent();
  1322. } //}}}
  1323. //{{{ addToSelection() method
  1324. /**
  1325. * Adds to the selection.
  1326. * @param selection The new selection
  1327. * since jEdit 3.2pre1
  1328. */
  1329. public void addToSelection(Selection[] selection)
  1330. {
  1331. if(selection != null)
  1332. {
  1333. for(int i = 0; i < selection.length; i++)
  1334. _addToSelection(selection[i]);
  1335. }
  1336. // to hide current line highlight
  1337. invalidateLine(caretLine);
  1338. fireCaretEvent();
  1339. } //}}}
  1340. //{{{ addToSelection() method
  1341. /**
  1342. * Adds to the selection.
  1343. * @param selection The new selection
  1344. * since jEdit 3.2pre1
  1345. */
  1346. public void addToSelection(Selection selection)
  1347. {
  1348. _addToSelection(selection);
  1349. // to hide current line highlight
  1350. invalidateLine(caretLine);
  1351. fireCaretEvent();
  1352. } //}}}
  1353. //{{{ getSelectionAtOffset() method
  1354. /**
  1355. * Returns the selection containing the specific offset, or null
  1356. * if there is no selection at that offset.
  1357. * @param offset The offset
  1358. * @since jEdit 3.2pre1
  1359. */
  1360. public Selection getSelectionAtOffset(int offset)
  1361. {
  1362. if(selection != null)
  1363. {
  1364. for(int i = 0; i < selection.size(); i++)
  1365. {
  1366. Selection s = (Selection)selection.elementAt(i);
  1367. if(offset >= s.start && offset <= s.end)
  1368. return s;
  1369. }
  1370. }
  1371. return null;
  1372. } //}}}
  1373. //{{{ removeFromSelection() method
  1374. /**
  1375. * Deactivates the specified selection.
  1376. * @param s The selection
  1377. * @since jEdit 3.2pre1
  1378. */
  1379. public void removeFromSelection(Selection sel)
  1380. {
  1381. selection.removeElement(sel);
  1382. invalidateLineRange(sel.startLine,sel.endLine);
  1383. // to hide current line highlight
  1384. invalidateLine(caretLine);
  1385. fireCaretEvent();
  1386. } //}}}
  1387. //{{{ removeFromSelection() method
  1388. /**
  1389. * Deactivates the selection at the specified offset. If there is
  1390. * no selection at that offset, does nothing.
  1391. * @param offset The offset
  1392. * @since jEdit 3.2pre1
  1393. */
  1394. public void removeFromSelection(int offset)
  1395. {
  1396. Selection sel = getSelectionAtOffset(offset);
  1397. if(sel == null)
  1398. return;
  1399. selection.removeElement(sel);
  1400. invalidateLineRange(sel.startLine,sel.endLine);
  1401. // to hide current line highlight
  1402. invalidateLine(caretLine);
  1403. fireCaretEvent();
  1404. } //}}}
  1405. //{{{ resizeSelection() method
  1406. /**
  1407. * Resizes the selection at the specified offset, or creates a new
  1408. * one if there is no selection at the specified offset. This is a
  1409. * utility method that is mainly useful in the mouse event handler
  1410. * because it handles the case of end being before offset gracefully
  1411. * (unlike the rest of the selection API).
  1412. * @param offset The offset
  1413. * @param end The new selection end
  1414. * @param rect Make the selection rectangular?
  1415. * @since jEdit 3.2pre1
  1416. */
  1417. public void resizeSelection(int offset, int end, boolean rect)
  1418. {
  1419. Selection s = getSelectionAtOffset(offset);
  1420. if(s != null)
  1421. {
  1422. invalidateLineRange(s.startLine,s.endLine);
  1423. selection.removeElement(s);
  1424. }
  1425. if(end < offset)
  1426. {
  1427. int tmp = offset;
  1428. offset = end;
  1429. end = tmp;
  1430. }
  1431. Selection newSel;
  1432. if(rect)
  1433. newSel = new Selection.Rect(offset,end);
  1434. else
  1435. newSel = new Selection.Range(offset,end);
  1436. _addToSelection(newSel);
  1437. fireCaretEvent();
  1438. } //}}}
  1439. //{{{ extendSelection() method
  1440. /**
  1441. * Extends the selection at the specified offset, or creates a new
  1442. * one if there is no selection at the specified offset. This is
  1443. * different from resizing in that the new chunk is added to the
  1444. * selection in question, instead of replacing it.
  1445. * @param offset The offset
  1446. * @param end The new selection end
  1447. * @param rect Make the selection rectangular?
  1448. * @since jEdit 3.2pre1
  1449. */
  1450. public void extendSelection(int offset, int end)
  1451. {
  1452. Selection s = getSelectionAtOffset(offset);
  1453. if(s != null)
  1454. {
  1455. invalidateLineRange(s.startLine,s.endLine);
  1456. selection.removeElement(s);
  1457. if(offset == s.start)
  1458. {
  1459. offset = end;
  1460. end = s.end;
  1461. }
  1462. else if(offset == s.end)
  1463. {
  1464. offset = s.start;
  1465. }
  1466. }
  1467. if(end < offset)
  1468. {
  1469. int tmp = end;
  1470. end = offset;
  1471. offset = tmp;
  1472. }
  1473. _addToSelection(new Selection.Range(offset,end));
  1474. fireCaretEvent();
  1475. } //}}}
  1476. //{{{ getSelectedText() method
  1477. /**
  1478. * Returns the text in the specified selection.
  1479. * @param s The selection
  1480. * @since jEdit 3.2pre1
  1481. */
  1482. public String getSelectedText(Selection s)
  1483. {
  1484. StringBuffer buf = new StringBuffer();
  1485. getSelectedText(s,buf);
  1486. return buf.toString();
  1487. } //}}}
  1488. //{{{ getSelectedText() method
  1489. /**
  1490. * Returns the text in all active selections.
  1491. * @param separator The string to insert between each text chunk
  1492. * (for example, a newline)
  1493. * @since jEdit 3.2pre1
  1494. */
  1495. public String getSelectedText(String separator)
  1496. {
  1497. if(selection.size() == 0)
  1498. return null;
  1499. StringBuffer buf = new StringBuffer();
  1500. for(int i = 0; i < selection.size(); i++)
  1501. {
  1502. if(i != 0)
  1503. buf.append(separator);
  1504. getSelectedText((Selection)selection.elementAt(i),buf);
  1505. }
  1506. return buf.toString();
  1507. } //}}}
  1508. //{{{ getSelectedText() method
  1509. /**
  1510. * Returns the text in all active selections, with a newline
  1511. * between each text chunk.
  1512. */
  1513. public String getSelectedText()
  1514. {
  1515. return getSelectedText("\n");
  1516. } //}}}
  1517. //{{{ setSelectedText() method
  1518. /**
  1519. * Replaces the selection with the specified text.
  1520. * @param s The selection
  1521. * @param selectedText The new text
  1522. * @since jEdit 3.2pre1
  1523. */
  1524. public void setSelectedText(Selection s, String selectedText)
  1525. {
  1526. if(!isEditable())
  1527. {
  1528. throw new InternalError("Text component"
  1529. + " read only");
  1530. }
  1531. try
  1532. {
  1533. buffer.beginCompoundEdit();
  1534. if(s instanceof Selection.Rect)
  1535. {
  1536. int start = s.start - getLineStartOffset(s.startLine);
  1537. int end = s.end - getLineStartOffset(s.endLine);
  1538. // Certain rectangles satisfy this condition...
  1539. if(end < start)
  1540. {
  1541. int tmp = end;
  1542. end = start;
  1543. start = tmp;
  1544. }
  1545. int lastNewline = 0;
  1546. int currNewline = 0;
  1547. for(int i = s.startLine; i <= s.endLine; i++)
  1548. {
  1549. int lineStart = getLineStartOffset(i);
  1550. int lineEnd = getLineEndOffset(i) - 1;
  1551. int rectStart = Math.min(lineEnd,lineStart + start);
  1552. buffer.remove(rectStart,Math.min(lineEnd - rectStart,
  1553. end - start));
  1554. if(selectedText == null)
  1555. continue;
  1556. currNewline = selectedText.indexOf('\n',lastNewline);
  1557. if(currNewline == -1)
  1558. currNewline = selectedText.length();
  1559. buffer.insert(rectStart,selectedText
  1560. .substring(lastNewline,currNewline));
  1561. lastNewline = Math.min(selectedText.length(),
  1562. currNewline + 1);
  1563. }
  1564. if(selectedText != null &&
  1565. currNewline != selectedText.length())
  1566. {
  1567. int offset = getLineEndOffset(s.endLine) - 1;
  1568. buffer.insert(offset,"\n");
  1569. buffer.insert(offset + 1,selectedText
  1570. .substring(currNewline + 1));
  1571. }
  1572. }
  1573. else
  1574. {
  1575. buffer.remove(s.start,s.end - s.start);
  1576. if(selectedText != null && selectedText.length() != 0)
  1577. {
  1578. buffer.insert(s.start,selectedText);
  1579. }
  1580. }
  1581. }
  1582. // No matter what happends... stops us from leaving buffer
  1583. // in a bad state
  1584. finally
  1585. {
  1586. buffer.endCompoundEdit();
  1587. }
  1588. // no no no!!!!
  1589. //selectNone();
  1590. } //}}}
  1591. //{{{ setSelectedText() method
  1592. /**
  1593. * Replaces the selection at the caret with the specified text.
  1594. * If there is no selection at the caret, the text is inserted at
  1595. * the caret position.
  1596. */
  1597. public void setSelectedText(String selectedText)
  1598. {
  1599. if(!isEditable())
  1600. {
  1601. throw new InternalError("Text component"
  1602. + " read only");
  1603. }
  1604. Selection[] selection = getSelection();
  1605. if(selection.length == 0)
  1606. {
  1607. // for compatibility with older jEdit versions
  1608. buffer.insert(caret,selectedText);
  1609. }
  1610. else
  1611. {
  1612. try
  1613. {
  1614. buffer.beginCompoundEdit();
  1615. for(int i = 0; i < selection.length; i++)
  1616. {
  1617. setSelectedText(selection[i],selectedText);
  1618. }
  1619. }
  1620. finally
  1621. {
  1622. buffer.endCompoundEdit();
  1623. }
  1624. }
  1625. selectNone();
  1626. } //}}}
  1627. //{{{ getSelectedLines() method
  1628. /**
  1629. * Returns an array of all line numbers that contain a selection.
  1630. * This array will also include the line number containing the
  1631. * caret, for convinience.
  1632. * @since jEdit 3.2pre1
  1633. */
  1634. public int[] getSelectedLines()
  1635. {
  1636. if(selection.size() == 0)
  1637. return new int[] { caretLine };
  1638. Integer line;
  1639. Hashtable hash = new Hashtable();
  1640. for(int i = 0; i < selection.size(); i++)
  1641. {
  1642. Selection s = (Selection)selection.elementAt(i);
  1643. int endLine = (s.end == getLineStartOffset(s.endLine)
  1644. ? s.endLine - 1
  1645. : s.endLine);
  1646. for(int j = s.startLine; j <= endLine; j++)
  1647. {
  1648. line = new Integer(j);
  1649. hash.put(line,line);
  1650. }
  1651. }
  1652. int[] returnValue = new int[hash.size()];
  1653. int i = 0;
  1654. Enumeration keys = hash.keys();
  1655. while(keys.hasMoreElements())
  1656. {
  1657. line = (Integer)keys.nextElement();
  1658. returnValue[i++] = line.intValue();
  1659. }
  1660. Arrays.sort(returnValue);
  1661. return returnValue;
  1662. } //}}}
  1663. //{{{ showSelectLineRangeDialog() method
  1664. /**
  1665. * Displays the 'select line range' dialog box, and selects the
  1666. * specified range of lines.
  1667. * @since jEdit 2.7pre2
  1668. */
  1669. public void showSelectLineRangeDialog()
  1670. {
  1671. new SelectLineRange(view);
  1672. } //}}}
  1673. //}}}
  1674. //{{{ Caret
  1675. //{{{ blinkCaret() method
  1676. /**
  1677. * Blinks the caret.
  1678. */
  1679. public final void blinkCaret()
  1680. {
  1681. if(caretBlinks)
  1682. {
  1683. blink = !blink;
  1684. invalidateLine(caretLine);
  1685. }
  1686. else
  1687. blink = true;
  1688. } //}}}
  1689. //{{{ centerCaret() method
  1690. /**
  1691. * Centers the caret on the screen.
  1692. * @since jEdit 2.7pre2
  1693. */
  1694. public void centerCaret()
  1695. {
  1696. int offset = getScreenLineStartOffset(visibleLines / 2);
  1697. if(offset == -1)
  1698. getToolkit().beep();
  1699. else
  1700. setCaretPosition(offset);
  1701. } //}}}
  1702. //{{{ setCaretPosition() method
  1703. /**
  1704. * Sets the caret position and deactivates the selection.
  1705. * @param caret The caret position
  1706. */
  1707. public void setCaretPosition(int newCaret)
  1708. {
  1709. invalidateSelectedLines();
  1710. selection.removeAllElements();
  1711. moveCaretPosition(newCaret,true);
  1712. } //}}}
  1713. //{{{ setCaretPosition() method
  1714. /**
  1715. * Sets the caret position and deactivates the selection.
  1716. * @param caret The caret position
  1717. * @param doElectricScroll Do electric scrolling?
  1718. */
  1719. public void setCaretPosition(int newCaret, boolean doElectricScroll)
  1720. {
  1721. invalidateSelectedLines();
  1722. selection.removeAllElements();
  1723. moveCaretPosition(newCaret,doElectricScroll);
  1724. } //}}}
  1725. //{{{ moveCaretPosition() method
  1726. /**
  1727. * Sets the caret position without deactivating the selection.
  1728. * @param caret The caret position
  1729. */
  1730. public void moveCaretPosition(int newCaret)
  1731. {
  1732. moveCaretPosition(newCaret,true);
  1733. } //}}}
  1734. //{{{ moveCaretPosition() method
  1735. /**
  1736. * Sets the caret position without deactivating the selection.
  1737. * @param caret The caret position
  1738. * @param doElectricScroll Do electric scrolling?
  1739. */
  1740. public void moveCaretPosition(int newCaret, boolean doElectricScroll)
  1741. {
  1742. if(newCaret < 0 || newCaret > buffer.getLength())
  1743. {
  1744. throw new IllegalArgumentException("caret out of bounds: "
  1745. + newCaret);
  1746. }
  1747. // When the user is typing, etc, we don't want the caret
  1748. // to blink
  1749. blink = true;
  1750. caretTimer.restart();
  1751. if(caret == newCaret)
  1752. {
  1753. finishCaretUpdate(doElectricScroll,false);
  1754. return;
  1755. }
  1756. int newCaretLine = getLineOfOffset(newCaret);
  1757. magicCaret = -1;
  1758. if(!foldVisibilityManager.isLineVisible(newCaretLine))
  1759. {
  1760. if(foldVisibilityManager.isNarrowed())
  1761. {
  1762. int collapseFolds = buffer.getIntegerProperty(
  1763. "collapseFolds",0);
  1764. if(collapseFolds != 0)
  1765. foldVisibilityManager.expandFolds(collapseFolds);
  1766. else
  1767. foldVisibilityManager.expandAllFolds();
  1768. }
  1769. else
  1770. foldVisibilityManager.expandFold(newCaretLine,false);
  1771. }
  1772. if(caretLine == newCaretLine)
  1773. {
  1774. if(caretScreenLine != -1)
  1775. invalidateScreenLineRange(caretScreenLine,caretScreenLine);
  1776. }
  1777. else
  1778. {
  1779. caretScreenLine = chunkCache.getScreenLineOfOffset(newCaretLine,
  1780. newCaret - buffer.getLineStartOffset(newCaretLine));
  1781. invalidateLineRange(caretLine,newCaretLine);
  1782. }
  1783. caret = newCaret;
  1784. caretLine = newCaretLine;
  1785. if(focusedComponent == this)
  1786. finishCaretUpdate(doElectricScroll,true);
  1787. } //}}}
  1788. //{{{ getCaretPosition() method
  1789. /**
  1790. * Returns the caret position.
  1791. */
  1792. public int getCaretPosition()
  1793. {
  1794. return caret;
  1795. } //}}}
  1796. //{{{ getCaretLine() method
  1797. /**
  1798. * Returns the line number containing the caret.
  1799. */
  1800. public int getCaretLine()
  1801. {
  1802. return caretLine;
  1803. } //}}}
  1804. //{{{ getMagicCaretPosition() method
  1805. /**
  1806. * @deprecated Do not call this method.
  1807. */
  1808. public final int getMagicCaretPosition()
  1809. {
  1810. if(magicCaret == -1)
  1811. {
  1812. magicCaret = offsetToX(caretLine,caret
  1813. - getLineStartOffset(caretLine));
  1814. }
  1815. return magicCaret;
  1816. } //}}}
  1817. //{{{ setMagicCaretPosition() method
  1818. /**
  1819. * Sets the `magic' caret position. This can be used to preserve
  1820. * the column position when moving up and down lines.
  1821. * @param magicCaret The magic caret position
  1822. */
  1823. public final void setMagicCaretPosition(int magicCaret)
  1824. {
  1825. this.magicCaret = magicCaret;
  1826. } //}}}
  1827. //{{{ addCaretListener() method
  1828. /**
  1829. * Adds a caret change listener to this text area.
  1830. * @param listener The listener
  1831. */
  1832. public final void addCaretListener(CaretListener listener)
  1833. {
  1834. listenerList.add(CaretListener.class,listener);
  1835. } //}}}
  1836. //{{{ removeCaretListener() method
  1837. /**
  1838. * Removes a caret change listener from this text area.
  1839. * @param listener The listener
  1840. */
  1841. public final void removeCaretListener(CaretListener listener)
  1842. {
  1843. listenerList.remove(CaretListener.class,listener);
  1844. } //}}}
  1845. //{{{ getBracketPosition() method
  1846. /**
  1847. * Returns the position of the highlighted bracket (the bracket
  1848. * matching the one before the caret)
  1849. */
  1850. public final int getBracketPosition()
  1851. {
  1852. return bracketPosition;
  1853. } //}}}
  1854. //{{{ getBracketLine() method
  1855. /**
  1856. * Returns the line of the highlighted bracket (the bracket
  1857. * matching the one before the caret)
  1858. */
  1859. public final int getBracketLine()
  1860. {
  1861. return bracketLine;
  1862. } //}}}
  1863. //{{{ goToNextBracket() method
  1864. /**
  1865. * Moves the caret to the next closing bracket.
  1866. * @since jEdit 2.7pre2.
  1867. */
  1868. public void goToNextBracket(boolean select)
  1869. {
  1870. String text = getText(caret,buffer.getLength() - caret - 1);
  1871. int newCaret = -1;
  1872. loop: for(int i = 0; i < text.length(); i++)
  1873. {
  1874. switch(text.charAt(i))
  1875. {
  1876. case ')': case ']': case '}':
  1877. newCaret = caret + i + 1;
  1878. break loop;
  1879. }
  1880. }
  1881. if(newCaret == -1)
  1882. getToolkit().beep();
  1883. else
  1884. {
  1885. if(select)
  1886. extendSelection(caret,newCaret);
  1887. else if(!multi)
  1888. selectNone();
  1889. moveCaretPosition(newCaret);
  1890. }
  1891. } //}}}
  1892. //{{{ goToNextCharacter() method
  1893. /**
  1894. * Moves the caret to the next character.
  1895. * @since jEdit 2.7pre2.
  1896. */
  1897. public void goToNextCharacter(boolean select)
  1898. {
  1899. if(!select && selection.size() != 0)
  1900. {
  1901. Selection s = getSelectionAtOffset(caret);
  1902. if(s != null)
  1903. {
  1904. if(multi)
  1905. {
  1906. if(caret != s.end)
  1907. {
  1908. moveCaretPosition(s.end);
  1909. return;
  1910. }
  1911. }
  1912. else