PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/TextArea.java

#
Java | 2675 lines | 1504 code | 301 blank | 870 comment | 279 complexity | 7caf13a8119e32dd613f4c6843c78385 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * TextArea.java - Abstract jEdit Text Area 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. * Portions copyright (C) 2006 Matthieu Casanova
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, write to the Free Software
  22. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. */
  24. package org.gjt.sp.jedit.textarea;
  25. //{{{ Imports
  26. import java.util.EventObject;
  27. import java.util.Iterator;
  28. import java.util.LinkedList;
  29. import java.util.TooManyListenersException;
  30. import javax.swing.*;
  31. import javax.swing.event.*;
  32. import java.awt.event.*;
  33. import java.awt.*;
  34. import java.awt.im.InputMethodRequests;
  35. import javax.swing.plaf.metal.MetalLookAndFeel;
  36. import javax.swing.text.Segment;
  37. import javax.swing.text.TabExpander;
  38. import org.gjt.sp.jedit.Debug;
  39. import org.gjt.sp.jedit.IPropertyManager;
  40. import org.gjt.sp.jedit.JEditActionContext;
  41. import org.gjt.sp.jedit.JEditActionSet;
  42. import org.gjt.sp.jedit.JEditBeanShellAction;
  43. import org.gjt.sp.jedit.TextUtilities;
  44. import org.gjt.sp.jedit.buffer.JEditBuffer;
  45. import org.gjt.sp.jedit.input.AbstractInputHandler;
  46. import org.gjt.sp.jedit.input.DefaultInputHandlerProvider;
  47. import org.gjt.sp.jedit.input.InputHandlerProvider;
  48. import org.gjt.sp.jedit.input.TextAreaInputHandler;
  49. import org.gjt.sp.jedit.syntax.Chunk;
  50. import org.gjt.sp.jedit.syntax.DefaultTokenHandler;
  51. import org.gjt.sp.jedit.syntax.Token;
  52. import org.gjt.sp.util.Log;
  53. import org.gjt.sp.util.StandardUtilities;
  54. import org.gjt.sp.util.ThreadUtilities;
  55. //}}}
  56. /** Abstract TextArea component.
  57. *
  58. * The concrete instance used by jEdit itself is called the JEditTextArea.
  59. *
  60. * This class uses a minimal set of jEdit APIs because it is the base class of the
  61. * JEditEmbeddedTextArea and StandaloneTextArea, so it needs to be embeddable and separable.
  62. *
  63. * @author Slava Pestov
  64. * @author kpouer (rafactoring into standalone text area)
  65. * @version $Id: TextArea.java 20017 2011-09-24 13:09:51Z evanpw $
  66. */
  67. public abstract class TextArea extends JComponent
  68. {
  69. //{{{ TextArea constructor
  70. /**
  71. * Creates a new JEditTextArea.
  72. * @param propertyManager the property manager that contains informations like shortcut bindings
  73. * @param inputHandlerProvider the inputHandlerProvider
  74. */
  75. protected TextArea(IPropertyManager propertyManager, InputHandlerProvider inputHandlerProvider)
  76. {
  77. this.inputHandlerProvider = inputHandlerProvider;
  78. enableEvents(AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
  79. //{{{ Initialize some misc. stuff
  80. selectionManager = new SelectionManager(this);
  81. chunkCache = new ChunkCache(this);
  82. painter = new TextAreaPainter(this);
  83. gutter = new Gutter(this);
  84. gutter.setMouseActionsProvider(new MouseActions(propertyManager, "gutter"));
  85. listenerList = new EventListenerList();
  86. caretEvent = new MutableCaretEvent();
  87. blink = true;
  88. offsetXY = new Point();
  89. structureMatchers = new LinkedList<StructureMatcher>();
  90. structureMatchers.add(new StructureMatcher.BracketMatcher());
  91. //}}}
  92. //{{{ Initialize the GUI
  93. setLayout(new ScrollLayout());
  94. add(ScrollLayout.CENTER,painter);
  95. add(ScrollLayout.LEFT,gutter);
  96. // some plugins add stuff in a "right-hand" gutter
  97. verticalBox = new Box(BoxLayout.X_AXIS);
  98. verticalBox.add(vertical = new JScrollBar(Adjustable.VERTICAL));
  99. vertical.setRequestFocusEnabled(false);
  100. add(ScrollLayout.RIGHT,verticalBox);
  101. add(ScrollLayout.BOTTOM,
  102. horizontal = new JScrollBar(Adjustable.HORIZONTAL));
  103. horizontal.setRequestFocusEnabled(false);
  104. horizontal.setValues(0,0,0,0);
  105. //}}}
  106. //{{{ this ensures that the text area's look is slightly
  107. // more consistent with the rest of the metal l&f.
  108. // while it depends on not-so-well-documented portions
  109. // of Swing, it only affects appearance, so future
  110. // breakage shouldn't matter
  111. if(UIManager.getLookAndFeel() instanceof MetalLookAndFeel)
  112. {
  113. setBorder(new TextAreaBorder());
  114. vertical.putClientProperty("JScrollBar.isFreeStanding",
  115. Boolean.FALSE);
  116. horizontal.putClientProperty("JScrollBar.isFreeStanding",
  117. Boolean.FALSE);
  118. //horizontal.setBorder(null);
  119. }
  120. //}}}
  121. //{{{ Add some event listeners
  122. vertical.addAdjustmentListener(new AdjustHandler());
  123. horizontal.addAdjustmentListener(new AdjustHandler());
  124. addFocusListener(new FocusHandler());
  125. addMouseWheelListener(new MouseWheelHandler());
  126. //}}}
  127. // This doesn't seem very correct, but it fixes a problem
  128. // when setting the initial caret position for a buffer
  129. // (eg, from the recent file list)
  130. focusedComponent = this;
  131. popupEnabled = true;
  132. } //}}}
  133. //{{{ getFoldPainter() method
  134. public FoldPainter getFoldPainter()
  135. {
  136. return new TriangleFoldPainter();
  137. } //}}}
  138. //{{{ initInputHandler() method
  139. /**
  140. * Creates an actionContext and initializes the input
  141. * handler for this textarea. Called when creating
  142. * a standalone textarea from within jEdit.
  143. */
  144. public void initInputHandler()
  145. {
  146. actionContext = new JEditActionContext<JEditBeanShellAction, JEditActionSet<JEditBeanShellAction>>()
  147. {
  148. @Override
  149. public void invokeAction(EventObject evt, JEditBeanShellAction action)
  150. {
  151. action.invoke(TextArea.this);
  152. }
  153. };
  154. setMouseHandler(new TextAreaMouseHandler(this));
  155. inputHandlerProvider = new DefaultInputHandlerProvider(new TextAreaInputHandler(this)
  156. {
  157. @Override
  158. protected JEditBeanShellAction getAction(String action)
  159. {
  160. return actionContext.getAction(action);
  161. }
  162. });
  163. } //}}}
  164. //{{{ getActionContext() method
  165. public JEditActionContext<JEditBeanShellAction,JEditActionSet<JEditBeanShellAction>> getActionContext()
  166. {
  167. return actionContext;
  168. } //}}}
  169. //{{{ setMouseHandler() method
  170. public void setMouseHandler(MouseInputAdapter mouseInputAdapter)
  171. {
  172. mouseHandler = mouseInputAdapter;
  173. painter.addMouseListener(mouseHandler);
  174. painter.addMouseMotionListener(mouseHandler);
  175. } //}}}
  176. //{{{ setTransferHandler() method
  177. @Override
  178. public void setTransferHandler(TransferHandler newHandler)
  179. {
  180. super.setTransferHandler(newHandler);
  181. try
  182. {
  183. getDropTarget().addDropTargetListener(
  184. new TextAreaDropHandler(this));
  185. }
  186. catch(TooManyListenersException e)
  187. {
  188. Log.log(Log.ERROR,this,e);
  189. }
  190. } //}}}
  191. //{{{ toString() method
  192. @Override
  193. public String toString()
  194. {
  195. StringBuilder builder = new StringBuilder();
  196. String baseVersion = super.toString();
  197. int len = baseVersion.length() - 1;
  198. builder.append(baseVersion);
  199. builder.setLength(len); // chop off the last ]
  200. builder.append(",caret=").append(caret);
  201. builder.append(",caretLine=").append(caretLine);
  202. builder.append(",caretScreenLine=").append(caretScreenLine);
  203. builder.append(",electricScroll=").append(electricScroll);
  204. builder.append(",horizontalOffset=").append(horizontalOffset);
  205. builder.append(",magicCaret=").append(magicCaret);
  206. builder.append(",offsetXY=").append(offsetXY.toString());
  207. builder.append(",oldCaretLine=").append(oldCaretLine);
  208. builder.append(",screenLastLine=").append(screenLastLine);
  209. builder.append(",visibleLines=").append(visibleLines);
  210. builder.append(",firstPhysicalLine=").append(getFirstPhysicalLine());
  211. builder.append(",physLastLine=").append(physLastLine).append("]");
  212. return builder.toString();
  213. } //}}}
  214. //{{{ dispose() method
  215. /**
  216. * Plugins and macros should not call this method.
  217. * @since jEdit 4.2pre1
  218. */
  219. public void dispose()
  220. {
  221. DisplayManager.textAreaDisposed(this);
  222. gutter.dispose();
  223. } //}}}
  224. //{{{ getInputHandler() method
  225. /**
  226. * @since jEdit 4.3pre1
  227. */
  228. public AbstractInputHandler getInputHandler()
  229. {
  230. return inputHandlerProvider.getInputHandler();
  231. } //}}}
  232. //{{{ getPainter() method
  233. /**
  234. * Returns the object responsible for painting this text area.
  235. */
  236. public final TextAreaPainter getPainter()
  237. {
  238. return painter;
  239. } //}}}
  240. //{{{ getGutter() method
  241. /**
  242. * Returns the gutter to the left of the text area or null if the gutter
  243. * is disabled
  244. */
  245. public final Gutter getGutter()
  246. {
  247. return gutter;
  248. } //}}}
  249. //{{{ getDisplayManager() method
  250. /**
  251. * @return the display manager used by this text area.
  252. * @since jEdit 4.2pre1
  253. */
  254. public DisplayManager getDisplayManager()
  255. {
  256. return displayManager;
  257. } //}}}
  258. //{{{ isCaretBlinkEnabled() method
  259. /**
  260. * @return true if the caret is blinking, false otherwise.
  261. */
  262. public final boolean isCaretBlinkEnabled()
  263. {
  264. return caretBlinks;
  265. } //}}}
  266. //{{{ setCaretBlinkEnabled() method
  267. /**
  268. * Toggles caret blinking.
  269. * @param caretBlinks True if the caret should blink, false otherwise
  270. */
  271. public void setCaretBlinkEnabled(boolean caretBlinks)
  272. {
  273. this.caretBlinks = caretBlinks;
  274. if(!caretBlinks)
  275. blink = false;
  276. if(buffer != null)
  277. invalidateLine(caretLine);
  278. } //}}}
  279. //{{{ getElectricScroll() method
  280. /**
  281. * @return the minimum distance (in number of lines)
  282. * from the caret to the nearest edge of the screen
  283. * (top or bottom edge).
  284. */
  285. public final int getElectricScroll()
  286. {
  287. return electricScroll;
  288. } //}}}
  289. //{{{ setElectricScroll() method
  290. /**
  291. * Sets the number of lines from the top and bottom of the text
  292. * area that are always visible
  293. * @param electricScroll The number of lines always visible from
  294. * the top or bottom
  295. */
  296. public final void setElectricScroll(int electricScroll)
  297. {
  298. this.electricScroll = electricScroll;
  299. } //}}}
  300. //{{{ isQuickCopyEnabled() method
  301. /**
  302. * Returns if clicking the middle mouse button pastes the most
  303. * recent selection (% register), and if Control-dragging inserts
  304. * the selection at the caret.
  305. */
  306. public final boolean isQuickCopyEnabled()
  307. {
  308. return quickCopy;
  309. } //}}}
  310. //{{{ setQuickCopyEnabled() method
  311. /**
  312. * Sets if clicking the middle mouse button pastes the most
  313. * recent selection (% register), and if Control-dragging inserts
  314. * the selection at the caret.
  315. * @param quickCopy A boolean flag
  316. */
  317. public final void setQuickCopyEnabled(boolean quickCopy)
  318. {
  319. this.quickCopy = quickCopy;
  320. } //}}}
  321. //{{{ getBuffer() method
  322. /**
  323. * Returns the buffer this text area is editing.
  324. * @since jedit 4.3pre3
  325. *
  326. * Prior to 4.3pre3, this function returned a "Buffer" type.
  327. * If this causes your code to break, try calling view.getBuffer() instead of
  328. * view.getTextArea().getBuffer().
  329. *
  330. */
  331. public final JEditBuffer getBuffer()
  332. {
  333. return buffer;
  334. } //}}}
  335. //{{{ setBuffer() method
  336. /**
  337. * Sets the buffer this text area is editing.
  338. * If you don't run a standalone textarea in jEdit please do not call this method -
  339. * use {@link org.gjt.sp.jedit.EditPane#setBuffer(org.gjt.sp.jedit.Buffer)} instead.
  340. * @param buffer The buffer
  341. */
  342. public void setBuffer(JEditBuffer buffer)
  343. {
  344. if(this.buffer == buffer)
  345. return;
  346. try
  347. {
  348. bufferChanging = true;
  349. if(this.buffer != null)
  350. {
  351. // dubious?
  352. //setFirstLine(0);
  353. if(!this.buffer.isLoading())
  354. selectNone();
  355. caretLine = caret = caretScreenLine = 0;
  356. match = null;
  357. }
  358. boolean inCompoundEdit = false;
  359. if (this.buffer != null)
  360. inCompoundEdit = this.buffer.insideCompoundEdit();
  361. if (inCompoundEdit)
  362. this.buffer.endCompoundEdit();
  363. this.buffer = buffer;
  364. if (inCompoundEdit)
  365. this.buffer.beginCompoundEdit();
  366. chunkCache.setBuffer(buffer);
  367. gutter.setBuffer(buffer);
  368. propertiesChanged();
  369. if(displayManager != null)
  370. {
  371. displayManager.release();
  372. }
  373. displayManager = DisplayManager.getDisplayManager(
  374. buffer,this);
  375. displayManager.init();
  376. if(buffer.isLoading())
  377. updateScrollBar();
  378. repaint();
  379. fireScrollEvent(true);
  380. }
  381. finally
  382. {
  383. bufferChanging = false;
  384. }
  385. } //}}}
  386. //{{{ isEditable() method
  387. /**
  388. * Returns true if this text area is editable, false otherwise.
  389. */
  390. public final boolean isEditable()
  391. {
  392. return buffer.isEditable();
  393. } //}}}
  394. //{{{ isDragEnabled() method
  395. /**
  396. * Returns if drag and drop of text is enabled.
  397. * @since jEdit 4.2pre5
  398. */
  399. public boolean isDragEnabled()
  400. {
  401. return dndEnabled;
  402. } //}}}
  403. //{{{ setDragEnabled() method
  404. /**
  405. * Sets if drag and drop of text is enabled.
  406. * @since jEdit 4.2pre5
  407. */
  408. public void setDragEnabled(boolean dndEnabled)
  409. {
  410. this.dndEnabled = dndEnabled;
  411. } //}}}
  412. //{{{ getJoinNonWordChars() method
  413. /**
  414. * If set, double clicking will join non-word characters to form one "word".
  415. * @since jEdit 4.3pre2
  416. */
  417. public boolean getJoinNonWordChars()
  418. {
  419. return joinNonWordChars;
  420. } //}}}
  421. //{{{ setJoinNonWordChars() method
  422. /**
  423. * If set, double clicking will join non-word characters to form one "word".
  424. * @since jEdit 4.3pre2
  425. */
  426. public void setJoinNonWordChars(boolean joinNonWordChars)
  427. {
  428. this.joinNonWordChars = joinNonWordChars;
  429. } //}}}
  430. //{{{ getCtrlForRectangularSelection() method
  431. /**
  432. * If set, CTRL enables rectangular selection mode while pressed.
  433. * @since jEdit 4.3pre10
  434. */
  435. public boolean isCtrlForRectangularSelection()
  436. {
  437. return ctrlForRectangularSelection;
  438. } //}}}
  439. //{{{ setCtrlForRectangularSelection() method
  440. /**
  441. * If set, CTRL enables rectangular selection mode while pressed.
  442. * @since jEdit 4.3pre10
  443. */
  444. public void setCtrlForRectangularSelection(boolean ctrlForRectangularSelection)
  445. {
  446. this.ctrlForRectangularSelection = ctrlForRectangularSelection;
  447. } //}}}
  448. //{{{ Scrolling
  449. //{{{ getFirstLine() method
  450. /**
  451. * Returns the vertical scroll bar position.
  452. * @since jEdit 4.2pre1
  453. */
  454. public final int getFirstLine()
  455. {
  456. return displayManager.firstLine.scrollLine
  457. + displayManager.firstLine.skew;
  458. } //}}}
  459. //{{{ setFirstLine() method
  460. /**
  461. * Sets the vertical scroll bar position
  462. *
  463. * @param firstLine The scroll bar position
  464. */
  465. public void setFirstLine(int firstLine)
  466. {
  467. //{{{ ensure we don't have empty space at the bottom or top, etc
  468. int max = displayManager.getScrollLineCount() - visibleLines
  469. + (lastLinePartial ? 1 : 0);
  470. if(firstLine > max)
  471. firstLine = max;
  472. if(firstLine < 0)
  473. firstLine = 0;
  474. //}}}
  475. if(Debug.SCROLL_DEBUG)
  476. {
  477. Log.log(Log.DEBUG,this,"setFirstLine() from "
  478. + getFirstLine() + " to " + firstLine);
  479. }
  480. int oldFirstLine = getFirstLine();
  481. if(firstLine == oldFirstLine)
  482. return;
  483. displayManager.setFirstLine(oldFirstLine,firstLine);
  484. repaint();
  485. fireScrollEvent(true);
  486. } //}}}
  487. //{{{ getFirstPhysicalLine() method
  488. /**
  489. * Returns the first visible physical line index.
  490. * @since jEdit 4.0pre4
  491. */
  492. public final int getFirstPhysicalLine()
  493. {
  494. return displayManager.firstLine.physicalLine;
  495. } //}}}
  496. //{{{ setFirstPhysicalLine() methods
  497. /**
  498. * Sets the vertical scroll bar position.
  499. * @param physFirstLine The first physical line to display
  500. * @since jEdit 4.2pre1
  501. */
  502. public void setFirstPhysicalLine(int physFirstLine)
  503. {
  504. setFirstPhysicalLine(physFirstLine,0);
  505. }
  506. /**
  507. * Sets the vertical scroll bar position.
  508. * @param physFirstLine The first physical line to display
  509. * @param skew A local screen line delta
  510. * @since jEdit 4.2pre1
  511. */
  512. public void setFirstPhysicalLine(int physFirstLine, int skew)
  513. {
  514. if(Debug.SCROLL_DEBUG)
  515. {
  516. Log.log(Log.DEBUG,this,"setFirstPhysicalLine("
  517. + physFirstLine + ',' + skew + ')');
  518. }
  519. int amount = physFirstLine - displayManager.firstLine.physicalLine;
  520. displayManager.setFirstPhysicalLine(amount,skew);
  521. repaint();
  522. fireScrollEvent(true);
  523. } //}}}
  524. //{{{ getLastPhysicalLine() method
  525. /**
  526. * Returns the last visible physical line index.
  527. * @since jEdit 4.0pre4
  528. */
  529. public final int getLastPhysicalLine()
  530. {
  531. return physLastLine;
  532. } //}}}
  533. //{{{ getLastScreenLine() method
  534. /**
  535. * Returns the last screen line index, it is different from
  536. * {@link #getVisibleLines()} because the buffer can have less lines than
  537. * the visible lines
  538. * @return the last screen line index.
  539. * @since jEdit 4.3pre1
  540. */
  541. public int getLastScreenLine()
  542. {
  543. return screenLastLine;
  544. } //}}}
  545. //{{{ getVisibleLines() method
  546. /**
  547. * Returns the number of lines visible in this text area.
  548. * @return the number of visible lines in the textarea
  549. */
  550. public final int getVisibleLines()
  551. {
  552. return visibleLines;
  553. } //}}}
  554. //{{{ getHorizontalOffset() method
  555. /**
  556. * Returns the horizontal offset of drawn lines.
  557. */
  558. public final int getHorizontalOffset()
  559. {
  560. return horizontalOffset;
  561. } //}}}
  562. //{{{ setHorizontalOffset() method
  563. /**
  564. * Sets the horizontal offset of drawn lines. This can be used to
  565. * implement horizontal scrolling.
  566. * @param horizontalOffset offset The new horizontal offset
  567. */
  568. public void setHorizontalOffset(int horizontalOffset)
  569. {
  570. if(horizontalOffset > 0)
  571. horizontalOffset = 0;
  572. if(horizontalOffset == this.horizontalOffset)
  573. return;
  574. this.horizontalOffset = horizontalOffset;
  575. painter.repaint();
  576. fireScrollEvent(false);
  577. } //}}}
  578. //{{{ scrollUpLine() method
  579. /**
  580. * Scrolls up by one line.
  581. * @since jEdit 2.7pre2
  582. */
  583. public void scrollUpLine()
  584. {
  585. setFirstLine(getFirstLine() - 1);
  586. } //}}}
  587. //{{{ scrollUpPage() method
  588. /**
  589. * Scrolls up by one page.
  590. * @since jEdit 2.7pre2
  591. */
  592. public void scrollUpPage()
  593. {
  594. setFirstLine(getFirstLine() - getVisibleLines()
  595. + (lastLinePartial ? 1 : 0));
  596. } //}}}
  597. //{{{ scrollDownLine() method
  598. /**
  599. * Scrolls down by one line.
  600. * @since jEdit 2.7pre2
  601. */
  602. public void scrollDownLine()
  603. {
  604. setFirstLine(getFirstLine() + 1);
  605. } //}}}
  606. //{{{ scrollDownPage() method
  607. /**
  608. * Scrolls down by one page.
  609. * @since jEdit 2.7pre2
  610. */
  611. public void scrollDownPage()
  612. {
  613. setFirstLine(getFirstLine() + getVisibleLines()
  614. - (lastLinePartial ? 1 : 0));
  615. } //}}}
  616. //{{{ scrollToCaret() method
  617. /**
  618. * Ensures that the caret is visible by scrolling the text area if
  619. * necessary.
  620. * @param doElectricScroll If true, electric scrolling will be performed
  621. */
  622. public void scrollToCaret(boolean doElectricScroll)
  623. {
  624. scrollTo(caretLine,caret - buffer.getLineStartOffset(caretLine),
  625. doElectricScroll);
  626. } //}}}
  627. //{{{ scrollTo() methods
  628. /**
  629. * Ensures that the specified location in the buffer is visible.
  630. * @param offset The offset from the start of the buffer
  631. * @param doElectricScroll If true, electric scrolling will be performed
  632. * @since jEdit 4.2pre3
  633. */
  634. public void scrollTo(int offset, boolean doElectricScroll)
  635. {
  636. int line = buffer.getLineOfOffset(offset);
  637. scrollTo(line,offset - buffer.getLineStartOffset(line),
  638. doElectricScroll);
  639. }
  640. /**
  641. * Ensures that the specified location in the buffer is visible.
  642. * @param line The line number
  643. * @param offset The offset from the start of the line
  644. * @param doElectricScroll If true, electric scrolling will be performed
  645. * @since jEdit 4.0pre6
  646. */
  647. public void scrollTo(int line, int offset, boolean doElectricScroll)
  648. {
  649. if (buffer.isLoading())
  650. return;
  651. if(Debug.SCROLL_TO_DEBUG)
  652. Log.log(Log.DEBUG,this,"scrollTo(), lineCount="
  653. + getLineCount());
  654. if(visibleLines <= 1)
  655. {
  656. if(Debug.SCROLL_TO_DEBUG)
  657. Log.log(Log.DEBUG,this,"visibleLines <= 0");
  658. // Fix the case when the line is wrapped
  659. // it was not possible to see the second (or next)
  660. // subregion of a line
  661. ChunkCache.LineInfo[] infos = chunkCache
  662. .getLineInfosForPhysicalLine(line);
  663. int subregion = ChunkCache.getSubregionOfOffset(
  664. offset,infos);
  665. setFirstPhysicalLine(line,subregion);
  666. return;
  667. }
  668. //{{{ Get ready
  669. int extraEndVirt;
  670. int lineLength = buffer.getLineLength(line);
  671. if(offset > lineLength)
  672. {
  673. extraEndVirt = charWidth * (offset - lineLength);
  674. offset = lineLength;
  675. }
  676. else
  677. extraEndVirt = 0;
  678. int _electricScroll = doElectricScroll
  679. && visibleLines - 1 > (electricScroll << 1)
  680. ? electricScroll : 0;
  681. //}}}
  682. //{{{ Scroll vertically
  683. int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
  684. int visibleLines = getVisibleLines();
  685. if(screenLine == -1)
  686. {
  687. // We are scrolling to a position that is not on the screen.
  688. if(Debug.SCROLL_TO_DEBUG)
  689. Log.log(Log.DEBUG,this,"screenLine == -1");
  690. ChunkCache.LineInfo[] infos = chunkCache
  691. .getLineInfosForPhysicalLine(line);
  692. int subregion = ChunkCache.getSubregionOfOffset(
  693. offset,infos);
  694. int prevLine = displayManager.getPrevVisibleLine(getFirstPhysicalLine());
  695. int nextLine = displayManager.getNextVisibleLine(getLastPhysicalLine());
  696. if(line == getFirstPhysicalLine())
  697. {
  698. if(Debug.SCROLL_TO_DEBUG)
  699. Log.log(Log.DEBUG,this,line + " == " + getFirstPhysicalLine());
  700. setFirstPhysicalLine(line,subregion
  701. - _electricScroll);
  702. }
  703. else if(line == prevLine)
  704. {
  705. if(Debug.SCROLL_TO_DEBUG)
  706. Log.log(Log.DEBUG,this,line + " == " + prevLine);
  707. setFirstPhysicalLine(prevLine,subregion
  708. - _electricScroll);
  709. }
  710. else if(line == getLastPhysicalLine())
  711. {
  712. if(Debug.SCROLL_TO_DEBUG)
  713. Log.log(Log.DEBUG,this,line + " == " + getLastPhysicalLine());
  714. setFirstPhysicalLine(line,
  715. subregion + _electricScroll
  716. - visibleLines
  717. + (lastLinePartial ? 2 : 1));
  718. }
  719. else if(line == nextLine)
  720. {
  721. if(Debug.SCROLL_TO_DEBUG)
  722. Log.log(Log.DEBUG,this,line + " == " + nextLine);
  723. setFirstPhysicalLine(nextLine,
  724. subregion + _electricScroll
  725. - visibleLines
  726. + (lastLinePartial ? 2 : 1));
  727. }
  728. else
  729. {
  730. if(Debug.SCROLL_TO_DEBUG)
  731. {
  732. Log.log(Log.DEBUG,this,"neither");
  733. Log.log(Log.DEBUG,this,"Last physical line is " + getLastPhysicalLine());
  734. }
  735. setFirstPhysicalLine(line,subregion
  736. - (visibleLines >> 1));
  737. if(Debug.SCROLL_TO_DEBUG)
  738. {
  739. Log.log(Log.DEBUG,this,"Last physical line is " + getLastPhysicalLine());
  740. }
  741. }
  742. }
  743. else if(screenLine < _electricScroll)
  744. {
  745. if(Debug.SCROLL_TO_DEBUG)
  746. Log.log(Log.DEBUG,this,"electric up");
  747. setFirstLine(getFirstLine() - _electricScroll + screenLine);
  748. }
  749. else if(screenLine > visibleLines - _electricScroll
  750. - (lastLinePartial ? 2 : 1))
  751. {
  752. if(Debug.SCROLL_TO_DEBUG)
  753. Log.log(Log.DEBUG,this,"electric down");
  754. setFirstLine(getFirstLine() + _electricScroll - visibleLines + screenLine + (lastLinePartial ? 2 : 1));
  755. } //}}}
  756. //{{{ Scroll horizontally
  757. if(!displayManager.isLineVisible(line))
  758. return;
  759. Point point = offsetToXY(line,offset,offsetXY);
  760. point.x += extraEndVirt;
  761. if(point.x < 0)
  762. {
  763. setHorizontalOffset(horizontalOffset
  764. - point.x + charWidth + 5);
  765. }
  766. else if(point.x >= painter.getWidth() - charWidth - 5)
  767. {
  768. setHorizontalOffset(horizontalOffset +
  769. (painter.getWidth() - point.x)
  770. - charWidth - 5);
  771. } //}}}
  772. } //}}}
  773. //{{{ addScrollListener() method
  774. /**
  775. * Adds a scroll listener to this text area.
  776. * @param listener The listener
  777. * @since jEdit 3.2pre2
  778. */
  779. public final void addScrollListener(ScrollListener listener)
  780. {
  781. listenerList.add(ScrollListener.class,listener);
  782. } //}}}
  783. //{{{ removeScrollListener() method
  784. /**
  785. * Removes a scroll listener from this text area.
  786. * @param listener The listener
  787. * @since jEdit 3.2pre2
  788. */
  789. public final void removeScrollListener(ScrollListener listener)
  790. {
  791. listenerList.remove(ScrollListener.class,listener);
  792. } //}}}
  793. //}}}
  794. //{{{ Screen line stuff
  795. //{{{ getPhysicalLineOfScreenLine() method
  796. /**
  797. * Returns the physical line number that contains the specified screen
  798. * line.
  799. * @param screenLine The screen line
  800. * @since jEdit 4.0pre6
  801. */
  802. public int getPhysicalLineOfScreenLine(int screenLine)
  803. {
  804. return chunkCache.getLineInfo(screenLine).physicalLine;
  805. } //}}}
  806. //{{{ getScreenLineOfOffset() method
  807. /**
  808. * Returns the screen (wrapped) line containing the specified offset.
  809. * Returns -1 if the line is not currently visible on the screen.
  810. * @param offset The offset
  811. * @since jEdit 4.0pre4
  812. */
  813. public int getScreenLineOfOffset(int offset)
  814. {
  815. int line = buffer.getLineOfOffset(offset);
  816. offset -= buffer.getLineStartOffset(line);
  817. return chunkCache.getScreenLineOfOffset(line,offset);
  818. } //}}}
  819. //{{{ getScreenLineStartOffset() method
  820. /**
  821. * Returns the start offset of the specified screen (wrapped) line.
  822. * @param line The line
  823. * @since jEdit 4.0pre4
  824. */
  825. public int getScreenLineStartOffset(int line)
  826. {
  827. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  828. if(lineInfo.physicalLine == -1)
  829. return -1;
  830. return buffer.getLineStartOffset(lineInfo.physicalLine)
  831. + lineInfo.offset;
  832. } //}}}
  833. //{{{ getScreenLineEndOffset() method
  834. /**
  835. * Returns the end offset of the specified screen (wrapped) line.
  836. * @param line The line
  837. * @since jEdit 4.0pre4
  838. */
  839. public int getScreenLineEndOffset(int line)
  840. {
  841. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(line);
  842. if(lineInfo.physicalLine == -1)
  843. return -1;
  844. return buffer.getLineStartOffset(lineInfo.physicalLine)
  845. + lineInfo.offset + lineInfo.length;
  846. } //}}}
  847. //}}}
  848. //{{{ Offset conversion
  849. //{{{ xyToOffset() methods
  850. /**
  851. * Converts a point to an offset.
  852. * Note that unlike in previous jEdit versions, this method now returns
  853. * -1 if the y co-ordinate is out of bounds.
  854. *
  855. * @param x The x co-ordinate of the point
  856. * @param y The y co-ordinate of the point
  857. */
  858. public int xyToOffset(int x, int y)
  859. {
  860. return xyToOffset(x,y,true);
  861. }
  862. /**
  863. * Converts a point to an offset.
  864. * Note that unlike in previous jEdit versions, this method now returns
  865. * -1 if the y co-ordinate is out of bounds.
  866. *
  867. * @param x The x co-ordinate of the point
  868. * @param y The y co-ordinate of the point
  869. * @param round Round up to next letter if past the middle of a letter?
  870. * @since jEdit 3.2pre6
  871. */
  872. public int xyToOffset(int x, int y, boolean round)
  873. {
  874. int height = painter.getLineHeight();
  875. int line = y / height;
  876. if(line < 0 || line >= visibleLines)
  877. return -1;
  878. return xToScreenLineOffset(line,x,round);
  879. } //}}}
  880. //{{{ xToScreenLineOffset() method
  881. /**
  882. * Converts a point in a given screen line to an offset.
  883. * Note that unlike in previous jEdit versions, this method now returns
  884. * -1 if the y co-ordinate is out of bounds.
  885. *
  886. * @param x The x co-ordinate of the point
  887. * @param screenLine The screen line
  888. * @param round Round up to next letter if past the middle of a letter?
  889. * @since jEdit 3.2pre6
  890. */
  891. public int xToScreenLineOffset(int screenLine, int x, boolean round)
  892. {
  893. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(screenLine);
  894. if(lineInfo.physicalLine == -1)
  895. {
  896. return getLineEndOffset(displayManager
  897. .getLastVisibleLine()) - 1;
  898. }
  899. else
  900. {
  901. int offset = Chunk.xToOffset(lineInfo.chunks,
  902. x - horizontalOffset,round);
  903. if(offset == -1 || offset == lineInfo.offset + lineInfo.length)
  904. offset = lineInfo.offset + lineInfo.length - 1;
  905. return getLineStartOffset(lineInfo.physicalLine) + offset;
  906. }
  907. } //}}}
  908. //{{{ offsetToXY() methods
  909. /**
  910. * Converts an offset into a point in the text area painter's
  911. * co-ordinate space.
  912. * @param offset The offset
  913. * @return The location of the offset on screen, or <code>null</code>
  914. * if the specified offset is not visible
  915. */
  916. public Point offsetToXY(int offset)
  917. {
  918. int line = buffer.getLineOfOffset(offset);
  919. offset -= buffer.getLineStartOffset(line);
  920. Point retVal = new Point();
  921. return offsetToXY(line,offset,retVal);
  922. }
  923. /**
  924. * Converts an offset into a point in the text area painter's
  925. * co-ordinate space.
  926. * @param line The line
  927. * @param offset The offset
  928. * @return The location of the offset on screen, or <code>null</code>
  929. * if the specified offset is not visible
  930. */
  931. public Point offsetToXY(int line, int offset)
  932. {
  933. return offsetToXY(line,offset,new Point());
  934. }
  935. /**
  936. * Converts a line,offset pair into an x,y (pixel) point relative to the
  937. * upper left corner (0,0) of the text area.
  938. *
  939. * @param line The physical line number (from top of document)
  940. * @param offset The offset in characters, from the start of the line
  941. * @param retVal The point to store the return value in
  942. * @return <code>retVal</code> for convenience, or <code>null</code>
  943. * if the specified offset is not visible
  944. * @since jEdit 4.0pre4
  945. */
  946. public Point offsetToXY(int line, int offset, Point retVal)
  947. {
  948. if(!displayManager.isLineVisible(line))
  949. return null;
  950. int screenLine = chunkCache.getScreenLineOfOffset(line,offset);
  951. if(screenLine == -1)
  952. return null;
  953. retVal.y = screenLine * painter.getLineHeight();
  954. ChunkCache.LineInfo info = chunkCache.getLineInfo(screenLine);
  955. retVal.x = (int)(horizontalOffset + Chunk.offsetToX(
  956. info.chunks,offset));
  957. return retVal;
  958. } //}}}
  959. //}}}
  960. //{{{ Painting
  961. //{{{ invalidateScreenLineRange() method
  962. /**
  963. * Marks a range of screen lines as needing a repaint.
  964. * @param start The first line
  965. * @param end The last line
  966. * @since jEdit 4.0pre4
  967. */
  968. public void invalidateScreenLineRange(int start, int end)
  969. {
  970. if(buffer.isLoading())
  971. return;
  972. if(start > end)
  973. {
  974. int tmp = end;
  975. end = start;
  976. start = tmp;
  977. }
  978. if(chunkCache.needFullRepaint())
  979. end = visibleLines;
  980. int y = start * painter.getLineHeight();
  981. int height = (end - start + 1) * painter.getLineHeight();
  982. painter.repaint(0,y,painter.getWidth(),height);
  983. gutter.repaint(0,y,gutter.getWidth(),height);
  984. } //}}}
  985. //{{{ invalidateLine() method
  986. /**
  987. * Marks a line as needing a repaint.
  988. * @param line The physical line to invalidate
  989. */
  990. public void invalidateLine(int line)
  991. {
  992. if(!isShowing()
  993. || buffer.isLoading()
  994. || line < getFirstPhysicalLine()
  995. || line > physLastLine
  996. || !displayManager.isLineVisible(line))
  997. return;
  998. int startLine = -1;
  999. int endLine = -1;
  1000. for(int i = 0; i < visibleLines; i++)
  1001. {
  1002. ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
  1003. if((info.physicalLine >= line || info.physicalLine == -1)
  1004. && startLine == -1)
  1005. {
  1006. startLine = i;
  1007. }
  1008. if((info.physicalLine >= line && info.lastSubregion)
  1009. || info.physicalLine == -1)
  1010. {
  1011. endLine = i;
  1012. break;
  1013. }
  1014. }
  1015. if(chunkCache.needFullRepaint() || endLine == -1)
  1016. endLine = visibleLines;
  1017. invalidateScreenLineRange(startLine,endLine);
  1018. } //}}}
  1019. //{{{ invalidateLineRange() method
  1020. /**
  1021. * Marks a range of physical lines as needing a repaint.
  1022. * @param start The first line to invalidate
  1023. * @param end The last line to invalidate
  1024. */
  1025. public void invalidateLineRange(int start, int end)
  1026. {
  1027. if(!isShowing() || buffer.isLoading())
  1028. return;
  1029. if(end < start)
  1030. {
  1031. int tmp = end;
  1032. end = start;
  1033. start = tmp;
  1034. }
  1035. if(end < getFirstPhysicalLine() || start > getLastPhysicalLine())
  1036. return;
  1037. int startScreenLine = -1;
  1038. int endScreenLine = -1;
  1039. for(int i = 0; i < visibleLines; i++)
  1040. {
  1041. ChunkCache.LineInfo info = chunkCache.getLineInfo(i);
  1042. if((info.physicalLine >= start || info.physicalLine == -1)
  1043. && startScreenLine == -1)
  1044. {
  1045. startScreenLine = i;
  1046. }
  1047. if((info.physicalLine >= end && info.lastSubregion)
  1048. || info.physicalLine == -1)
  1049. {
  1050. endScreenLine = i;
  1051. break;
  1052. }
  1053. }
  1054. if(startScreenLine == -1)
  1055. startScreenLine = 0;
  1056. if(chunkCache.needFullRepaint() || endScreenLine == -1)
  1057. endScreenLine = visibleLines;
  1058. invalidateScreenLineRange(startScreenLine,endScreenLine);
  1059. } //}}}
  1060. //}}}
  1061. //{{{ Convenience methods
  1062. //{{{ getBufferLength() method
  1063. /**
  1064. * Returns the length of the buffer.
  1065. */
  1066. public final int getBufferLength()
  1067. {
  1068. return buffer.getLength();
  1069. } //}}}
  1070. //{{{ getLineCount() method
  1071. /**
  1072. * Returns the number of physical lines in the buffer.
  1073. */
  1074. public final int getLineCount()
  1075. {
  1076. return buffer.getLineCount();
  1077. } //}}}
  1078. //{{{ getLineOfOffset() method
  1079. /**
  1080. * Returns the line containing the specified offset.
  1081. * @param offset The offset
  1082. */
  1083. public final int getLineOfOffset(int offset)
  1084. {
  1085. return buffer.getLineOfOffset(offset);
  1086. } //}}}
  1087. //{{{ getLineStartOffset() method
  1088. /**
  1089. * Returns the start offset of the specified line.
  1090. * @param line The line (physical line)
  1091. * @return The start offset of the specified line, or -1 if the line is
  1092. * invalid
  1093. */
  1094. public int getLineStartOffset(int line)
  1095. {
  1096. return buffer.getLineStartOffset(line);
  1097. } //}}}
  1098. //{{{ getLineEndOffset() method
  1099. /**
  1100. * Returns the end offset of the specified line.
  1101. * @param line The line (physical line)
  1102. * @return The end offset of the specified line, or -1 if the line is
  1103. * invalid.
  1104. */
  1105. public int getLineEndOffset(int line)
  1106. {
  1107. return buffer.getLineEndOffset(line);
  1108. } //}}}
  1109. //{{{ getLineLength() method
  1110. /**
  1111. * Returns the length of the specified line.
  1112. * @param line The line
  1113. */
  1114. public int getLineLength(int line)
  1115. {
  1116. return buffer.getLineLength(line);
  1117. } //}}}
  1118. //{{{ getText() methods
  1119. /**
  1120. * Returns the specified substring of the buffer.
  1121. * @param start The start offset
  1122. * @param len The length of the substring
  1123. * @return The substring
  1124. */
  1125. public final String getText(int start, int len)
  1126. {
  1127. return buffer.getText(start,len);
  1128. }
  1129. /**
  1130. * Copies the specified substring of the buffer into a segment.
  1131. * @param start The start offset
  1132. * @param len The length of the substring
  1133. * @param segment The segment
  1134. */
  1135. public final void getText(int start, int len, Segment segment)
  1136. {
  1137. buffer.getText(start,len,segment);
  1138. }
  1139. /**
  1140. * Returns the entire text of this text area.
  1141. */
  1142. public String getText()
  1143. {
  1144. return buffer.getText(0,buffer.getLength());
  1145. } //}}}
  1146. //{{{ getLineText() methods
  1147. /**
  1148. * Returns the text on the specified line.
  1149. * @param lineIndex the line number
  1150. * @return The text, or null if the lineIndex is invalid
  1151. */
  1152. public final String getLineText(int lineIndex)
  1153. {
  1154. return buffer.getLineText(lineIndex);
  1155. }
  1156. /**
  1157. * Copies the text on the specified line into a Segment. If lineIndex
  1158. * is invalid, the segment will contain a null string.
  1159. * @param lineIndex The line number (physical line)
  1160. * @param segment the segment into which the data will be stored.
  1161. */
  1162. public final void getLineText(int lineIndex, Segment segment)
  1163. {
  1164. buffer.getLineText(lineIndex,segment);
  1165. } //}}}
  1166. //{{{ getVisibleLineText() methods
  1167. /**
  1168. * Returns the visible part of the given line
  1169. * @param screenLine the screenLine
  1170. * @return the visible text
  1171. * @since 4.5pre1
  1172. */
  1173. public String getVisibleLineText(int screenLine)
  1174. {
  1175. int offset = -getHorizontalOffset();
  1176. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(screenLine);
  1177. int lineStartOffset = getLineStartOffset(lineInfo.physicalLine);
  1178. Point point = offsetToXY(lineStartOffset + lineInfo.offset);
  1179. int begin = xyToOffset(offset + point.x, point.y);
  1180. int end = xyToOffset(getPainter().getWidth(), point.y);
  1181. return buffer.getText(begin, end - begin);
  1182. }
  1183. /**
  1184. * Returns the visible part of the given line
  1185. * @param screenLine the screenLine
  1186. * @param segment the segment into which the data will be stored.
  1187. * @since 4.5pre1
  1188. */
  1189. public void getVisibleLineText(int screenLine, Segment segment)
  1190. {
  1191. int offset = -getHorizontalOffset();
  1192. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(screenLine);
  1193. int lineStartOffset = getLineStartOffset(lineInfo.physicalLine);
  1194. Point point = offsetToXY(lineStartOffset + lineInfo.offset);
  1195. int begin = xyToOffset(offset + point.x, point.y);
  1196. int end = xyToOffset(getPainter().getWidth(), point.y);
  1197. buffer.getText(begin, end - begin, segment);
  1198. }//}}}
  1199. /**
  1200. * Returns the visible part of the given line in a CharSequence.
  1201. * The buffer data are not copied. so this should be used in EDT
  1202. * thread
  1203. * @param screenLine the screenLine
  1204. * @return the visible text
  1205. * @since 4.5pre1
  1206. */
  1207. public CharSequence getVisibleLineSegment(int screenLine)
  1208. {
  1209. int offset = -getHorizontalOffset();
  1210. ChunkCache.LineInfo lineInfo = chunkCache.getLineInfo(screenLine);
  1211. int lineStartOffset = getLineStartOffset(lineInfo.physicalLine);
  1212. Point point = offsetToXY(lineStartOffset + lineInfo.offset);
  1213. int begin = xyToOffset(offset + point.x, point.y);
  1214. int end = xyToOffset(getPainter().getWidth(), point.y);
  1215. return buffer.getSegment(begin, end - begin);
  1216. }
  1217. //{{{ setText() method
  1218. /**
  1219. * Sets the entire text of this text area.
  1220. * @param text the new content of the buffer
  1221. */
  1222. public void setText(String text)
  1223. {
  1224. try
  1225. {
  1226. buffer.beginCompoundEdit();
  1227. buffer.remove(0,buffer.getLength());
  1228. buffer.insert(0,text);
  1229. }
  1230. finally
  1231. {
  1232. buffer.endCompoundEdit();
  1233. }
  1234. } //}}}
  1235. //}}}
  1236. //{{{ Selection
  1237. //{{{ selectAll() method
  1238. /**
  1239. * Selects all text in the buffer. Preserves the scroll position.
  1240. */
  1241. public final void selectAll()
  1242. {
  1243. int firstLine = getFirstLine();
  1244. int horizOffset = getHorizontalOffset();
  1245. setSelection(new Selection.Range(0,buffer.getLength()));
  1246. moveCaretPosition(buffer.getLength(),true);
  1247. setFirstLine(firstLine);
  1248. setHorizontalOffset(horizOffset);
  1249. } //}}}
  1250. //{{{ selectLine() method
  1251. /**
  1252. * Selects the current line.
  1253. * @since jEdit 2.7pre2
  1254. */
  1255. public void selectLine()
  1256. {
  1257. int caretLine = getCaretLine();
  1258. int start = getLineStartOffset(caretLine);
  1259. int end = getLineEndOffset(caretLine) - 1;
  1260. Selection s = new Selection.Range(start,end);
  1261. if(multi)
  1262. addToSelection(s);
  1263. else
  1264. setSelection(s);
  1265. moveCaretPosition(end);
  1266. } //}}}
  1267. //{{{ selectParagraph() method
  1268. /**
  1269. * Selects the paragraph at the caret position.
  1270. * @since jEdit 2.7pre2
  1271. */
  1272. public void selectParagraph()
  1273. {
  1274. int caretLine = getCaretLine();
  1275. if(getLineLength(caretLine) == 0)
  1276. {
  1277. getToolkit().beep();
  1278. return;
  1279. }
  1280. int start = caretLine;
  1281. int end = caretLine;
  1282. while(start >= 0)
  1283. {
  1284. if(getLineLength(start) == 0)
  1285. break;
  1286. else
  1287. start--;
  1288. }
  1289. while(end < getLineCount())
  1290. {
  1291. if(getLineLength(end) == 0)
  1292. break;
  1293. else
  1294. end++;
  1295. }
  1296. int selectionStart = getLineStartOffset(start + 1);
  1297. int selectionEnd = getLineEndOffset(end - 1) - 1;
  1298. Selection s = new Selection.Range(selectionStart,selectionEnd);
  1299. if(multi)
  1300. addToSelection(s);
  1301. else
  1302. setSelection(s);
  1303. moveCaretPosition(selectionEnd);
  1304. } //}}}
  1305. //{{{ selectWord() method
  1306. /**
  1307. * Selects the word at the caret position.
  1308. * @since jEdit 2.7pre2
  1309. */
  1310. public void selectWord()
  1311. {
  1312. int line = getCaretLine();
  1313. int lineStart = getLineStartOffset(line);
  1314. int offset = getCaretPosition() - lineStart;
  1315. if(getLineLength(line) == 0)
  1316. return;
  1317. String lineText = getLineText(line);
  1318. String noWordSep = buffer.getStringProperty("noWordSep");
  1319. if(offset == getLineLength(line))
  1320. offset--;
  1321. int wordStart = TextUtilities.findWordStart(lineText,offset,
  1322. noWordSep,true,false,false);
  1323. int wordEnd = TextUtilities.findWordEnd(lineText,offset+1,
  1324. noWordSep,true,false,false);
  1325. Selection s = new Selection.Range(lineStart + wordStart,
  1326. lineStart + wordEnd);
  1327. if(multi)
  1328. addToSelection(s);
  1329. else
  1330. setSelection(s);
  1331. moveCaretPosition(lineStart + wordEnd);
  1332. } //}}}
  1333. //{{{ selectToMatchingBracket() method
  1334. /**
  1335. * Selects from the bracket at the specified position to the
  1336. * corresponding bracket.
  1337. * @since jEdit 4.2pre1
  1338. */
  1339. public Selection selectToMatchingBracket(int position,
  1340. boolean quickCopy)
  1341. {
  1342. int positionLine = buffer.getLineOfOffset(position);
  1343. int lineOffset = position - buffer.getLineStartOffset(positionLine);
  1344. if(getLineLength(positionLine) != 0)
  1345. {
  1346. int bracket = TextUtilities.findMatchingBracket(buffer,
  1347. positionLine,Math.max(0,lineOffset - 1));
  1348. if(bracket != -1)
  1349. {
  1350. Selection s;
  1351. if(bracket < position)
  1352. {
  1353. if(!quickCopy)
  1354. moveCaretPosition(position,false);
  1355. s = new Selection.Range(bracket,position);
  1356. }
  1357. else
  1358. {
  1359. if(!quickCopy)
  1360. moveCaretPosition(bracket + 1,false);
  1361. s = new Selection.Range(position - 1,bracket + 1);
  1362. }
  1363. if(!multi && !quickCopy)
  1364. selectNone();
  1365. addToSelection(s);
  1366. return s;
  1367. }
  1368. }
  1369. return null;
  1370. }
  1371. /**
  1372. * Selects from the bracket at the caret position to the corresponding
  1373. * bracket.
  1374. * @since jEdit 4.0pre2
  1375. */
  1376. public void selectToMatchingBracket()
  1377. {
  1378. selectToMatchingBracket(caret,false);
  1379. } //}}}
  1380. //{{{ selectBlock() method
  1381. /**
  1382. * Selects the code block surrounding the caret.
  1383. * @since jEdit 2.7pre2
  1384. */
  1385. public void selectBlock()
  1386. {
  1387. Selection s = getSelectionAtOffset(caret);
  1388. int start, end;
  1389. if(s == null)
  1390. start = end = caret;
  1391. else
  1392. {
  1393. start = s.start;
  1394. end = s.end;
  1395. }
  1396. String text = getText(0,buffer.getLength());
  1397. // We can't do the backward scan if start == 0
  1398. if(start == 0)
  1399. {
  1400. getToolkit().beep();
  1401. return;
  1402. }
  1403. // Scan backwards, trying to find a bracket
  1404. String openBrackets = "([{";
  1405. String closeBrackets = ")]}";
  1406. int count = 1;
  1407. char openBracket = '\0';
  1408. char closeBracket = '\0';
  1409. backward_scan: while(--start >= 0)
  1410. {
  1411. char c = text.charAt(start);
  1412. int index = openBrackets.indexOf(c);
  1413. if(index != -1)
  1414. {
  1415. if(--count == 0)
  1416. {
  1417. openBracket = c;
  1418. closeBracket = closeBrackets.charAt(index);
  1419. break backward_scan;
  1420. }
  1421. }
  1422. else if(closeBrackets.indexOf(c) != -1)
  1423. count++;
  1424. }
  1425. // Reset count
  1426. count = 1;
  1427. // Scan forward, matching that bracket
  1428. if(openBracket == '\0')
  1429. {
  1430. getToolkit().beep();
  1431. return;
  1432. }
  1433. forward_scan: do
  1434. {
  1435. char c = text.charAt(end);
  1436. if(c == closeBracket)
  1437. {
  1438. if(--count == 0)
  1439. {
  1440. end++;
  1441. break forward_scan;
  1442. }
  1443. }
  1444. else if(c == openBracket)
  1445. count++;
  1446. }
  1447. while(++end < buffer.getLength());
  1448. s = new Selection.Range(start,end);
  1449. if(multi)
  1450. addToSelection(s);
  1451. else
  1452. setSelection(s);
  1453. moveCaretPosition(end);
  1454. } //}}}
  1455. //{{{ lineInStructureScope() method
  1456. /**
  1457. * Returns if the specified line is contained in the currently
  1458. * matched structure's scope.
  1459. * @since jEdit 4.2pre3
  1460. */
  1461. public boolean lineInStructureScope(int line)
  1462. {
  1463. if(match == null)
  1464. return false;
  1465. if(match.startLine < caretLine)
  1466. return line >= match.startLine && line <= caretLine;
  1467. else
  1468. return line <= match.endLine && line >= caretLine;
  1469. } //}}}
  1470. //{{{ invertSelection() method
  1471. /**
  1472. * Inverts the selection.
  1473. * @since jEdit 4.0pre1
  1474. */
  1475. public final void invertSelection()
  1476. {
  1477. selectionManager.invertSelection();
  1478. } //}}}
  1479. //{{{ getSelectionCount() method
  1480. /**
  1481. * Returns the number of selections. This can be used to test
  1482. * for the existence of selections.
  1483. * @since jEdit 3.2pre2
  1484. */
  1485. public int getSelectionCount()
  1486. {
  1487. return selectionManager.getSelectionCount();
  1488. } //}}}
  1489. //{{{ getSelection() methods
  1490. /**
  1491. * Returns the current selection.
  1492. * @since jEdit 3.2pre1
  1493. */
  1494. public Selection[] getSelection()
  1495. {
  1496. return selectionManager.getSelection();
  1497. }
  1498. /**
  1499. * Returns the selection with the specified index. This must be
  1500. * between 0 and the return value of <code>getSelectionCount()</code>.
  1501. * @since jEdit 4.3pre1
  1502. * @param index the index of the selection you want
  1503. */
  1504. public Selection getSelection(int index)
  1505. {
  1506. return selectionManager.selection.get(index);
  1507. } //}}}
  1508. //{{{ getSelectionIterator() method
  1509. /**
  1510. * Returns the current selection.
  1511. * @since jEdit 4.3pre1
  1512. */
  1513. public Iterator<Selection> getSelectionIterator()
  1514. {
  1515. return selectionManager.selection.iterator();
  1516. } //}}}
  1517. //{{{ selectNone() method
  1518. /**
  1519. * Deselects everything.
  1520. */
  1521. public void selectNone()
  1522. {
  1523. invalidateSelectedLines();
  1524. setSelection((Selection)null);
  1525. } //}}}
  1526. //{{{ setSelection() methods
  1527. /**
  1528. * Sets the selection. Nested and overlapping selections are merged
  1529. * where possible. Null elements of the array are ignored.
  1530. * @param selection The new selection
  1531. * since jEdit 3.2pre1
  1532. */
  1533. public void setSelection(Selection[] selection)
  1534. {
  1535. // invalidate the old selection
  1536. invalidateSelectedLines();
  1537. selectionManager.setSelection(selection);
  1538. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1539. }
  1540. /**
  1541. * Sets the selection. Nested and overlapping selections are merged
  1542. * where possible.
  1543. * @param selection The new selection
  1544. * since jEdit 3.2pre1
  1545. */
  1546. public void setSelection(Selection selection)
  1547. {
  1548. invalidateSelectedLines();
  1549. selectionManager.setSelection(selection);
  1550. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1551. } //}}}
  1552. //{{{ addToSelection() methods
  1553. /**
  1554. * Adds to the selection. Nested and overlapping selections are merged
  1555. * where possible.
  1556. * @param selection The new selection
  1557. * since jEdit 3.2pre1
  1558. */
  1559. public void addToSelection(Selection[] selection)
  1560. {
  1561. invalidateSelectedLines();
  1562. selectionManager.addToSelection(selection);
  1563. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1564. }
  1565. /**
  1566. * Adds to the selection. Nested and overlapping selections are merged
  1567. * where possible.
  1568. * @param selection The new selection
  1569. * since jEdit 3.2pre1
  1570. */
  1571. public void addToSelection(Selection selection)
  1572. {
  1573. invalidateSelectedLines();
  1574. selectionManager.addToSelection(selection);
  1575. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1576. } //}}}
  1577. //{{{ getSelectionAtOffset() method
  1578. /**
  1579. * Returns the selection containing the specific offset, or <code>null</code>
  1580. * if there is no selection at that offset.
  1581. * @param offset The offset
  1582. * @since jEdit 3.2pre1
  1583. */
  1584. public Selection getSelectionAtOffset(int offset)
  1585. {
  1586. return selectionManager.getSelectionAtOffset(offset);
  1587. } //}}}
  1588. //{{{ removeFromSelection() methods
  1589. /**
  1590. * Deactivates the specified selection.
  1591. * @param sel The selection
  1592. * @since jEdit 3.2pre1
  1593. */
  1594. public void removeFromSelection(Selection sel)
  1595. {
  1596. invalidateSelectedLines();
  1597. selectionManager.removeFromSelection(sel);
  1598. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1599. }
  1600. /**
  1601. * Deactivates the selection at the specified offset. If there is
  1602. * no selection at that offset, does nothing.
  1603. * @param offset The offset
  1604. * @since jEdit 3.2pre1
  1605. */
  1606. public void removeFromSelection(int offset)
  1607. {
  1608. Selection sel = getSelectionAtOffset(offset);
  1609. if(sel == null)
  1610. return;
  1611. invalidateSelectedLines();
  1612. selectionManager.removeFromSelection(sel);
  1613. finishCaretUpdate(caretLine,NO_SCROLL,true);
  1614. } //}}}
  1615. //{{{ resizeSelection() method
  1616. /**
  1617. * Resizes the selection at the specified offset, or creates a new
  1618. * one if there is no selection at the specified offset. This is a
  1619. * utility method that is mainly useful in the mouse event handler
  1620. * because it handles the case of end being before offset gracefully
  1621. * (unlike the rest of the selection API).
  1622. * @param offset The offset
  1623. * @param end The new selection end
  1624. * @param extraEndVirt Only for rectangular selections - specifies how
  1625. * far it extends into virtual space.
  1626. * @param rect Make the selection rectangular?
  1627. * @since jEdit 3.2pre1
  1628. */
  1629. public void resizeSelection(int offset, int end, int extraEndVirt,
  1630. boolean rect)
  1631. {
  1632. Selection s = selectionManager.getSelectionAtOffset(offset);
  1633. if(s != null)
  1634. {
  1635. invalidateLineRange(s.startLine,s.endLine);
  1636. selectionManager.removeFromSelection(s);
  1637. }
  1638. selectionManager.resizeSelection(offset,end,extraEndVirt,rect);
  1639. fireCaretEvent();
  1640. } //}}}
  1641. //{{{ extendSelection() methods
  1642. /**
  1643. * Extends the selection at the specified offset, or creates a new
  1644. * one if there is no selection at the specified offset. This is
  1645. * different from resizing in that the new chunk is added to the
  1646. * selection in question, instead of replacing it.
  1647. * @param offset The offset
  1648. * @param end The new selection end
  1649. * @since jEdit 3.2pre1
  1650. */
  1651. public void extendSelection(int offset, int end)
  1652. {
  1653. extendSelection(offset,end,0,0);
  1654. }
  1655. /**
  1656. * Extends the selection at the specified offset, or creates a new
  1657. * one if there is no selection at the specified offset. This is
  1658. * different from resizing in that the new chunk is added to the
  1659. * selection in question, instead of replacing it.
  1660. * @param offset The offset
  1661. * @param end The new selection end
  1662. * @param extraStartVirt Extra virtual space at the start
  1663. * @param extraEndVirt Extra virtual space at the end
  1664. * @since jEdit 4.2pre1
  1665. */
  1666. public void extendSelection(int offset, int end,
  1667. int extraStartVirt, int extraEndVirt)
  1668. {
  1669. Selection s = getSelectionAtOffset(offset);
  1670. if(s != null)
  1671. {
  1672. invalidateLineRange(s.startLine,s.endLine);
  1673. selectionManager.removeFromSelection(s);
  1674. if(offset == s.start)
  1675. {
  1676. offset = end;
  1677. end = s.end;
  1678. }
  1679. else if(offset == s.end)
  1680. {
  1681. offset = s.start;
  1682. }
  1683. }
  1684. if(end < offset)
  1685. {
  1686. int tmp = end;
  1687. end = offset;
  1688. offset = tmp;
  1689. }
  1690. if(rectangularSelectionMode)
  1691. {
  1692. s = new Selection.Rect(offset,end);
  1693. ((Selection.Rect)s).extraStartVirt = extraStartVirt;
  1694. ((Selection.Rect)s).extraEndVirt = extraEndVirt;
  1695. }
  1696. else
  1697. s = new Selection.Range(offset,end);
  1698. selectionManager.addToSelection(s);
  1699. fireCaretEvent();
  1700. if(rectangularSelectionMode && extraEndVirt != 0)
  1701. {
  1702. int line = getLineOfOffset(end);
  1703. scrollTo(line,getLineLength(line) + extraEndVirt,false);
  1704. }
  1705. } //}}}
  1706. //{{{ getSelectedText() methods
  1707. /**
  1708. * Returns the text in the specified selection.
  1709. * @param s The selection
  1710. * @since jEdit 3.2pre1
  1711. */
  1712. public String getSelectedText(Selection s)
  1713. {
  1714. StringBuilder buf = new StringBuilder(s.end - s.start);
  1715. s.getText(buffer,buf);
  1716. return buf.toString();
  1717. }
  1718. /**
  1719. * Returns the text in all active selections.
  1720. * @param separator The string to insert between each text chunk
  1721. * (for example, a newline)
  1722. * @since jEdit 3.2pre1
  1723. */
  1724. public String getSelectedText(String separator)
  1725. {
  1726. Selection[] sel = selectionManager.getSelection();
  1727. if(sel.length == 0)
  1728. return null;
  1729. StringBuilder buf = new StringBuilder();
  1730. for(int i = 0; i < sel.length; i++)
  1731. {
  1732. if(i != 0)
  1733. buf.append(separator);
  1734. sel[i].getText(buffer,buf);
  1735. }
  1736. return buf.toString();
  1737. }
  1738. /**
  1739. * Returns the text in all active selections, with a newline
  1740. * between each text chunk.
  1741. */
  1742. public String getSelectedText()
  1743. {
  1744. return getSelectedText("\n");
  1745. } //}}}
  1746. //{{{ setSelectedText() methods
  1747. /**
  1748. * Replaces the selection with the specified text.
  1749. * @param s The selection
  1750. * @param selectedText The new text
  1751. * @since jEdit 3.2pre1
  1752. */
  1753. public void setSelectedText(Selection s, String selectedText)
  1754. {
  1755. if(!isEditable())
  1756. {
  1757. throw new InternalError("Text component"
  1758. + " read only");
  1759. }
  1760. try
  1761. {
  1762. buffer.beginCompoundEdit();
  1763. moveCaretPosition(s.setText(buffer,selectedText));
  1764. }
  1765. // No matter what happends... stops us from leaving buffer
  1766. // in a bad state
  1767. finally
  1768. {
  1769. buffer.endCompoundEdit();
  1770. }
  1771. // no no no!!!!
  1772. //selectNone();
  1773. }
  1774. /**
  1775. * Replaces the selection at the caret with the specified text.
  1776. * If there is no selection at the caret, the text is inserted at
  1777. * the caret position.
  1778. */
  1779. public void setSelectedText(String selectedText)
  1780. {
  1781. int newCaret = replaceSelection(selectedText);
  1782. if(newCaret != -1)
  1783. moveCaretPosition(newCaret);
  1784. selectNone();
  1785. }
  1786. /**
  1787. * Replaces the selection at the caret with the specified text.
  1788. * If there is no selection at the caret, the text is inserted at
  1789. * the caret position.
  1790. * @param selectedText The new selection
  1791. * @param moveCaret Move caret to insertion location if necessary
  1792. * @since jEdit 4.2pre5
  1793. */
  1794. public void setSelectedText(String selectedText, boolean moveCaret)
  1795. {
  1796. int newCaret = replaceSelection(selectedText);
  1797. if(moveCaret && newCaret != -1)
  1798. moveCaretPosition(newCaret);
  1799. selectNone();
  1800. } //}}}
  1801. //{{{ replaceSelection() method
  1802. /**
  1803. * Set the selection, but does not deactivate it, and does not move the
  1804. * caret.
  1805. *
  1806. * Please use {@link #setSelectedText(String)} instead.
  1807. *
  1808. * @param selectedText The new selection
  1809. * @return The new caret position
  1810. * @since 4.3pre1
  1811. */
  1812. public int replaceSelection(String selectedText)
  1813. {
  1814. if(!isEditable())
  1815. throw new RuntimeException("Text component read only");
  1816. int newCaret = -1;
  1817. if(getSelectionCount() == 0)
  1818. {
  1819. // for compatibility with older jEdit versions
  1820. buffer.insert(caret,selectedText);
  1821. }
  1822. else
  1823. {
  1824. try
  1825. {
  1826. buffer.beginCompoundEdit();
  1827. Selection[] selection = getSelection();
  1828. for(int i = 0; i < selection.length; i++)
  1829. newCaret = selection[i].setText(buffer,selectedText);
  1830. }
  1831. finally
  1832. {
  1833. buffer.endCompoundEdit();
  1834. }
  1835. }
  1836. return newCaret;
  1837. } //}}}
  1838. //{{{ getSelectedLines() method
  1839. /**
  1840. * Returns a sorted array of line numbers on which a selection or
  1841. * selections are present.<p>
  1842. *
  1843. * This method is the most convenient way to iterate through selected
  1844. * lines in a buffer. The line numbers in the array returned by this
  1845. * method can be passed as a parameter to such methods as
  1846. * {@link JEditBuffer#getLineText(int)}.
  1847. *
  1848. * @since jEdit 3.2pre1
  1849. */
  1850. public int[] getSelectedLines()
  1851. {
  1852. if(selectionManager.getSelectionCount() == 0)
  1853. return new int[] { caretLine };
  1854. return selectionManager.getSelectedLines();
  1855. } //}}}
  1856. //}}}
  1857. //{{{ Caret
  1858. //{{{ caretAutoScroll() method
  1859. /**
  1860. * Return if change in buffer should scroll this text area.
  1861. * @since jEdit 4.3pre2
  1862. */
  1863. public boolean caretAutoScroll()
  1864. {
  1865. return focusedComponent == this;
  1866. } //}}}
  1867. //{{{ addStructureMatcher() method
  1868. /**
  1869. * Adds a structure matcher.
  1870. * @since jEdit 4.2pre3
  1871. */
  1872. public void addStructureMatcher(StructureMatcher matcher)
  1873. {
  1874. structureMatchers.add(matcher);
  1875. } //}}}
  1876. //{{{ removeStructureMatcher() method
  1877. /**
  1878. * Removes a structure matcher.
  1879. * @since jEdit 4.2pre3
  1880. */
  1881. public void removeStructureMatcher(StructureMatcher matcher)
  1882. {
  1883. structureMatchers.remove(matcher);
  1884. } //}}}
  1885. //{{{ getStructureMatchStart() method
  1886. /**
  1887. * Returns the structure element (bracket, or XML tag, etc) matching the
  1888. * one before the caret.
  1889. * @since jEdit 4.2pre3
  1890. */
  1891. public StructureMatcher.Match getStructureMatch()
  1892. {
  1893. return match;
  1894. } //}}}
  1895. //{{{ blinkCaret() method
  1896. /**
  1897. * Blinks the caret.
  1898. */
  1899. public final void blinkCaret()
  1900. {
  1901. if(caretBlinks)
  1902. {
  1903. blink = !blink;
  1904. invalidateLine(caretLine);
  1905. }
  1906. else
  1907. blink = true;
  1908. } //}}}
  1909. //{{{ centerCaret() method
  1910. /**
  1911. * Centers the caret on the screen.
  1912. * @since jEdit 2.7pre2
  1913. */
  1914. public void centerCaret()
  1915. {
  1916. int offset = getScreenLineStartOffset(visibleLines >> 1);
  1917. if(offset == -1)
  1918. getToolkit().beep();
  1919. else
  1920. setCaretPosition(offset);
  1921. } //}}}
  1922. // {{{ scrollAndCenterCaret() method
  1923. /**
  1924. * Tries to scroll the textArea so that the caret is centered on the screen.
  1925. * Sometimes gets confused by folds but at least makes the caret visible and
  1926. * guesses better on subsequent attempts.
  1927. *
  1928. * @since jEdit 4.3pre15
  1929. */
  1930. public void scrollAndCenterCaret()
  1931. {
  1932. if (!getDisplayManager().isLineVisible(getCaretLine()))
  1933. getDisplayManager().expandFold(getCaretLine(),true);
  1934. int physicalLine = getCaretLine();
  1935. int midPhysicalLine = getPhysicalLineOfScreenLine(visibleLines >> 1);
  1936. int diff = physicalLine - midPhysicalLine;
  1937. setFirstLine(getFirstLine() + diff);
  1938. requestFocus();
  1939. } // }}}
  1940. //{{{ setCaretPosition() methods
  1941. /**
  1942. * Sets the caret position and deactivates the selection.
  1943. * @param newCaret The caret position
  1944. */
  1945. public void setCaretPosition(int newCaret)
  1946. {
  1947. selectNone();
  1948. moveCaretPosition(newCaret,true);
  1949. }
  1950. /**
  1951. * Sets the caret position and deactivates the selection.
  1952. * @param newCaret The caret position
  1953. * @param doElectricScroll Do electric scrolling?
  1954. */
  1955. public void setCaretPosition(int newCaret, boolean doElectricScroll)
  1956. {
  1957. selectNone();
  1958. moveCaretPosition(newCaret,doElectricScroll);
  1959. } //}}}
  1960. //{{{ moveCaretPosition() methods
  1961. /**
  1962. * Sets the caret position without deactivating the selection.
  1963. * @param newCaret The caret position
  1964. */
  1965. public void moveCaretPosition(int newCaret)
  1966. {
  1967. moveCaretPosition(newCaret,true);
  1968. }
  1969. /**
  1970. * Sets the caret position without deactivating the selection.
  1971. * @param newCaret The caret position
  1972. * @param doElectricScroll Do electric scrolling?
  1973. */
  1974. public void moveCaretPosition(int newCaret, boolean doElectricScroll)
  1975. {
  1976. moveCaretPosition(newCaret,doElectricScroll ? ELECTRIC_SCROLL
  1977. : NORMAL_SCROLL);
  1978. }
  1979. public static final int NO_SCROLL = 0;
  1980. public static final int NORMAL_SCROLL = 1;
  1981. public static final int ELECTRIC_SCROLL = 2;
  1982. /**
  1983. * Sets the caret position without deactivating the selection.
  1984. * @param newCaret The caret position
  1985. * @param scrollMode The scroll mode (NO_SCROLL, NORMAL_SCROLL, or
  1986. * ELECTRIC_SCROLL).
  1987. * @since jEdit 4.2pre1
  1988. */
  1989. public void moveCaretPosition(int newCaret, int scrollMode)
  1990. {
  1991. if(newCaret < 0 || newCaret > buffer.getLength())
  1992. {
  1993. throw new IllegalArgumentException("caret out of bounds: "
  1994. + newCaret);
  1995. }
  1996. int oldCaretLine = caretLine;
  1997. if(caret == newCaret)
  1998. finishCaretUpdate(oldCaretLine,scrollMode,false);
  1999. else
  2000. {
  2001. caret = newCaret;
  2002. caretLine = getLineOfOffset(newCaret);
  2003. magicCaret = -1;
  2004. finishCaretUpdate(oldCaretLine,scrollMode,true);
  2005. }
  2006. } //}}}
  2007. //{{{ getCaretPosition() method
  2008. /**
  2009. * Returns a zero-based index of the caret position.
  2010. */
  2011. public int getCaretPosition()
  2012. {
  2013. return caret;
  2014. } //}}}
  2015. //{{{ getCaretLine() method
  2016. /**
  2017. * Returns the line number containing the caret.
  2018. */
  2019. public int getCaretLine()
  2020. {
  2021. return caretLine;
  2022. } //}}}
  2023. //{{{ getMagicCaretPosition() method
  2024. /**
  2025. * Returns an internal position used to keep the caret in one
  2026. * column while moving around lines of varying lengths.
  2027. * @since jEdit 4.2pre1
  2028. */
  2029. public int getMagicCaretPosition()
  2030. {
  2031. if(magicCaret == -1)
  2032. {
  2033. magicCaret = chunkCache.subregionOffsetToX(
  2034. caretLine,caret - getLineStartOffset(caretLine));
  2035. }
  2036. return magicCaret;
  2037. } //}}}
  2038. //{{{ setMagicCaretPosition() method
  2039. /**
  2040. * Sets the `magic' caret position. This can be used to preserve
  2041. * the column position when moving up and down lines.
  2042. * @param magicCaret The magic caret position
  2043. * @since jEdit 4.2pre1
  2044. */
  2045. public void setMagicCaretPosition(int magicCaret)
  2046. {
  2047. this.magicCaret = magicCaret;
  2048. } //}}}
  2049. //{{{ addCaretListener() method
  2050. /**
  2051. * Adds a caret change listener to this text area.
  2052. * @param listener The listener
  2053. */
  2054. public final void addCaretListener(CaretListener listener)
  2055. {
  2056. listenerList.add(CaretListener.class,listener);
  2057. } //}}}
  2058. //{{{ removeCaretListener() method
  2059. /**
  2060. * Removes a caret change listener from this text area.
  2061. * @param listener The listener
  2062. */
  2063. public final void removeCaretListener(CaretListener listener)
  2064. {
  2065. listenerList.remove(CaretListener.class,listener);
  2066. } //}}}
  2067. //{{{ goToNextBracket() method
  2068. /**
  2069. * Moves the caret to the next closing bracket.
  2070. * @param select true if you want to extend selection
  2071. * @since jEdit 2.7pre2.
  2072. */
  2073. public void goToNextBracket(boolean select)
  2074. {
  2075. int newCaret = -1;
  2076. if(caret != buffer.getLength())
  2077. {
  2078. String text = getText(caret,buffer.getLength()
  2079. - caret - 1);
  2080. loop: for(int i = 0; i < text.length(); i++)
  2081. {
  2082. switch(text.charAt(i))
  2083. {
  2084. case ')': case ']': case '}':
  2085. newCaret = caret + i + 1;
  2086. break loop;
  2087. }
  2088. }
  2089. }
  2090. if(newCaret == -1)
  2091. getToolkit().beep();
  2092. else
  2093. {
  2094. if(select)
  2095. extendSelection(caret,newCaret);
  2096. else if(!multi)
  2097. selectNone();
  2098. moveCaretPosition(newCaret);
  2099. }
  2100. } //}}}
  2101. //{{{ goToNextCharacter() method
  2102. /**
  2103. * Moves the caret to the next character.
  2104. * @param select true if you want to extend selection
  2105. * @since jEdit 2.7pre2.
  2106. */
  2107. public void goToNextCharacter(boolean select)
  2108. {
  2109. Selection s = getSelectionAtOffset(caret);
  2110. if(!select && s instanceof Selection.Range)
  2111. {
  2112. if(multi)
  2113. {
  2114. if(caret != s.end)
  2115. {
  2116. moveCaretPosition(s.end);
  2117. return;
  2118. }
  2119. }
  2120. else
  2121. {
  2122. setCaretPosition(s.end);
  2123. return;
  2124. }
  2125. }
  2126. int extraStartVirt, extraEndVirt;
  2127. if(s instanceof Selection.Rect)
  2128. {
  2129. extraStartVirt = ((Selection.Rect)s).extraStartVirt;
  2130. extraEndVirt = ((Selection.Rect)s).extraEndVirt;
  2131. }
  2132. else
  2133. {
  2134. extraStartVirt = 0;
  2135. extraEndVirt = 0;
  2136. }
  2137. int newCaret = caret;
  2138. if(caret == buffer.getLength())
  2139. {
  2140. if(select && (rectangularSelectionMode || s instanceof Selection.Rect))
  2141. {
  2142. if(s != null && caret == s.start)
  2143. extraStartVirt++;
  2144. else
  2145. extraEndVirt++;
  2146. }
  2147. else
  2148. {
  2149. getToolkit().beep();
  2150. return;
  2151. }
  2152. }
  2153. else if(caret == getLineEndOffset(caretLine) - 1)
  2154. {
  2155. if(select && (rectangularSelectionMode || s instanceof Selection.Rect))
  2156. {
  2157. if(s != null && caret == s.start)
  2158. extraStartVirt++;
  2159. else
  2160. extraEndVirt++;
  2161. }
  2162. else
  2163. {
  2164. int line = displayManager.getNextVisibleLine(caretLine);
  2165. if(line == -1)
  2166. {
  2167. getToolkit().beep();
  2168. return;
  2169. }
  2170. else
  2171. newCaret = getLineStartOffset(line);
  2172. }
  2173. }
  2174. else
  2175. newCaret = caret + 1;
  2176. if(select)
  2177. extendSelection(caret,newCaret,extraStartVirt,extraEndVirt);
  2178. else if(!multi)
  2179. selectNone();
  2180. moveCaretPosition(newCaret);
  2181. } //}}}
  2182. //{{{ goToNextLine() method
  2183. /**
  2184. * Move the caret to the next line.
  2185. * @param select true if you want to extend selection
  2186. * @since jEdit 2.7pre2
  2187. */
  2188. public void goToNextLine(boolean select)
  2189. {
  2190. Selection s = getSelectionAtOffset(caret);
  2191. boolean rectSelect = s == null ? rectangularSelectionMode
  2192. : s instanceof Selection.Rect;
  2193. int magic = getMagicCaretPosition();
  2194. int newCaret = chunkCache.getBelowPosition(caretLine,
  2195. caret - buffer.getLineStartOffset(caretLine),magic + 1,
  2196. rectSelect && select);
  2197. if(newCaret == -1)
  2198. {
  2199. int end = getLineEndOffset(caretLine) - 1;
  2200. if(caret == end)
  2201. {
  2202. getToolkit().beep();
  2203. return;
  2204. }
  2205. else
  2206. newCaret = end;
  2207. }
  2208. _changeLine(select, newCaret);
  2209. setMagicCaretPosition(magic);
  2210. }//}}}
  2211. //{{{ goToNextPage() method
  2212. /**
  2213. * Moves the caret to the next screenful.
  2214. * @param select true if you want to extend selection
  2215. * @since jEdit 2.7pre2.
  2216. */
  2217. public void goToNextPage(boolean select)
  2218. {
  2219. scrollToCaret(false);
  2220. int magic = getMagicCaretPosition();
  2221. if(caretLine < displayManager.getFirstVisibleLine())
  2222. {
  2223. caretLine = displayManager.getNextVisibleLine(
  2224. caretLine);
  2225. }
  2226. int newCaret;
  2227. if(getFirstLine() + getVisibleLines() >= displayManager
  2228. .getScrollLineCount())
  2229. {
  2230. int lastVisibleLine = displayManager
  2231. .getLastVisibleLine();
  2232. newCaret = getLineEndOffset(lastVisibleLine) - 1;
  2233. }
  2234. else
  2235. {
  2236. int caretScreenLine = getScreenLineOfOffset(caret);
  2237. scrollDownPage();
  2238. newCaret = xToScreenLineOffset(caretScreenLine,
  2239. magic,true);
  2240. }
  2241. if(select)
  2242. extendSelection(caret,newCaret);
  2243. else if(!multi)
  2244. selectNone();
  2245. moveCaretPosition(newCaret,false);
  2246. setMagicCaretPosition(magic);
  2247. } //}}}
  2248. //{{{ goToNextParagraph() method
  2249. /**
  2250. * Moves the caret to the start of the next paragraph.
  2251. * @param select true if you want to extend selection
  2252. * @since jEdit 2.7pre2
  2253. */
  2254. public void goToNextParagraph(boolean select)
  2255. {
  2256. int lineNo = getCaretLine();
  2257. int newCaret = getBufferLength();
  2258. boolean foundBlank = false;
  2259. final Segment lineSegment = new Segment();
  2260. loop: for(int i = lineNo + 1; i < getLineCount(); i++)
  2261. {
  2262. if(!displayManager.isLineVisible(i))
  2263. continue;
  2264. getLineText(i,lineSegment);
  2265. for(int j = 0; j < lineSegment.count; j++)
  2266. {
  2267. switch(lineSegment.array[lineSegment.offset + j])
  2268. {
  2269. case ' ':
  2270. case '\t':
  2271. break;
  2272. default:
  2273. if(foundBlank)
  2274. {
  2275. newCaret = getLineStartOffset(i);
  2276. break loop;
  2277. }
  2278. else
  2279. continue loop;
  2280. }
  2281. }
  2282. foundBlank = true;
  2283. }
  2284. if(select)
  2285. extendSelection(caret,newCaret);
  2286. else if(!multi)
  2287. selectNone();
  2288. moveCaretPosition(newCaret);
  2289. } //}}}
  2290. //{{{ goToNextWord() methods
  2291. /**
  2292. * Moves the caret to the start of the next word.
  2293. * Note that if the "view.eatWhitespace" boolean propery is false,
  2294. * this method moves the caret to the end of the current word instead.
  2295. * @param select true if you want to extend selection
  2296. * @since jEdit 2.7pre2
  2297. */
  2298. public void goToNextWord(boolean select)
  2299. {
  2300. goToNextWord(select,false);
  2301. }
  2302. /**
  2303. * Moves the caret to the start of the next word.
  2304. * @since jEdit 4.1pre5
  2305. */
  2306. public void goToNextWord(boolean select, boolean eatWhitespace)
  2307. {
  2308. if (buffer.isLoading())
  2309. return;
  2310. int lineStart = getLineStartOffset(caretLine);
  2311. int newCaret = caret - lineStart;
  2312. String lineText = getLineText(caretLine);
  2313. if(newCaret == lineText.length())
  2314. {
  2315. int nextLine = displayManager.getNextVisibleLine(caretLine);
  2316. if(nextLine == -1)
  2317. {
  2318. getToolkit().beep();
  2319. return;
  2320. }
  2321. newCaret = getLineStartOffset(nextLine);
  2322. }
  2323. else
  2324. {
  2325. String noWordSep = buffer.getStringProperty("noWordSep");
  2326. boolean camelCasedWords = buffer.getBooleanProperty("camelCasedWords");
  2327. newCaret = TextUtilities.findWordEnd(lineText,
  2328. newCaret + 1,noWordSep,true,camelCasedWords,
  2329. eatWhitespace);
  2330. newCaret += lineStart;
  2331. }
  2332. if(select)
  2333. extendSelection(caret,newCaret);
  2334. else if(!multi)
  2335. selectNone();
  2336. moveCaretPosition(newCaret);
  2337. } //}}}
  2338. //{{{ goToPrevBracket() method
  2339. /**
  2340. * Moves the caret to the previous bracket.
  2341. * @param select true if you want to extend selection
  2342. * @since jEdit 2.7pre2
  2343. */
  2344. public void goToPrevBracket(boolean select)
  2345. {
  2346. String text = getText(0,caret);
  2347. int newCaret = -1;
  2348. loop: for(int i = getCaretPosition() - 1; i >= 0; i--)
  2349. {
  2350. switch(text.charAt(i))
  2351. {
  2352. case '(': case '[': case '{':
  2353. newCaret = i;
  2354. break loop;
  2355. }
  2356. }
  2357. if(newCaret == -1)
  2358. getToolkit().beep();
  2359. else
  2360. {
  2361. if(select)
  2362. extendSelection(caret,newCaret);
  2363. else if(!multi)
  2364. selectNone();
  2365. moveCaretPosition(newCaret);
  2366. }
  2367. } //}}}
  2368. //{{{ goToPrevCharacter() method
  2369. /**
  2370. * Moves the caret to the previous character.
  2371. * @param select true if you want to extend selection
  2372. * @since jEdit 2.7pre2.
  2373. */
  2374. public void g