/jEdit/tags/jedit-4-3-pre4/org/gjt/sp/jedit/textarea/JEditTextArea.java

# · Java · 2815 lines · 1617 code · 328 blank · 870 comment · 328 complexity · f963d7401b2241340e9d818581c23045 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, 2005 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 java.awt.*;
  26. import java.awt.event.*;
  27. import java.util.ArrayList;
  28. import java.util.Iterator;
  29. import java.util.LinkedList;
  30. import java.util.List;
  31. import java.util.TooManyListenersException;
  32. import javax.swing.*;
  33. import javax.swing.event.*;
  34. import javax.swing.plaf.metal.MetalLookAndFeel;
  35. import javax.swing.text.Segment;
  36. import org.gjt.sp.jedit.Abbrevs;
  37. import org.gjt.sp.jedit.Buffer;
  38. import org.gjt.sp.jedit.Debug;
  39. import org.gjt.sp.jedit.GUIUtilities;
  40. import org.gjt.sp.jedit.Macros;
  41. import org.gjt.sp.jedit.MiscUtilities;
  42. import org.gjt.sp.jedit.TextUtilities;
  43. import org.gjt.sp.jedit.View;
  44. import org.gjt.sp.jedit.buffer.*;
  45. import org.gjt.sp.jedit.gui.InputHandler;
  46. import org.gjt.sp.jedit.syntax.*;
  47. import org.gjt.sp.util.Log;
  48. //}}}
  49. /**
  50. * jEdit's text component.<p>
  51. *
  52. * Unlike most other text editors, the selection API permits selection and
  53. * concurrent manipulation of multiple, non-contiguous regions of text.
  54. * Methods in this class that deal with selecting text rely upon classes derived
  55. * the {@link Selection} class.
  56. *
  57. * @author Slava Pestov
  58. * @author John Gellene (API documentation)
  59. * @version $Id: JEditTextArea.java 5377 2006-04-22 22:35:17Z ezust $
  60. */
  61. public class JEditTextArea extends JComponent
  62. {
  63. //{{{ JEditTextArea constructor
  64. /**
  65. * Creates a new JEditTextArea.
  66. */
  67. public JEditTextArea(View view)
  68. {
  69. enableEvents(AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
  70. this.view = view;
  71. //{{{ Initialize some misc. stuff
  72. selectionManager = new SelectionManager(this);
  73. chunkCache = new ChunkCache(this);
  74. painter = new TextAreaPainter(this);
  75. repaintMgr = new FastRepaintManager(this,painter);
  76. gutter = new Gutter(view,this);
  77. listenerList = new EventListenerList();
  78. caretEvent = new MutableCaretEvent();
  79. blink = true;
  80. lineSegment = new Segment();
  81. offsetXY = new Point();
  82. structureMatchers = new LinkedList();
  83. structureMatchers.add(new StructureMatcher.BracketMatcher());
  84. //}}}
  85. //{{{ Initialize the GUI
  86. setLayout(new ScrollLayout());
  87. add(ScrollLayout.CENTER,painter);
  88. add(ScrollLayout.LEFT,gutter);
  89. // some plugins add stuff in a "right-hand" gutter
  90. verticalBox = new Box(BoxLayout.X_AXIS);
  91. verticalBox.add(vertical = new JScrollBar(JScrollBar.VERTICAL));
  92. vertical.setRequestFocusEnabled(false);
  93. add(ScrollLayout.RIGHT,verticalBox);
  94. add(ScrollLayout.BOTTOM,
  95. horizontal = new JScrollBar(JScrollBar.HORIZONTAL));
  96. horizontal.setRequestFocusEnabled(false);
  97. horizontal.setValues(0,0,0,0);
  98. //}}}
  99. //{{{ this ensures that the text area's look is slightly
  100. // more consistent with the rest of the metal l&f.
  101. // while it depends on not-so-well-documented portions
  102. // of Swing, it only affects appearance, so future
  103. // breakage shouldn't matter
  104. if(UIManager.getLookAndFeel() instanceof MetalLookAndFeel)
  105. {
  106. setBorder(new TextAreaBorder());
  107. vertical.putClientProperty("JScrollBar.isFreeStanding",
  108. Boolean.FALSE);
  109. horizontal.putClientProperty("JScrollBar.isFreeStanding",
  110. Boolean.FALSE);
  111. //horizontal.setBorder(null);
  112. }
  113. //}}}
  114. //{{{ Add some event listeners
  115. vertical.addAdjustmentListener(new AdjustHandler());
  116. horizontal.addAdjustmentListener(new AdjustHandler());
  117. mouseHandler = new MouseHandler(this);
  118. painter.addMouseListener(mouseHandler);
  119. painter.addMouseMotionListener(mouseHandler);
  120. addFocusListener(new FocusHandler());
  121. addMouseWheelListener(new MouseWheelHandler());
  122. /* Drag and drop */
  123. setTransferHandler(new TextAreaTransferHandler());
  124. try
  125. {
  126. getDropTarget().addDropTargetListener(
  127. new TextAreaDropHandler(this));
  128. }
  129. catch(TooManyListenersException e)
  130. {
  131. Log.log(Log.ERROR,this,e);
  132. } //}}}
  133. // This doesn't seem very correct, but it fixes a problem
  134. // when setting the initial caret position for a buffer
  135. // (eg, from the recent file list)
  136. focusedComponent = this;
  137. popupEnabled = true;
  138. } //}}}
  139. public String toString() {
  140. ArrayList sl = new ArrayList();
  141. sl .add("caret: " + caret);
  142. sl.add("caretLine: " + caretLine );
  143. sl.add("caretScreenLine: " + caretScreenLine);
  144. sl.add("electricScroll: " + electricScroll);
  145. sl.add("horizontalOffset: " + horizontalOffset);
  146. sl.add("magicCaret: " + magicCaret);
  147. sl.add("offsetXY" + offsetXY.toString());
  148. sl.add("oldCaretLine: " + oldCaretLine);
  149. sl.add("screenLastLine: " + screenLastLine);
  150. sl.add("visibleLines: " + visibleLines);
  151. sl.add("firstPhysicalLine: " + getFirstPhysicalLine());
  152. sl.add("physLastLine: " + physLastLine);
  153. return TextUtilities.join(sl, "\n");
  154. }
  155. //{{{ dispose() method
  156. /**
  157. * Plugins and macros should not call this method.
  158. * @since jEdit 4.2pre1
  159. */
  160. public void dispose()
  161. {
  162. DisplayManager.textAreaDisposed(this);
  163. } //}}}
  164. //{{{ Getters and setters
  165. //{{{ getView() method
  166. /**
  167. * Returns this text area's view.
  168. * @since jEdit 4.2pre5
  169. */
  170. public View getView()
  171. {
  172. return view;
  173. } //}}}
  174. //{{{ getInputHandler() method
  175. /**
  176. * @since jEdit 4.3pre1
  177. */
  178. public InputHandler getInputHandler()
  179. {
  180. return view.getInputHandler();
  181. } //}}}
  182. //{{{ getPainter() method
  183. /**
  184. * Returns the object responsible for painting this text area.
  185. */
  186. public final TextAreaPainter getPainter()
  187. {
  188. return painter;
  189. } //}}}
  190. //{{{ getGutter() method
  191. /**
  192. * Returns the gutter to the left of the text area or null if the gutter
  193. * is disabled
  194. */
  195. public final Gutter getGutter()
  196. {
  197. return gutter;
  198. } //}}}
  199. //{{{ getDisplayManager() method
  200. /**
  201. * @return the display manager used by this text area.
  202. * @since jEdit 4.2pre1
  203. */
  204. public DisplayManager getDisplayManager()
  205. {
  206. return displayManager;
  207. } //}}}
  208. //{{{ isCaretBlinkEnabled() method
  209. /**
  210. * @return true if the caret is blinking, false otherwise.
  211. */
  212. public final boolean isCaretBlinkEnabled()
  213. {
  214. return caretBlinks;
  215. } //}}}
  216. //{{{ setCaretBlinkEnabled() method
  217. /**
  218. * Toggles caret blinking.
  219. * @param caretBlinks True if the caret should blink, false otherwise
  220. */
  221. public void setCaretBlinkEnabled(boolean caretBlinks)
  222. {
  223. this.caretBlinks = caretBlinks;
  224. if(!caretBlinks)
  225. blink = false;
  226. if(buffer != null)
  227. invalidateLine(caretLine);
  228. } //}}}
  229. //{{{ getElectricScroll() method
  230. /**
  231. * @return the minimum distance (in number of lines)
  232. * from the caret to the nearest edge of the screen
  233. * (top or bottom edge).
  234. */
  235. public final int getElectricScroll()
  236. {
  237. return electricScroll;
  238. } //}}}
  239. //{{{ setElectricScroll() method
  240. /**
  241. * Sets the number of lines from the top and bottom of the text
  242. * area that are always visible
  243. * @param electricScroll The number of lines always visible from
  244. * the top or bottom
  245. */
  246. public final void setElectricScroll(int electricScroll)
  247. {
  248. this.electricScroll = electricScroll;
  249. } //}}}
  250. //{{{ isQuickCopyEnabled() method
  251. /**
  252. * Returns if clicking the middle mouse button pastes the most
  253. * recent selection (% register), and if Control-dragging inserts
  254. * the selection at the caret.
  255. */
  256. public final boolean isQuickCopyEnabled()
  257. {
  258. return quickCopy;
  259. } //}}}
  260. //{{{ setQuickCopyEnabled() method
  261. /**
  262. * Sets if clicking the middle mouse button pastes the most
  263. * recent selection (% register), and if Control-dragging inserts
  264. * the selection at the caret.
  265. * @param quickCopy A boolean flag
  266. */
  267. public final void setQuickCopyEnabled(boolean quickCopy)
  268. {
  269. this.quickCopy = quickCopy;
  270. } //}}}
  271. //{{{ getBuffer() method
  272. /**
  273. * Returns the buffer this text area is editing.
  274. * @since jedit 4.3pre3
  275. *
  276. * Prior to 4.3pre3, this function returned a "Buffer" type.
  277. * If this causes your code to break, try calling view.getBuffer() instead of
  278. * view.getTextArea().getBuffer().
  279. *
  280. */
  281. public final JEditBuffer getBuffer()
  282. {
  283. return buffer;
  284. } //}}}
  285. //{{{ setBuffer() method
  286. /**
  287. * Sets the buffer this text area is editing. Do not call this method -
  288. * use {@link org.gjt.sp.jedit.EditPane#setBuffer(Buffer)} instead.
  289. * @param buffer The buffer
  290. */
  291. public void setBuffer(JEditBuffer buffer)
  292. {
  293. if(this.buffer == buffer)
  294. return;
  295. try
  296. {
  297. bufferChanging = true;
  298. if(this.buffer != null)
  299. {
  300. // dubious?
  301. //setFirstLine(0);
  302. if(!buffer.isLoading())
  303. selectNone();
  304. caretLine = caret = caretScreenLine = 0;
  305. match = null;
  306. }
  307. this.buffer = buffer;
  308. chunkCache.setBuffer(buffer);
  309. repaintMgr.setFastScroll(false);
  310. propertiesChanged();
  311. if(displayManager != null)
  312. {
  313. DisplayManager.releaseDisplayManager(
  314. displayManager);
  315. }
  316. displayManager = DisplayManager.getDisplayManager(
  317. buffer,this);
  318. displayManager.init();
  319. if(buffer.isLoading())
  320. updateScrollBar();
  321. repaint();
  322. fireScrollEvent(true);
  323. }
  324. finally
  325. {
  326. bufferChanging = false;
  327. }
  328. } //}}}
  329. //{{{ isEditable() method
  330. /**
  331. * Returns true if this text area is editable, false otherwise.
  332. */
  333. public final boolean isEditable()
  334. {
  335. return buffer.isEditable();
  336. } //}}}
  337. //{{{ isDragInProgress() method
  338. /**
  339. * Drag and drop of text in jEdit is implementing using jEdit 1.4 APIs,
  340. * however since jEdit must run with Java 1.3, this class only has the
  341. * necessary support to call a hook method via reflection. This method
  342. * is called by the {@link org.gjt.sp.jedit.Java14} class to signal that
  343. * a drag is in progress.
  344. * @since jEdit 4.2pre5
  345. */
  346. public boolean isDragInProgress()
  347. {
  348. return dndInProgress;
  349. } //}}}
  350. //{{{ setDragInProgress() method
  351. /**
  352. * Drag and drop of text in jEdit is implementing using jEdit 1.4 APIs,
  353. * however since jEdit must run with Java 1.3, this class only has the
  354. * necessary support to call a hook method via reflection. This method
  355. * is called by the {@link org.gjt.sp.jedit.Java14} class to signal that
  356. * a drag is in progress.
  357. * @since jEdit 4.2pre5
  358. */
  359. public void setDragInProgress(boolean dndInProgress)
  360. {
  361. this.dndInProgress = dndInProgress;
  362. } //}}}
  363. //{{{ isDragEnabled() method
  364. /**
  365. * Returns if drag and drop of text is enabled.
  366. * @since jEdit 4.2pre5
  367. */
  368. public boolean isDragEnabled()
  369. {
  370. return dndEnabled;
  371. } //}}}
  372. //{{{ setDragEnabled() method
  373. /**
  374. * Sets if drag and drop of text is enabled.
  375. * @since jEdit 4.2pre5
  376. */
  377. public void setDragEnabled(boolean dndEnabled)
  378. {
  379. this.dndEnabled = dndEnabled;
  380. } //}}}
  381. //{{{ getJoinNonWordChars() method
  382. /**
  383. * If set, double clicking will join non-word characters to form one "word".
  384. * @since jEdit 4.3pre2
  385. */
  386. public boolean getJoinNonWordChars()
  387. {
  388. return joinNonWordChars;
  389. } //}}}
  390. //{{{ setJoinNonWordChars() method
  391. /**
  392. * If set, double clicking will join non-word characters to form one "word".
  393. * @since jEdit 4.3pre2
  394. */
  395. public void setJoinNonWordChars(boolean joinNonWordChars)
  396. {
  397. this.joinNonWordChars = joinNonWordChars;
  398. } //}}}
  399. //}}}
  400. //{{{ Scrolling
  401. //{{{ getFirstLine() method
  402. /**
  403. * Returns the vertical scroll bar position.
  404. * @since jEdit 4.2pre1
  405. */
  406. public final int getFirstLine()
  407. {
  408. return displayManager.firstLine.scrollLine
  409. + displayManager.firstLine.skew;
  410. } //}}}
  411. //{{{ setFirstLine() method
  412. /**
  413. * Sets the vertical scroll bar position
  414. *
  415. * @param firstLine The scroll bar position
  416. */
  417. public void setFirstLine(int firstLine)
  418. {
  419. //{{{ ensure we don't have empty space at the bottom or top, etc
  420. int max = displayManager.getScrollLineCount() - visibleLines
  421. + (lastLinePartial ? 1 : 0);
  422. if(firstLine > max)
  423. firstLine = max;
  424. if(firstLine < 0)
  425. firstLine = 0;
  426. //}}}
  427. if(Debug.SCROLL_DEBUG)
  428. {
  429. Log.log(Log.DEBUG,this,"setFirstLine() from "
  430. + getFirstLine() + " to " + firstLine);
  431. }
  432. int oldFirstLine = getFirstLine();
  433. if(firstLine == oldFirstLine)
  434. return;
  435. displayManager.setFirstLine(oldFirstLine,firstLine);
  436. repaint();
  437. fireScrollEvent(true);
  438. } //}}}
  439. //{{{ getFirstPhysicalLine() method
  440. /**
  441. * Returns the first visible physical line index.
  442. * @since jEdit 4.0pre4
  443. */
  444. public final int getFirstPhysicalLine()
  445. {
  446. return displayManager.firstLine.physicalLine;
  447. } //}}}
  448. //{{{ setFirstPhysicalLine() method
  449. /**
  450. * Sets the vertical scroll bar position.
  451. * @param physFirstLine The first physical line to display
  452. * @since jEdit 4.2pre1
  453. */
  454. public void setFirstPhysicalLine(int physFirstLine)
  455. {
  456. setFirstPhysicalLine(physFirstLine,0);
  457. } //}}}
  458. //{{{ setFirstPhysicalLine() method
  459. /**
  460. * Sets the vertical scroll bar position.
  461. * @param physFirstLine The first physical line to display
  462. * @param skew A local screen line delta
  463. * @since jEdit 4.2pre1
  464. */
  465. public void setFirstPhysicalLine(int physFirstLine, int skew)
  466. {
  467. if(Debug.SCROLL_DEBUG)
  468. {
  469. Log.log(Log.DEBUG,this,"setFirstPhysicalLine("
  470. + physFirstLine + "," + skew + ")");
  471. }
  472. int amount = (physFirstLine - displayManager.firstLine.physicalLine);
  473. displayManager.setFirstPhysicalLine(amount,skew);
  474. repaint();
  475. fireScrollEvent(true);
  476. } //}}}
  477. //{{{ getLastPhysicalLine() method
  478. /**
  479. * Returns the last visible physical line index.
  480. * @since jEdit 4.0pre4
  481. */
  482. public final int getLastPhysicalLine()
  483. {
  484. return physLastLine;
  485. } //}}}
  486. //{{{ getLastScreenLine() method
  487. /**
  488. * @since jEdit 4.3pre1
  489. */
  490. public int getLastScreenLine()
  491. {
  492. return screenLastLine;
  493. } //}}}
  494. //{{{ getVisibleLines() method
  495. /**
  496. * Returns the number of lines visible in this text area.
  497. */
  498. public final int getVisibleLines()
  499. {
  500. return visibleLines;
  501. } //}}}
  502. //{{{ getHorizontalOffset() method
  503. /**
  504. * Returns the horizontal offset of drawn lines.
  505. */
  506. public final int getHorizontalOffset()
  507. {
  508. return horizontalOffset;
  509. } //}}}
  510. //{{{ setHorizontalOffset() method
  511. /**
  512. * Sets the horizontal offset of drawn lines. This can be used to
  513. * implement horizontal scrolling.
  514. * @param horizontalOffset offset The new horizontal offset
  515. */
  516. public void setHorizontalOffset(int horizontalOffset)
  517. {
  518. if(horizontalOffset > 0)
  519. horizontalOffset = 0;
  520. if(horizontalOffset == this.horizontalOffset)
  521. return;
  522. this.horizontalOffset = horizontalOffset;
  523. painter.repaint();
  524. fireScrollEvent(false);
  525. } //}}}
  526. //{{{ scrollUpLine() method
  527. /**
  528. * Scrolls up by one line.
  529. * @since jEdit 2.7pre2
  530. */
  531. public void scrollUpLine()
  532. {
  533. setFirstLine(getFirstLine() - 1);
  534. } //}}}
  535. //{{{ scrollUpPage() method
  536. /**
  537. * Scrolls up by one page.
  538. * @since jEdit 2.7pre2
  539. */
  540. public void scrollUpPage()
  541. {
  542. setFirstLine(getFirstLine() - getVisibleLines()
  543. + (lastLinePartial ? 1 : 0));
  544. } //}}}
  545. //{{{ scrollDownLine() method
  546. /**
  547. * Scrolls down by one line.
  548. * @since jEdit 2.7pre2
  549. */
  550. public void scrollDownLine()
  551. {
  552. setFirstLine(getFirstLine() + 1);
  553. } //}}}
  554. //{{{ scrollDownPage() method
  555. /**
  556. * Scrolls down by one page.
  557. * @since jEdit 2.7pre2
  558. */
  559. public void scrollDownPage()
  560. {
  561. setFirstLine(getFirstLine() + getVisibleLines()
  562. - (lastLinePartial ? 1 : 0));
  563. } //}}}
  564. //{{{ scrollToCaret() method
  565. /**
  566. * Ensures that the caret is visible by scrolling the text area if
  567. * necessary.
  568. * @param doElectricScroll If true, electric scrolling will be performed
  569. */
  570. public void scrollToCaret(boolean doElectricScroll)
  571. {
  572. scrollTo(caretLine,caret - buffer.getLineStartOffset(caretLine),
  573. doElectricScroll);
  574. } //}}}
  575. //{{{ scrollTo() method
  576. /**
  577. * Ensures that the specified location in the buffer is visible.
  578. * @param offset The offset from the start of the buffer
  579. * @param doElectricScroll If true, electric scrolling will be performed
  580. * @since jEdit 4.2pre3
  581. */
  582. public void scrollTo(int offset, boolean doElectricScroll)
  583. {
  584. int line = buffer.getLineOfOffset(offset);
  585. scrollTo(line,offset - buffer.getLineStartOffset(line),
  586. doElectricScroll);
  587. } //}}}
  588. //{{{ scrollTo() method
  589. /**
  590. * Ensures that the specified location in the buffer is visible.
  591. * @param line The line number
  592. * @param offset The offset from the start of the line
  593. * @param doElectricScroll If true, electric scrolling will be performed
  594. * @since jEdit 4.0pre6
  595. */
  596. public void scrollTo(int line, int offset, boolean doElectricScroll)
  597. {
  598. if(Debug.SCROLL_TO_DEBUG)
  599. Log.log(Log.DEBUG,this,"scrollTo(), lineCount="
  600. + getLineCount());
  601. //{{{ Get ready
  602. int extraEndVirt;
  603. int lineLength = buffer.getLineLength(line);
  604. if(offset > lineLength)
  605. {
  606. extraEndVirt = charWidth * (offset - lineLength);
  607. offset = lineLength;
  608. }
  609. else
  610. extraEndVirt = 0;
  611. int _electricScroll = (doElectricScroll
  612. && visibleLines - 1 > electricScroll * 2
  613. ? electricScroll : 0); //}}}
  614. if(visibleLines <= 1)
  615. {
  616. if(Debug.SCROLL_TO_DEBUG)
  617. Log.log(Log.DEBUG,this,"visibleLines <= 0");
  618. setFirstPhysicalLine(line,_electricScroll);
  619. return;
  620. }
  621. //{{{ Scroll vertically
  622. int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
  623. int visibleLines = getVisibleLines();
  624. if(screenLine == -1)
  625. {
  626. if(Debug.SCROLL_TO_DEBUG)
  627. Log.log(Log.DEBUG,this,"screenLine == -1");
  628. ChunkCache.LineInfo[] infos = chunkCache
  629. .getLineInfosForPhysicalLine(line);
  630. int subregion = chunkCache.getSubregionOfOffset(
  631. offset,infos);
  632. int prevLine = displayManager.getPrevVisibleLine(getFirstPhysicalLine());
  633. int nextLine = displayManager.getNextVisibleLine(getLastPhysicalLine());
  634. if(line == getFirstPhysicalLine())
  635. {
  636. if(Debug.SCROLL_TO_DEBUG)
  637. Log.log(Log.DEBUG,this,line + " == " + getFirstPhysicalLine());
  638. setFirstPhysicalLine(line,subregion
  639. - _electricScroll);
  640. }
  641. else if(line == prevLine)
  642. {
  643. if(Debug.SCROLL_TO_DEBUG)
  644. Log.log(Log.DEBUG,this,line + " == " + prevLine);
  645. setFirstPhysicalLine(prevLine,subregion
  646. - _electricScroll);
  647. }
  648. else if(line == getLastPhysicalLine())
  649. {
  650. if(Debug.SCROLL_TO_DEBUG)
  651. Log.log(Log.DEBUG,this,line + " == " + getLastPhysicalLine());
  652. setFirstPhysicalLine(line,
  653. subregion + _electricScroll
  654. - visibleLines
  655. + (lastLinePartial ? 2 : 1));
  656. }
  657. else if(line == nextLine)
  658. {
  659. if(Debug.SCROLL_TO_DEBUG)
  660. Log.log(Log.DEBUG,this,line + " == " + nextLine);
  661. setFirstPhysicalLine(nextLine,
  662. subregion + electricScroll
  663. - visibleLines
  664. + (lastLinePartial ? 2 : 1));
  665. }
  666. else
  667. {
  668. if(Debug.SCROLL_TO_DEBUG)
  669. {
  670. Log.log(Log.DEBUG,this,"neither");
  671. Log.log(Log.DEBUG,this,"Last physical line is " + getLastPhysicalLine());
  672. }
  673. setFirstPhysicalLine(line,subregion
  674. - visibleLines / 2);
  675. if(Debug.SCROLL_TO_DEBUG)
  676. {
  677. Log.log(Log.DEBUG,this,"Last physical line is " + getLastPhysicalLine());
  678. }
  679. }
  680. }
  681. else if(screenLine < _electricScroll)
  682. {
  683. if(Debug.SCROLL_TO_DEBUG)
  684. Log.log(Log.DEBUG,this,"electric up");
  685. setFirstLine(getFirstLine() - _electricScroll + screenLine);
  686. }
  687. else if(screenLine > visibleLines - _electricScroll
  688. - (lastLinePartial ? 2 : 1))
  689. {
  690. if(Debug.SCROLL_TO_DEBUG)
  691. Log.log(Log.DEBUG,this,"electric down");
  692. setFirstLine(getFirstLine() + _electricScroll - visibleLines + screenLine + (lastLinePartial ? 2 : 1));
  693. } //}}}
  694. //{{{ Scroll horizontally
  695. if(!displayManager.isLineVisible(line))
  696. return;
  697. Point point = offsetToXY(line,offset,offsetXY);
  698. if(point == null)
  699. // FIXME - we need to reset the state of this window so that it has the right
  700. // dimensions again.
  701. {
  702. Log.log(Log.ERROR,this,"BUG: screenLine=" + screenLine
  703. + ",visibleLines=" + visibleLines
  704. + ",physicalLine=" + line
  705. + ",offset=" + offset
  706. + ",firstPhysicalLine=" + getFirstPhysicalLine()
  707. + ",lastPhysicalLine=" + getLastPhysicalLine());
  708. }
  709. point.x += extraEndVirt;
  710. if(point.x < 0)
  711. {
  712. setHorizontalOffset(horizontalOffset
  713. - point.x + charWidth + 5);
  714. }
  715. else if(point.x >= painter.getWidth() - charWidth - 5)
  716. {
  717. setHorizontalOffset(horizontalOffset +
  718. (painter.getWidth() - point.x)
  719. - charWidth - 5);
  720. } //}}}
  721. } //}}}
  722. //{{{ addScrollListener() method
  723. /**
  724. * Adds a scroll listener to this text area.
  725. * @param listener The listener
  726. * @since jEdit 3.2pre2
  727. */
  728. public final void addScrollListener(ScrollListener listener)
  729. {
  730. listenerList.add(ScrollListener.class,listener);
  731. } //}}}
  732. //{{{ removeScrollListener() method
  733. /**
  734. * Removes a scroll listener from this text area.
  735. * @param listener The listener
  736. * @since jEdit 3.2pre2
  737. */
  738. public final void removeScrollListener(ScrollListener listener)
  739. {
  740. listenerList.remove(ScrollListener.class,listener);
  741. } //}}}
  742. //}}}
  743. //{{{ Screen line stuff
  744. //{{{ getPhysicalLineOfScreenLine() method
  745. /**
  746. * Returns the physical line number that contains the specified screen
  747. * line.
  748. * @param screenLine The screen line
  749. * @since jEdit 4.0pre6
  750. */
  751. public int getPhysicalLineOfScreenLine(int screenLine)
  752. {
  753. return chunkCache.getLineInfo(screenLine).physicalLine;
  754. } //}}}
  755. //{{{ getScreenLineOfOffset() method
  756. /**
  757. * Returns the screen (wrapped) line containing the specified offset.
  758. * Returns -1 if the line is not currently visible on the screen.
  759. * @param offset The offset
  760. * @since jEdit 4.0pre4
  761. */
  762. public int getScreenLineOfOffset(int offset)
  763. {
  764. int line = buffer.getLineOfOffset(offset);
  765. offset -= buffer.getLineStartOffset(line);
  766. return chunkCache.getScreenLineOfOffset(line,offset);
  767. } //}}}
  768. //{{{ getScreenLineStartOffset() method
  769. /**
  770. * Returns the start offset of the specified screen (wrapped) line.
  771. * @param line The line
  772. * @since jEdit 4.0pre4
  773. */
  774. public int getScreenLineStartOffset(int line)
  775. {
  776. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  777. if(lineInfo.physicalLine == -1)
  778. return -1;
  779. return buffer.getLineStartOffset(lineInfo.physicalLine)
  780. + lineInfo.offset;
  781. } //}}}
  782. //{{{ getScreenLineEndOffset() method
  783. /**
  784. * Returns the end offset of the specified screen (wrapped) line.
  785. * @param line The line
  786. * @since jEdit 4.0pre4
  787. */
  788. public int getScreenLineEndOffset(int line)
  789. {
  790. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  791. if(lineInfo.physicalLine == -1)
  792. return -1;
  793. return buffer.getLineStartOffset(lineInfo.physicalLine)
  794. + lineInfo.offset + lineInfo.length;
  795. } //}}}
  796. //}}}
  797. //{{{ Offset conversion
  798. //{{{ xyToOffset() method
  799. /**
  800. * Converts a point to an offset.
  801. * Note that unlike in previous jEdit versions, this method now returns
  802. * -1 if the y co-ordinate is out of bounds.
  803. *
  804. * @param x The x co-ordinate of the point
  805. * @param y The y co-ordinate of the point
  806. */
  807. public int xyToOffset(int x, int y)
  808. {
  809. return xyToOffset(x,y,true);
  810. } //}}}
  811. //{{{ xyToOffset() method
  812. /**
  813. * Converts a point to an offset.
  814. * Note that unlike in previous jEdit versions, this method now returns
  815. * -1 if the y co-ordinate is out of bounds.
  816. *
  817. * @param x The x co-ordinate of the point
  818. * @param y The y co-ordinate of the point
  819. * @param round Round up to next letter if past the middle of a letter?
  820. * @since jEdit 3.2pre6
  821. */
  822. public int xyToOffset(int x, int y, boolean round)
  823. {
  824. FontMetrics fm = painter.getFontMetrics();
  825. int height = fm.getHeight();
  826. int line = y / height;
  827. if(line < 0 || line >= visibleLines)
  828. return -1;
  829. return xToScreenLineOffset(line,x,round);
  830. } //}}}
  831. //{{{ xToScreenLineOffset() method
  832. /**
  833. * Converts a point in a given screen line to an offset.
  834. * Note that unlike in previous jEdit versions, this method now returns
  835. * -1 if the y co-ordinate is out of bounds.
  836. *
  837. * @param x The x co-ordinate of the point
  838. * @param screenLine The screen line
  839. * @param round Round up to next letter if past the middle of a letter?
  840. * @since jEdit 3.2pre6
  841. */
  842. public int xToScreenLineOffset(int screenLine, int x, boolean round)
  843. {
  844. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(screenLine);
  845. if(lineInfo.physicalLine == -1)
  846. {
  847. return getLineEndOffset(displayManager
  848. .getLastVisibleLine()) - 1;
  849. }
  850. else
  851. {
  852. int offset = Chunk.xToOffset(lineInfo.chunks,
  853. x - horizontalOffset,round);
  854. if(offset == -1 || offset == lineInfo.offset + lineInfo.length)
  855. offset = lineInfo.offset + lineInfo.length - 1;
  856. return getLineStartOffset(lineInfo.physicalLine) + offset;
  857. }
  858. } //}}}
  859. //{{{ offsetToXY() method
  860. /**
  861. * Converts an offset into a point in the text area painter's
  862. * co-ordinate space.
  863. * @param offset The offset
  864. * @return The location of the offset on screen, or <code>null</code>
  865. * if the specified offset is not visible
  866. */
  867. public Point offsetToXY(int offset)
  868. {
  869. int line = buffer.getLineOfOffset(offset);
  870. offset -= buffer.getLineStartOffset(line);
  871. Point retVal = new Point();
  872. return offsetToXY(line,offset,retVal);
  873. } //}}}
  874. //{{{ offsetToXY() method
  875. /**
  876. * Converts an offset into a point in the text area painter's
  877. * co-ordinate space.
  878. * @param line The line
  879. * @param offset The offset
  880. * @return The location of the offset on screen, or <code>null</code>
  881. * if the specified offset is not visible
  882. */
  883. public Point offsetToXY(int line, int offset)
  884. {
  885. return offsetToXY(line,offset,new Point());
  886. } //}}}
  887. //{{{ offsetToXY() method
  888. /**
  889. * Converts a line,offset pair into an x,y (pixel) point relative to the
  890. * upper left corner (0,0) of the text area.
  891. *
  892. * @param line The physical line number (from top of document)
  893. * @param offset The offset in characters, from the start of the line
  894. * @param retVal The point to store the return value in
  895. * @return <code>retVal</code> for convenience, or <code>null</code>
  896. * if the specified offset is not visible
  897. * @since jEdit 4.0pre4
  898. */
  899. public Point offsetToXY(int line, int offset, Point retVal)
  900. {
  901. if(!displayManager.isLineVisible(line))
  902. return null;
  903. int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
  904. if(screenLine == -1)
  905. return null;
  906. FontMetrics fm = painter.getFontMetrics();
  907. retVal.y = screenLine * fm.getHeight();
  908. ChunkCache.LineInfo info = chunkCache.getLineInfo(screenLine);
  909. retVal.x = (int)(horizontalOffset + Chunk.offsetToX(
  910. info.chunks,offset));
  911. return retVal;
  912. } //}}}
  913. //}}}
  914. //{{{ Painting
  915. //{{{ invalidateScreenLineRange() method
  916. /**
  917. * Marks a range of screen lines as needing a repaint.
  918. * @param start The first line
  919. * @param end The last line
  920. * @since jEdit 4.0pre4
  921. */
  922. public void invalidateScreenLineRange(int start, int end)
  923. {
  924. if(buffer.isLoading())
  925. return;
  926. if(start > end)
  927. {
  928. int tmp = end;
  929. end = start;
  930. start = tmp;
  931. }
  932. if(chunkCache.needFullRepaint())
  933. end = visibleLines;
  934. FontMetrics fm = painter.getFontMetrics();
  935. int y = start * fm.getHeight();
  936. int height = (end - start + 1) * fm.getHeight();
  937. painter.repaint(0,y,painter.getWidth(),height);
  938. gutter.repaint(0,y,gutter.getWidth(),height);
  939. } //}}}
  940. //{{{ invalidateLine() method
  941. /**
  942. * Marks a line as needing a repaint.
  943. * @param line The physical line to invalidate
  944. */
  945. public void invalidateLine(int line)
  946. {
  947. if(!isShowing()
  948. || buffer.isLoading()
  949. || line < getFirstPhysicalLine()
  950. || line > physLastLine
  951. || !displayManager.isLineVisible(line))
  952. return;
  953. int startLine = -1;
  954. int endLine = -1;
  955. for(int i = 0; i < visibleLines; i++)
  956. {
  957. ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
  958. if((info.physicalLine >= line || info.physicalLine == -1)
  959. && startLine == -1)
  960. {
  961. startLine = i;
  962. }
  963. if((info.physicalLine >= line && info.lastSubregion)
  964. || info.physicalLine == -1)
  965. {
  966. endLine = i;
  967. break;
  968. }
  969. }
  970. if(chunkCache.needFullRepaint() || endLine == -1)
  971. endLine = visibleLines;
  972. invalidateScreenLineRange(startLine,endLine);
  973. } //}}}
  974. //{{{ invalidateLineRange() method
  975. /**
  976. * Marks a range of physical lines as needing a repaint.
  977. * @param start The first line to invalidate
  978. * @param end The last line to invalidate
  979. */
  980. public void invalidateLineRange(int start, int end)
  981. {
  982. if(!isShowing() || buffer.isLoading())
  983. return;
  984. if(end < start)
  985. {
  986. int tmp = end;
  987. end = start;
  988. start = tmp;
  989. }
  990. if(end < getFirstPhysicalLine() || start > getLastPhysicalLine())
  991. return;
  992. int startScreenLine = -1;
  993. int endScreenLine = -1;
  994. for(int i = 0; i < visibleLines; i++)
  995. {
  996. ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
  997. if((info.physicalLine >= start || info.physicalLine == -1)
  998. && startScreenLine == -1)
  999. {
  1000. startScreenLine = i;
  1001. }
  1002. if((info.physicalLine >= end && info.lastSubregion)
  1003. || info.physicalLine == -1)
  1004. {
  1005. endScreenLine = i;
  1006. break;
  1007. }
  1008. }
  1009. if(startScreenLine == -1)
  1010. startScreenLine = 0;
  1011. if(chunkCache.needFullRepaint() || endScreenLine == -1)
  1012. endScreenLine = visibleLines;
  1013. invalidateScreenLineRange(startScreenLine,endScreenLine);
  1014. } //}}}
  1015. //}}}
  1016. //{{{ Convenience methods
  1017. //{{{ getBufferLength() method
  1018. /**
  1019. * Returns the length of the buffer.
  1020. */
  1021. public final int getBufferLength()
  1022. {
  1023. return buffer.getLength();
  1024. } //}}}
  1025. //{{{ getLineCount() method
  1026. /**
  1027. * Returns the number of physical lines in the buffer.
  1028. */
  1029. public final int getLineCount()
  1030. {
  1031. return buffer.getLineCount();
  1032. } //}}}
  1033. //{{{ getLineOfOffset() method
  1034. /**
  1035. * Returns the line containing the specified offset.
  1036. * @param offset The offset
  1037. */
  1038. public final int getLineOfOffset(int offset)
  1039. {
  1040. return buffer.getLineOfOffset(offset);
  1041. } //}}}
  1042. //{{{ getLineStartOffset() method
  1043. /**
  1044. * Returns the start offset of the specified line.
  1045. * @param line The line
  1046. * @return The start offset of the specified line, or -1 if the line is
  1047. * invalid
  1048. */
  1049. public int getLineStartOffset(int line)
  1050. {
  1051. return buffer.getLineStartOffset(line);
  1052. } //}}}
  1053. //{{{ getLineEndOffset() method
  1054. /**
  1055. * Returns the end offset of the specified line.
  1056. * @param line The line
  1057. * @return The end offset of the specified line, or -1 if the line is
  1058. * invalid.
  1059. */
  1060. public int getLineEndOffset(int line)
  1061. {
  1062. return buffer.getLineEndOffset(line);
  1063. } //}}}
  1064. //{{{ getLineLength() method
  1065. /**
  1066. * Returns the length of the specified line.
  1067. * @param line The line
  1068. */
  1069. public int getLineLength(int line)
  1070. {
  1071. return buffer.getLineLength(line);
  1072. } //}}}
  1073. //{{{ getText() method
  1074. /**
  1075. * Returns the specified substring of the buffer.
  1076. * @param start The start offset
  1077. * @param len The length of the substring
  1078. * @return The substring
  1079. */
  1080. public final String getText(int start, int len)
  1081. {
  1082. return buffer.getText(start,len);
  1083. } //}}}
  1084. //{{{ getText() method
  1085. /**
  1086. * Copies the specified substring of the buffer into a segment.
  1087. * @param start The start offset
  1088. * @param len The length of the substring
  1089. * @param segment The segment
  1090. */
  1091. public final void getText(int start, int len, Segment segment)
  1092. {
  1093. buffer.getText(start,len,segment);
  1094. } //}}}
  1095. //{{{ getLineText() method
  1096. /**
  1097. * Returns the text on the specified line.
  1098. * @param lineIndex The line
  1099. * @return The text, or null if the line is invalid
  1100. */
  1101. public final String getLineText(int lineIndex)
  1102. {
  1103. return buffer.getLineText(lineIndex);
  1104. } //}}}
  1105. //{{{ getLineText() method
  1106. /**
  1107. * Copies the text on the specified line into a segment. If the line
  1108. * is invalid, the segment will contain a null string.
  1109. * @param lineIndex The line
  1110. */
  1111. public final void getLineText(int lineIndex, Segment segment)
  1112. {
  1113. buffer.getLineText(lineIndex,segment);
  1114. } //}}}
  1115. //{{{ getText() method
  1116. /**
  1117. * Returns the entire text of this text area.
  1118. */
  1119. public String getText()
  1120. {
  1121. return buffer.getText(0,buffer.getLength());
  1122. } //}}}
  1123. //{{{ setText() method
  1124. /**
  1125. * Sets the entire text of this text area.
  1126. */
  1127. public void setText(String text)
  1128. {
  1129. try
  1130. {
  1131. buffer.beginCompoundEdit();
  1132. buffer.remove(0,buffer.getLength());
  1133. buffer.insert(0,text);
  1134. }
  1135. finally
  1136. {
  1137. buffer.endCompoundEdit();
  1138. }
  1139. } //}}}
  1140. //}}}
  1141. //{{{ Selection
  1142. //{{{ selectAll() method
  1143. /**
  1144. * Selects all text in the buffer. Preserves the scroll position.
  1145. */
  1146. public final void selectAll()
  1147. {
  1148. int firstLine = getFirstLine();
  1149. int horizOffset = getHorizontalOffset();
  1150. setSelection(new Selection.Range(0,buffer.getLength()));
  1151. moveCaretPosition(buffer.getLength(),true);
  1152. setFirstLine(firstLine);
  1153. setHorizontalOffset(horizOffset);
  1154. } //}}}
  1155. //{{{ selectLine() method
  1156. /**
  1157. * Selects the current line.
  1158. * @since jEdit 2.7pre2
  1159. */
  1160. public void selectLine()
  1161. {
  1162. int caretLine = getCaretLine();
  1163. int start = getLineStartOffset(caretLine);
  1164. int end = getLineEndOffset(caretLine) - 1;
  1165. Selection s = new Selection.Range(start,end);
  1166. if(multi)
  1167. addToSelection(s);
  1168. else
  1169. setSelection(s);
  1170. moveCaretPosition(end);
  1171. } //}}}
  1172. //{{{ selectParagraph() method
  1173. /**
  1174. * Selects the paragraph at the caret position.
  1175. * @since jEdit 2.7pre2
  1176. */
  1177. public void selectParagraph()
  1178. {
  1179. int caretLine = getCaretLine();
  1180. if(getLineLength(caretLine) == 0)
  1181. {
  1182. getToolkit().beep();
  1183. return;
  1184. }
  1185. int start = caretLine;
  1186. int end = caretLine;
  1187. while(start >= 0)
  1188. {
  1189. if(getLineLength(start) == 0)
  1190. break;
  1191. else
  1192. start--;
  1193. }
  1194. while(end < getLineCount())
  1195. {
  1196. if(getLineLength(end) == 0)
  1197. break;
  1198. else
  1199. end++;
  1200. }
  1201. int selectionStart = getLineStartOffset(start + 1);
  1202. int selectionEnd = getLineEndOffset(end - 1) - 1;
  1203. Selection s = new Selection.Range(selectionStart,selectionEnd);
  1204. if(multi)
  1205. addToSelection(s);
  1206. else
  1207. setSelection(s);
  1208. moveCaretPosition(selectionEnd);
  1209. } //}}}
  1210. //{{{ selectWord() method
  1211. /**
  1212. * Selects the word at the caret position.
  1213. * @since jEdit 2.7pre2
  1214. */
  1215. public void selectWord()
  1216. {
  1217. int line = getCaretLine();
  1218. int lineStart = getLineStartOffset(line);
  1219. int offset = getCaretPosition() - lineStart;
  1220. if(getLineLength(line) == 0)
  1221. return;
  1222. String lineText = getLineText(line);
  1223. String noWordSep = buffer.getStringProperty("noWordSep");
  1224. if(offset == getLineLength(line))
  1225. offset--;
  1226. int wordStart = TextUtilities.findWordStart(lineText,offset,noWordSep);
  1227. int wordEnd = TextUtilities.findWordEnd(lineText,offset+1,noWordSep);
  1228. Selection s = new Selection.Range(lineStart + wordStart,
  1229. lineStart + wordEnd);
  1230. if(multi)
  1231. addToSelection(s);
  1232. else
  1233. setSelection(s);
  1234. moveCaretPosition(lineStart + wordEnd);
  1235. } //}}}
  1236. //{{{ selectToMatchingBracket() method
  1237. /**
  1238. * Selects from the bracket at the specified position to the
  1239. * corresponding bracket.
  1240. * @since jEdit 4.2pre1
  1241. */
  1242. public Selection selectToMatchingBracket(int position,
  1243. boolean quickCopy)
  1244. {
  1245. int positionLine = buffer.getLineOfOffset(position);
  1246. int lineOffset = position - buffer.getLineStartOffset(positionLine);
  1247. if(getLineLength(positionLine) != 0)
  1248. {
  1249. int bracket = TextUtilities.findMatchingBracket(buffer,
  1250. positionLine,Math.max(0,lineOffset - 1));
  1251. if(bracket != -1)
  1252. {
  1253. Selection s;
  1254. if(bracket < position)
  1255. {
  1256. if(!quickCopy)
  1257. moveCaretPosition(position,false);
  1258. s = new Selection.Range(bracket,position);
  1259. }
  1260. else
  1261. {
  1262. if(!quickCopy)
  1263. moveCaretPosition(bracket + 1,false);
  1264. s = new Selection.Range(position - 1,bracket + 1);
  1265. }
  1266. if(!multi && !quickCopy)
  1267. selectNone();
  1268. addToSelection(s);
  1269. return s;
  1270. }
  1271. }
  1272. return null;
  1273. } //}}}
  1274. //{{{ selectToMatchingBracket() method
  1275. /**
  1276. * Selects from the bracket at the caret position to the corresponding
  1277. * bracket.
  1278. * @since jEdit 4.0pre2
  1279. */
  1280. public void selectToMatchingBracket()
  1281. {
  1282. selectToMatchingBracket(caret,false);
  1283. } //}}}
  1284. //{{{ selectBlock() method
  1285. /**
  1286. * Selects the code block surrounding the caret.
  1287. * @since jEdit 2.7pre2
  1288. */
  1289. public void selectBlock()
  1290. {
  1291. String openBrackets = "([{";
  1292. String closeBrackets = ")]}";
  1293. Selection s = getSelectionAtOffset(caret);
  1294. int start, end;
  1295. if(s == null)
  1296. start = end = caret;
  1297. else
  1298. {
  1299. start = s.start;
  1300. end = s.end;
  1301. }
  1302. String text = getText(0,buffer.getLength());
  1303. // Scan backwards, trying to find a bracket
  1304. int count = 1;
  1305. char openBracket = '\0';
  1306. char closeBracket = '\0';
  1307. // We can't do the backward scan if start == 0
  1308. if(start == 0)
  1309. {
  1310. getToolkit().beep();
  1311. return;
  1312. }
  1313. backward_scan: while(--start > 0)
  1314. {
  1315. char c = text.charAt(start);
  1316. int index = openBrackets.indexOf(c);
  1317. if(index != -1)
  1318. {
  1319. if(--count == 0)
  1320. {
  1321. openBracket = c;
  1322. closeBracket = closeBrackets.charAt(index);
  1323. break backward_scan;
  1324. }
  1325. }
  1326. else if(closeBrackets.indexOf(c) != -1)
  1327. count++;
  1328. }
  1329. // Reset count
  1330. count = 1;
  1331. // Scan forward, matching that bracket
  1332. if(openBracket == '\0')
  1333. {
  1334. getToolkit().beep();
  1335. return;
  1336. }
  1337. else
  1338. {
  1339. forward_scan: do
  1340. {
  1341. char c = text.charAt(end);
  1342. if(c == closeBracket)
  1343. {
  1344. if(--count == 0)
  1345. {
  1346. end++;
  1347. break forward_scan;
  1348. }
  1349. }
  1350. else if(c == openBracket)
  1351. count++;
  1352. }
  1353. while(++end < buffer.getLength());
  1354. }
  1355. s = new Selection.Range(start,end);
  1356. if(multi)
  1357. addToSelection(s);
  1358. else
  1359. setSelection(s);
  1360. moveCaretPosition(end);
  1361. } //}}}
  1362. //{{{ lineInStructureScope() method
  1363. /**
  1364. * Returns if the specified line is contained in the currently
  1365. * matched structure's scope.
  1366. * @since jEdit 4.2pre3
  1367. */
  1368. public boolean lineInStructureScope(int line)
  1369. {
  1370. if(match == null)
  1371. return false;
  1372. if(match.startLine < caretLine)
  1373. return (line >= match.startLine && line <= caretLine);
  1374. else
  1375. return (line <= match.endLine && line >= caretLine);
  1376. } //}}}
  1377. //{{{ invertSelection() method
  1378. /**
  1379. * Inverts the selection.
  1380. * @since jEdit 4.0pre1
  1381. */
  1382. public final void invertSelection()
  1383. {
  1384. selectionManager.invertSelection();
  1385. } //}}}
  1386. //{{{ getSelectionCount() method
  1387. /**
  1388. * Returns the number of selections. This can be used to test
  1389. * for the existence of selections.
  1390. * @since jEdit 3.2pre2
  1391. */
  1392. public int getSelectionCount()
  1393. {
  1394. return selectionManager.getSelectionCount();
  1395. } //}}}
  1396. //{{{ getSelection() method
  1397. /**
  1398. * Returns the current selection.
  1399. * @since jEdit 3.2pre1
  1400. */
  1401. public Selection[] getSelection()
  1402. {
  1403. return selectionManager.getSelection();
  1404. } //}}}
  1405. //{{{ getSelectionIterator() method
  1406. /**
  1407. * Returns the current selection.
  1408. * @since jEdit 4.3pre1
  1409. */
  1410. public Iterator getSelectionIterator()
  1411. {
  1412. return selectionManager.selection.iterator();
  1413. } //}}}
  1414. //{{{ getSelection() method
  1415. /**
  1416. * Returns the selection with the specified index. This must be
  1417. * between 0 and the return value of <code>getSelectionCount()</code>.
  1418. * @since jEdit 4.3pre1
  1419. */
  1420. public Selection getSelection(int index)
  1421. {
  1422. return (Selection)selectionManager.selection.get(index);
  1423. } //}}}
  1424. //{{{ selectNone() method
  1425. /**
  1426. * Deselects everything.
  1427. */
  1428. public void selectNone()
  1429. {
  1430. invalidateSelectedLines();
  1431. setSelection((Selection)null);
  1432. } //}}}
  1433. //{{{ setSelection() method
  1434. /**
  1435. * Sets the selection. Nested and overlapping selections are merged
  1436. * where possible. Null elements of the array are ignored.
  1437. * @param selection The new selection
  1438. * since jEdit 3.2pre1
  1439. */
  1440. public void setSelection(Selection[] selection)
  1441. {
  1442. // invalidate the old selection
  1443. invalidateSelectedLines();
  1444. selectionManager.setSelection(selection);
  1445. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1446. } //}}}
  1447. //{{{ setSelection() method
  1448. /**
  1449. * Sets the selection. Nested and overlapping selections are merged
  1450. * where possible.
  1451. * @param selection The new selection
  1452. * since jEdit 3.2pre1
  1453. */
  1454. public void setSelection(Selection selection)
  1455. {
  1456. invalidateSelectedLines();
  1457. selectionManager.setSelection(selection);
  1458. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1459. } //}}}
  1460. //{{{ addToSelection() method
  1461. /**
  1462. * Adds to the selection. Nested and overlapping selections are merged
  1463. * where possible.
  1464. * @param selection The new selection
  1465. * since jEdit 3.2pre1
  1466. */
  1467. public void addToSelection(Selection[] selection)
  1468. {
  1469. invalidateSelectedLines();
  1470. selectionManager.addToSelection(selection);
  1471. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1472. } //}}}
  1473. //{{{ addToSelection() method
  1474. /**
  1475. * Adds to the selection. Nested and overlapping selections are merged
  1476. * where possible.
  1477. * @param selection The new selection
  1478. * since jEdit 3.2pre1
  1479. */
  1480. public void addToSelection(Selection selection)
  1481. {
  1482. invalidateSelectedLines();
  1483. selectionManager.addToSelection(selection);
  1484. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1485. } //}}}
  1486. //{{{ getSelectionAtOffset() method
  1487. /**
  1488. * Returns the selection containing the specific offset, or <code>null</code>
  1489. * if there is no selection at that offset.
  1490. * @param offset The offset
  1491. * @since jEdit 3.2pre1
  1492. */
  1493. public Selection getSelectionAtOffset(int offset)
  1494. {
  1495. return selectionManager.getSelectionAtOffset(offset);
  1496. } //}}}
  1497. //{{{ removeFromSelection() method
  1498. /**
  1499. * Deactivates the specified selection.
  1500. * @param sel The selection
  1501. * @since jEdit 3.2pre1
  1502. */
  1503. public void removeFromSelection(Selection sel)
  1504. {
  1505. invalidateSelectedLines();
  1506. selectionManager.removeFromSelection(sel);
  1507. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1508. } //}}}
  1509. //{{{ removeFromSelection() method
  1510. /**
  1511. * Deactivates the selection at the specified offset. If there is
  1512. * no selection at that offset, does nothing.
  1513. * @param offset The offset
  1514. * @since jEdit 3.2pre1
  1515. */
  1516. public void removeFromSelection(int offset)
  1517. {
  1518. Selection sel = getSelectionAtOffset(offset);
  1519. if(sel == null)
  1520. return;
  1521. invalidateSelectedLines();
  1522. selectionManager.removeFromSelection(sel);
  1523. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1524. } //}}}
  1525. //{{{ resizeSelection() method
  1526. /**
  1527. * Resizes the selection at the specified offset, or creates a new
  1528. * one if there is no selection at the specified offset. This is a
  1529. * utility method that is mainly useful in the mouse event handler
  1530. * because it handles the case of end being before offset gracefully
  1531. * (unlike the rest of the selection API).
  1532. * @param offset The offset
  1533. * @param end The new selection end
  1534. * @param extraEndVirt Only for rectangular selections - specifies how
  1535. * far it extends into virtual space.
  1536. * @param rect Make the selection rectangular?
  1537. * @since jEdit 3.2pre1
  1538. */
  1539. public void resizeSelection(int offset, int end, int extraEndVirt,
  1540. boolean rect)
  1541. {
  1542. Selection s = selectionManager.getSelectionAtOffset(offset);
  1543. if(s != null)
  1544. {
  1545. invalidateLineRange(s.startLine,s.endLine);
  1546. selectionManager.removeFromSelection(s);
  1547. }
  1548. selectionManager.resizeSelection(offset,end,extraEndVirt,rect);
  1549. fireCaretEvent();
  1550. } //}}}
  1551. //{{{ extendSelection() method
  1552. /**
  1553. * Extends the selection at the specified offset, or creates a new
  1554. * one if there is no selection at the specified offset. This is
  1555. * different from resizing in that the new chunk is added to the
  1556. * selection in question, instead of replacing it.
  1557. * @param offset The offset
  1558. * @param end The new selection end
  1559. * @since jEdit 3.2pre1
  1560. */
  1561. public void extendSelection(int offset, int end)
  1562. {
  1563. extendSelection(offset,end,0,0);
  1564. } //}}}
  1565. //{{{ extendSelection() method
  1566. /**
  1567. * Extends the selection at the specified offset, or creates a new
  1568. * one if there is no selection at the specified offset. This is
  1569. * different from resizing in that the new chunk is added to the
  1570. * selection in question, instead of replacing it.
  1571. * @param offset The offset
  1572. * @param end The new selection end
  1573. * @param extraStartVirt Extra virtual space at the start
  1574. * @param extraEndVirt Extra virtual space at the end
  1575. * @since jEdit 4.2pre1
  1576. */
  1577. public void extendSelection(int offset, int end,
  1578. int extraStartVirt, int extraEndVirt)
  1579. {
  1580. Selection s = getSelectionAtOffset(offset);
  1581. if(s != null)
  1582. {
  1583. invalidateLineRange(s.startLine,s.endLine);
  1584. selectionManager.removeFromSelection(s);
  1585. if(offset == s.start)
  1586. {
  1587. offset = end;
  1588. end = s.end;
  1589. }
  1590. else if(offset == s.end)
  1591. {
  1592. offset = s.start;
  1593. }
  1594. }
  1595. if(end < offset)
  1596. {
  1597. int tmp = end;
  1598. end = offset;
  1599. offset = tmp;
  1600. }
  1601. if(rectangularSelectionMode)
  1602. {
  1603. s = new Selection.Rect(offset,end);
  1604. ((Selection.Rect)s).extraStartVirt = extraStartVirt;
  1605. ((Selection.Rect)s).extraEndVirt = extraEndVirt;
  1606. }
  1607. else
  1608. s = new Selection.Range(offset,end);
  1609. selectionManager.addToSelection(s);
  1610. fireCaretEvent();
  1611. if(rectangularSelectionMode && extraEndVirt != 0)
  1612. {
  1613. int line = getLineOfOffset(end);
  1614. scrollTo(line,getLineLength(line) + extraEndVirt,false);
  1615. }
  1616. } //}}}
  1617. //{{{ getSelectedText() method
  1618. /**
  1619. * Returns the text in the specified selection.
  1620. * @param s The selection
  1621. * @since jEdit 3.2pre1
  1622. */
  1623. public String getSelectedText(Selection s)
  1624. {
  1625. StringBuffer buf = new StringBuffer();
  1626. s.getText(buffer,buf);
  1627. return buf.toString();
  1628. } //}}}
  1629. //{{{ getSelectedText() method
  1630. /**
  1631. * Returns the text in all active selections.
  1632. * @param separator The string to insert between each text chunk
  1633. * (for example, a newline)
  1634. * @since jEdit 3.2pre1
  1635. */
  1636. public String getSelectedText(String separator)
  1637. {
  1638. Selection[] sel = selectionManager.getSelection();
  1639. if(sel.length == 0)
  1640. return null;
  1641. StringBuffer buf = new StringBuffer();
  1642. for(int i = 0; i < sel.length; i++)
  1643. {
  1644. if(i != 0)
  1645. buf.append(separator);
  1646. sel[i].getText(buffer,buf);
  1647. }
  1648. return buf.toString();
  1649. } //}}}
  1650. //{{{ getSelectedText() method
  1651. /**
  1652. * Returns the text in all active selections, with a newline
  1653. * between each text chunk.
  1654. */
  1655. public String getSelectedText()
  1656. {
  1657. return getSelectedText("\n");
  1658. } //}}}
  1659. //{{{ setSelectedText() method
  1660. /**
  1661. * Replaces the selection with the specified text.
  1662. * @param s The selection
  1663. * @param selectedText The new text
  1664. * @since jEdit 3.2pre1
  1665. */
  1666. public void setSelectedText(Selection s, String selectedText)
  1667. {
  1668. if(!isEditable())
  1669. {
  1670. throw new InternalError("Text component"
  1671. + " read only");
  1672. }
  1673. try
  1674. {
  1675. buffer.beginCompoundEdit();
  1676. moveCaretPosition(s.setText(buffer,selectedText));
  1677. }
  1678. // No matter what happends... stops us from leaving buffer
  1679. // in a bad state
  1680. finally
  1681. {
  1682. buffer.endCompoundEdit();
  1683. }
  1684. // no no no!!!!
  1685. //selectNone();
  1686. } //}}}
  1687. //{{{ setSelectedText() method
  1688. /**
  1689. * Replaces the selection at the caret with the specified text.
  1690. * If there is no selection at the caret, the text is inserted at
  1691. * the caret position.
  1692. */
  1693. public void setSelectedText(String selectedText)
  1694. {
  1695. int newCaret = replaceSelection(selectedText);
  1696. if(newCaret != -1)
  1697. moveCaretPosition(newCaret);
  1698. selectNone();
  1699. } //}}}
  1700. //{{{ setSelectedText() method
  1701. /**
  1702. * Replaces the selection at the caret with the specified text.
  1703. * If there is no selection at the caret, the text is inserted at
  1704. * the caret position.
  1705. * @param selectedText The new selection
  1706. * @param moveCaret Move caret to insertion location if necessary
  1707. * @since jEdit 4.2pre5
  1708. */
  1709. public void setSelectedText(String selectedText, boolean moveCaret)
  1710. {
  1711. int newCaret = replaceSelection(selectedText);
  1712. if(moveCaret && newCaret != -1)
  1713. moveCaretPosition(newCaret);
  1714. selectNone();
  1715. } //}}}
  1716. //{{{ replaceSelection() method
  1717. /**
  1718. * Set the selection, but does not deactivate it, and does not move the
  1719. * caret.
  1720. *
  1721. * Please use {@link #setSelectedText(String)} instead.
  1722. *
  1723. * @param selectedText The new selection
  1724. * @return The new caret position
  1725. * @since 4.3pre1
  1726. */
  1727. public int replaceSelection(String selectedText)
  1728. {
  1729. if(!isEditable())
  1730. throw new RuntimeException("Text component read only");
  1731. int newCaret = -1;
  1732. if(getSelectionCount() == 0)
  1733. {
  1734. // for compatibility with older jEdit versions
  1735. buffer.insert(caret,selectedText);
  1736. }
  1737. else
  1738. {
  1739. try
  1740. {
  1741. buffer.beginCompoundEdit();
  1742. Selection[] selection = getSelection();
  1743. for(int i = 0; i < selection.length; i++)
  1744. newCaret = selection[i].setText(buffer,selectedText);
  1745. }
  1746. finally
  1747. {
  1748. buffer.endCompoundEdit();
  1749. }
  1750. }
  1751. return newCaret;
  1752. } //}}}
  1753. //{{{ getSelectedLines() method
  1754. /**
  1755. * Returns a sorted array of line numbers on which a selection or
  1756. * selections are present.<p>
  1757. *
  1758. * This method is the most convenient way to iterate through selected
  1759. * lines in a buffer. The line numbers in the array returned by this
  1760. * method can be passed as a parameter to such methods as
  1761. * {@link org.gjt.sp.jedit.Buffer#getLineText(int)}.
  1762. *
  1763. * @since jEdit 3.2pre1
  1764. */
  1765. public int[] getSelectedLines()
  1766. {
  1767. if(selectionManager.getSelectionCount() == 0)
  1768. return new int[] { caretLine };
  1769. return selectionManager.getSelectedLines();
  1770. } //}}}
  1771. //}}}
  1772. //{{{ Caret
  1773. //{{{ caretAutoScroll() method
  1774. /**
  1775. * Return if change in buffer should scroll this text area.
  1776. * @since jEdit 4.3pre2
  1777. */
  1778. public boolean caretAutoScroll()
  1779. {
  1780. return (focusedComponent == this);
  1781. } //}}}
  1782. //{{{ addStructureMatcher() method
  1783. /**
  1784. * Adds a structure matcher.
  1785. * @since jEdit 4.2pre3
  1786. */
  1787. public void addStructureMatcher(StructureMatcher matcher)
  1788. {
  1789. structureMatchers.add(matcher);
  1790. } //}}}
  1791. //{{{ removeStructureMatcher() method
  1792. /**
  1793. * Removes a structure matcher.
  1794. * @since jEdit 4.2pre3
  1795. */
  1796. public void removeStructureMatcher(StructureMatcher matcher)
  1797. {
  1798. structureMatchers.remove(matcher);
  1799. } //}}}
  1800. //{{{ getStructureMatchStart() method
  1801. /**
  1802. * Returns the structure element (bracket, or XML tag, etc) matching the
  1803. * one before the caret.
  1804. * @since jEdit 4.2pre3
  1805. */
  1806. public StructureMatcher.Match getStructureMatch()
  1807. {
  1808. return match;
  1809. } //}}}
  1810. //{{{ blinkCaret() method
  1811. /**
  1812. * Blinks the caret.
  1813. */
  1814. public final void blinkCaret()
  1815. {
  1816. if(caretBlinks)
  1817. {
  1818. blink = !blink;
  1819. inval