/jEdit/branches/4.3.x-fix-view-leak/org/gjt/sp/jedit/textarea/JEditTextArea.java

# · Java · 642 lines · 375 code · 75 blank · 192 comment · 61 complexity · 85263f414587d30630e343e4700e0991 MD5 · raw 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.AWTEvent;
  26. import java.awt.event.ActionEvent;
  27. import java.awt.event.ActionListener;
  28. import java.awt.Point;
  29. import java.awt.event.MouseEvent;
  30. import javax.swing.JMenuItem;
  31. import org.gjt.sp.jedit.*;
  32. import org.gjt.sp.jedit.options.GlobalOptions;
  33. import org.gjt.sp.jedit.msg.PositionChanging;
  34. //}}}
  35. /**
  36. * jEdit's text component.<p>
  37. *
  38. * Unlike most other text editors, the selection API permits selection and
  39. * concurrent manipulation of multiple, non-contiguous regions of text.
  40. * Methods in this class that deal with selecting text rely upon classes derived
  41. * the {@link Selection} class.
  42. *
  43. * @author Slava Pestov
  44. * @author John Gellene (API documentation)
  45. * @version $Id: JEditTextArea.java 16763 2009-12-28 16:20:33Z k_satoda $
  46. */
  47. public class JEditTextArea extends TextArea
  48. {
  49. //{{{ JEditTextArea constructor
  50. /**
  51. * Creates a new JEditTextArea.
  52. */
  53. public JEditTextArea(View view)
  54. {
  55. super(jEdit.getPropertyManager(), view);
  56. enableEvents(AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
  57. this.view = view;
  58. } //}}}
  59. //{{{ getFoldPainter() method
  60. @Override
  61. public FoldPainter getFoldPainter()
  62. {
  63. FoldPainter foldPainter = (FoldPainter) ServiceManager.getService(
  64. FOLD_PAINTER_SERVICE, getFoldPainterName());
  65. if (foldPainter == null)
  66. foldPainter = (FoldPainter) ServiceManager.getService(
  67. FOLD_PAINTER_SERVICE,
  68. DEFAULT_FOLD_PAINTER_SERVICE);
  69. return foldPainter;
  70. } //}}}
  71. // {{{ Overrides for macro recording.
  72. //{{{ home() method
  73. /**
  74. * An override to record the acutual action taken for home().
  75. */
  76. @Override
  77. public void home(boolean select)
  78. {
  79. Macros.Recorder recorder = view.getMacroRecorder();
  80. switch(getInputHandler().getLastActionCount() % 2)
  81. {
  82. case 1:
  83. if(recorder != null)
  84. recorder.record("textArea.goToStartOfWhiteSpace(" + select + ");");
  85. goToStartOfWhiteSpace(select);
  86. break;
  87. default:
  88. if(recorder != null)
  89. recorder.record("textArea.goToStartOfLine(" + select + ");");
  90. goToStartOfLine(select);
  91. break;
  92. }
  93. } //}}}
  94. //{{{ end() method
  95. /**
  96. * An override to record the acutual action taken for end().
  97. */
  98. @Override
  99. public void end(boolean select)
  100. {
  101. Macros.Recorder recorder = view.getMacroRecorder();
  102. switch(getInputHandler().getLastActionCount() % 2)
  103. {
  104. case 1:
  105. if(recorder != null)
  106. recorder.record("textArea.goToEndOfWhiteSpace(" + select + ");");
  107. goToEndOfWhiteSpace(select);
  108. break;
  109. default:
  110. if(recorder != null)
  111. recorder.record("textArea.goToEndOfLine(" + select + ");");
  112. goToEndOfLine(select);
  113. break;
  114. }
  115. } //}}}
  116. //{{{ smartHome() method
  117. /**
  118. * An override to record the acutual action taken for smartHome().
  119. */
  120. @Override
  121. public void smartHome(boolean select)
  122. {
  123. Macros.Recorder recorder = view.getMacroRecorder();
  124. switch(view.getInputHandler().getLastActionCount())
  125. {
  126. case 1:
  127. if(recorder != null)
  128. recorder.record("textArea.goToStartOfWhiteSpace(" + select + ");");
  129. goToStartOfWhiteSpace(select);
  130. break;
  131. case 2:
  132. if(recorder != null)
  133. recorder.record("textArea.goToStartOfLine(" + select + ");");
  134. goToStartOfLine(select);
  135. break;
  136. default: //case 3:
  137. if(recorder != null)
  138. recorder.record("textArea.goToFirstVisibleLine(" + select + ");");
  139. goToFirstVisibleLine(select);
  140. break;
  141. }
  142. } //}}}
  143. //{{{ smartEnd() method
  144. /**
  145. * An override to record the acutual action taken for smartHome().
  146. */
  147. @Override
  148. public void smartEnd(boolean select)
  149. {
  150. Macros.Recorder recorder = view.getMacroRecorder();
  151. switch(view.getInputHandler().getLastActionCount())
  152. {
  153. case 1:
  154. if(recorder != null)
  155. recorder.record("textArea.goToEndOfWhiteSpace(" + select + ");");
  156. goToEndOfWhiteSpace(select);
  157. break;
  158. case 2:
  159. if(recorder != null)
  160. recorder.record("textArea.goToEndOfLine(" + select + ");");
  161. goToEndOfLine(select);
  162. break;
  163. default: //case 3:
  164. if(recorder != null)
  165. recorder.record("textArea.goToLastVisibleLine(" + select + ");");
  166. goToLastVisibleLine(select);
  167. break;
  168. }
  169. } //}}}
  170. // }}}
  171. // {{{ overrides from the base class that are EditBus aware
  172. public void goToBufferEnd(boolean select)
  173. {
  174. EditBus.send(new PositionChanging(this));
  175. super.goToBufferEnd(select);
  176. }
  177. //{{{ goToMatchingBracket() method
  178. /**
  179. * Moves the caret to the bracket matching the one before the caret.
  180. * Also sends PositionChanging if it goes somewhere.
  181. * @since jEdit 4.3pre18
  182. */
  183. public void goToMatchingBracket()
  184. {
  185. if(getLineLength(caretLine) != 0)
  186. {
  187. int dot = caret - getLineStartOffset(caretLine);
  188. int bracket = TextUtilities.findMatchingBracket(
  189. buffer,caretLine,Math.max(0,dot - 1));
  190. if(bracket != -1)
  191. {
  192. EditBus.send(new PositionChanging(this));
  193. selectNone();
  194. moveCaretPosition(bracket + 1,false);
  195. return;
  196. }
  197. }
  198. getToolkit().beep();
  199. } //}}}
  200. public void goToBufferStart(boolean select)
  201. {
  202. EditBus.send(new PositionChanging(this));
  203. super.goToBufferStart(select);
  204. } // }}}
  205. // {{{ replaceSelection(String)
  206. @Override
  207. public int replaceSelection(String selectedText)
  208. {
  209. EditBus.send(new PositionChanging(this));
  210. return super.replaceSelection(selectedText);
  211. }//}}}
  212. //{{{ showGoToLineDialog() method
  213. /**
  214. * Displays the 'go to line' dialog box, and moves the caret to the
  215. * specified line number.
  216. * @since jEdit 2.7pre2
  217. */
  218. public void showGoToLineDialog()
  219. {
  220. String line = GUIUtilities.input(view,"goto-line",null);
  221. if(line == null)
  222. return;
  223. try
  224. {
  225. int lineNumber = Integer.parseInt(line) - 1;
  226. EditBus.send(new PositionChanging(this));
  227. setCaretPosition(getLineStartOffset(lineNumber));
  228. }
  229. catch(Exception e)
  230. {
  231. getToolkit().beep();
  232. }
  233. } //}}}
  234. //{{{ userInput() method
  235. /**
  236. * Handles the insertion of the specified character. It performs the
  237. * following operations in addition to TextArea#userInput(char):
  238. * <ul>
  239. * <li>Inserting a space with automatic abbrev expansion enabled will
  240. * try to expand the abbrev
  241. * </ul>
  242. *
  243. * @param ch The character
  244. * @since jEdit 2.7pre3
  245. */
  246. @Override
  247. public void userInput(char ch)
  248. {
  249. if(ch == ' ' && Abbrevs.getExpandOnInput()
  250. && Abbrevs.expandAbbrev(view,false))
  251. return;
  252. super.userInput(ch);
  253. } //}}}
  254. //{{{ addExplicitFold() method
  255. /**
  256. * Surrounds the selection with explicit fold markers.
  257. * @since jEdit 4.0pre3
  258. */
  259. @Override
  260. public void addExplicitFold()
  261. {
  262. try
  263. {
  264. super.addExplicitFold();
  265. }
  266. catch (TextAreaException e)
  267. {
  268. GUIUtilities.error(view,"folding-not-explicit",null);
  269. }
  270. } //}}}
  271. //{{{ formatParagraph() method
  272. /**
  273. * Formats the paragraph containing the caret.
  274. * @since jEdit 2.7pre2
  275. */
  276. @Override
  277. public void formatParagraph()
  278. {
  279. try
  280. {
  281. super.formatParagraph();
  282. }
  283. catch (TextAreaException e)
  284. {
  285. GUIUtilities.error(view,"format-maxlinelen",null);
  286. }
  287. } //}}}
  288. //{{{ doWordCount() method
  289. protected static void doWordCount(View view, String text)
  290. {
  291. char[] chars = text.toCharArray();
  292. int characters = chars.length;
  293. int words = 0;
  294. int lines = 1;
  295. boolean word = true;
  296. for(int i = 0; i < chars.length; i++)
  297. {
  298. switch(chars[i])
  299. {
  300. case '\r': case '\n':
  301. lines++;
  302. case ' ': case '\t':
  303. word = true;
  304. break;
  305. default:
  306. if(word)
  307. {
  308. words++;
  309. word = false;
  310. }
  311. break;
  312. }
  313. }
  314. Object[] args = { characters, words, lines };
  315. GUIUtilities.message(view,"wordcount",args);
  316. } //}}}
  317. //{{{ showWordCountDialog() method
  318. /**
  319. * Displays the 'word count' dialog box.
  320. * @since jEdit 2.7pre2
  321. */
  322. public void showWordCountDialog()
  323. {
  324. String selection = getSelectedText();
  325. if(selection != null)
  326. {
  327. doWordCount(view,selection);
  328. return;
  329. }
  330. doWordCount(view,buffer.getText(0,buffer.getLength()));
  331. } //}}}
  332. //{{{ Getters and setters
  333. //{{{ getView() method
  334. /**
  335. * Returns this text area's view.
  336. * @since jEdit 4.2pre5
  337. */
  338. public View getView()
  339. {
  340. return view;
  341. } //}}}
  342. //}}}
  343. //{{{ Deprecated methods
  344. //{{{ getSelectionStart() method
  345. /**
  346. * @deprecated Instead, obtain a Selection instance using
  347. * any means, and call its <code>getStart()</code> method
  348. */
  349. @Deprecated
  350. public final int getSelectionStart()
  351. {
  352. if(getSelectionCount() != 1)
  353. return caret;
  354. return getSelection(0).getStart();
  355. } //}}}
  356. //{{{ getSelectionStart() method
  357. /**
  358. * @deprecated Instead, obtain a Selection instance using
  359. * any means, and call its <code>getStart(int)</code> method
  360. */
  361. @Deprecated
  362. public int getSelectionStart(int line)
  363. {
  364. if(getSelectionCount() != 1)
  365. return caret;
  366. return getSelection(0).getStart(buffer,line);
  367. } //}}}
  368. //{{{ getSelectionStartLine() method
  369. /**
  370. * @deprecated Instead, obtain a Selection instance using
  371. * any means, and call its <code>getStartLine()</code> method
  372. */
  373. @Deprecated
  374. public final int getSelectionStartLine()
  375. {
  376. if(getSelectionCount() != 1)
  377. return caret;
  378. return getSelection(0).getStartLine();
  379. } //}}}
  380. //{{{ setSelectionStart() method
  381. /**
  382. * @deprecated Do not use.
  383. */
  384. @Deprecated
  385. public final void setSelectionStart(int selectionStart)
  386. {
  387. int selectionEnd = getSelectionCount() == 1 ? getSelection(0).getEnd() : caret;
  388. setSelection(new Selection.Range(selectionStart, selectionEnd));
  389. moveCaretPosition(selectionEnd,true);
  390. } //}}}
  391. //{{{ getSelectionEnd() method
  392. /**
  393. * @deprecated Instead, obtain a Selection instance using
  394. * any means, and call its <code>getEnd()</code> method
  395. */
  396. @Deprecated
  397. public final int getSelectionEnd()
  398. {
  399. return getSelectionCount() == 1 ? getSelection(0).getEnd() : caret;
  400. } //}}}
  401. //{{{ getSelectionEnd() method
  402. /**
  403. * @deprecated Instead, obtain a Selection instance using
  404. * any means, and call its <code>getEnd(int)</code> method
  405. */
  406. @Deprecated
  407. public int getSelectionEnd(int line)
  408. {
  409. if(getSelectionCount() != 1)
  410. return caret;
  411. return getSelection(0).getEnd(buffer,line);
  412. } //}}}
  413. //{{{ getSelectionEndLine() method
  414. /**
  415. * @deprecated Instead, obtain a Selection instance using
  416. * any means, and call its <code>getEndLine()</code> method
  417. */
  418. @Deprecated
  419. public final int getSelectionEndLine()
  420. {
  421. if(getSelectionCount() != 1)
  422. return caret;
  423. return getSelection(0).getEndLine();
  424. } //}}}
  425. //{{{ setSelectionEnd() method
  426. /**
  427. * @deprecated Do not use.
  428. */
  429. @Deprecated
  430. public final void setSelectionEnd(int selectionEnd)
  431. {
  432. int selectionStart = getSelectionCount() == 1 ? getSelection(0).getStart() : caret;
  433. setSelection(new Selection.Range(selectionStart, selectionEnd));
  434. moveCaretPosition(selectionEnd,true);
  435. } //}}}
  436. //{{{ select() method
  437. /**
  438. * @deprecated Instead, call either <code>addToSelection()</code>,
  439. * or <code>setSelection()</code> with a new Selection instance.
  440. */
  441. @Deprecated
  442. public void select(int start, int end)
  443. {
  444. setSelection(new Selection.Range(start, end));
  445. moveCaretPosition(end,true);
  446. } //}}}
  447. //{{{ select() method
  448. /**
  449. * @deprecated Instead, call either <code>addToSelection()</code>,
  450. * or <code>setSelection()</code> with a new Selection instance.
  451. */
  452. @Deprecated
  453. public void select(int start, int end, boolean doElectricScroll)
  454. {
  455. selectNone();
  456. int newStart, newEnd;
  457. if(start < end)
  458. {
  459. newStart = start;
  460. newEnd = end;
  461. }
  462. else
  463. {
  464. newStart = end;
  465. newEnd = start;
  466. }
  467. setSelection(new Selection.Range(newStart,newEnd));
  468. moveCaretPosition(end,doElectricScroll);
  469. } //}}}
  470. //{{{ isSelectionRectangular() method
  471. /**
  472. * @deprecated Instead, check if the appropriate Selection
  473. * is an instance of the Selection.Rect class.
  474. */
  475. @Deprecated
  476. public boolean isSelectionRectangular()
  477. {
  478. Selection s = getSelectionAtOffset(caret);
  479. return s != null && s instanceof Selection.Rect;
  480. } //}}}
  481. //}}}
  482. //{{{ Private members
  483. //{{{ Instance variables
  484. private View view;
  485. //}}}
  486. //}}}
  487. //{{{ Fold painters
  488. /**
  489. * Fold painter service.
  490. * @since jEdit 4.3pre16
  491. */
  492. public static final String FOLD_PAINTER_PROPERTY = "foldPainter";
  493. public static final String FOLD_PAINTER_SERVICE = "org.gjt.sp.jedit.textarea.FoldPainter";
  494. public static final String DEFAULT_FOLD_PAINTER_SERVICE = "Triangle";
  495. //{{{ getFoldPainterService() method
  496. public static String getFoldPainterName()
  497. {
  498. return jEdit.getProperty(FOLD_PAINTER_PROPERTY, DEFAULT_FOLD_PAINTER_SERVICE);
  499. } //}}}
  500. //}}} Fold painters
  501. //{{{ handlePopupTrigger() method
  502. /**
  503. * Do the same thing as right-clicking on the text area. The Gestures
  504. * plugin uses this API.
  505. * @since jEdit 4.2pre13
  506. */
  507. @Override
  508. public void handlePopupTrigger(MouseEvent evt)
  509. {
  510. if(popup.isVisible())
  511. popup.setVisible(false);
  512. else
  513. {
  514. // Rebuild popup menu every time the menu is requested.
  515. createPopupMenu(evt);
  516. int x = evt.getX();
  517. int y = evt.getY();
  518. int dragStart = xyToOffset(x,y,
  519. !(painter.isBlockCaretEnabled()
  520. || isOverwriteEnabled()));
  521. if(getSelectionCount() == 0 || multi)
  522. moveCaretPosition(dragStart,false);
  523. GUIUtilities.showPopupMenu(popup,painter,x,y);
  524. }
  525. } //}}}
  526. //{{{ createPopupMenu() method
  527. /**
  528. * Creates the popup menu.
  529. * @since 4.3pre15
  530. */
  531. @Override
  532. public void createPopupMenu(MouseEvent evt)
  533. {
  534. popup = GUIUtilities.loadPopupMenu("view.context", this, evt);
  535. JMenuItem customize = new JMenuItem(jEdit.getProperty(
  536. "view.context.customize"));
  537. customize.addActionListener(new ActionListener()
  538. {
  539. public void actionPerformed(ActionEvent evt)
  540. {
  541. new GlobalOptions(view,"context");
  542. }
  543. });
  544. popup.addSeparator();
  545. popup.add(customize);
  546. } //}}}
  547. //{{{ showPopupMenu() method
  548. /**
  549. * Shows the popup menu below the current caret position.
  550. * @since 4.3pre10
  551. */
  552. @Override
  553. public void showPopupMenu()
  554. {
  555. if (!popup.isVisible() && hasFocus())
  556. {
  557. Point caretPos = offsetToXY(getCaretPosition());
  558. if (caretPos != null)
  559. {
  560. // Open the context menu below the caret
  561. int charHeight = getPainter().getFontMetrics().getHeight();
  562. GUIUtilities.showPopupMenu(popup,
  563. painter,caretPos.x,caretPos.y + charHeight,true);
  564. }
  565. }
  566. } //}}}
  567. }