PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 1307 lines | 867 code | 138 blank | 302 comment | 220 complexity | c41a517d38ff4dd3594122133397edeb 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. * EditPane.java - Text area and buffer switcher
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2000, 2005 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit;
  23. //{{{ Imports
  24. import java.awt.BorderLayout;
  25. import java.awt.Color;
  26. import java.awt.Dimension;
  27. import java.awt.Graphics2D;
  28. import java.util.HashMap;
  29. import java.util.Map;
  30. import javax.swing.JPanel;
  31. import javax.swing.SwingUtilities;
  32. import org.gjt.sp.jedit.EditBus.EBHandler;
  33. import org.gjt.sp.jedit.buffer.JEditBuffer;
  34. import org.gjt.sp.jedit.bufferset.BufferSet;
  35. import org.gjt.sp.jedit.bufferset.BufferSetListener;
  36. import org.gjt.sp.jedit.gui.BufferSwitcher;
  37. import org.gjt.sp.jedit.gui.StatusBar;
  38. import org.gjt.sp.jedit.io.VFSManager;
  39. import org.gjt.sp.jedit.msg.BufferChanging;
  40. import org.gjt.sp.jedit.msg.BufferUpdate;
  41. import org.gjt.sp.jedit.msg.EditPaneUpdate;
  42. import org.gjt.sp.jedit.msg.PropertiesChanged;
  43. import org.gjt.sp.jedit.options.GutterOptionPane;
  44. import org.gjt.sp.jedit.syntax.SyntaxStyle;
  45. import org.gjt.sp.jedit.textarea.AntiAlias;
  46. import org.gjt.sp.jedit.textarea.Gutter;
  47. import org.gjt.sp.jedit.textarea.GutterPopupHandler;
  48. import org.gjt.sp.jedit.textarea.JEditTextArea;
  49. import org.gjt.sp.jedit.textarea.MouseHandler;
  50. import org.gjt.sp.jedit.textarea.Selection;
  51. import org.gjt.sp.jedit.textarea.StatusListener;
  52. import org.gjt.sp.jedit.textarea.TextArea;
  53. import org.gjt.sp.jedit.textarea.TextAreaExtension;
  54. import org.gjt.sp.jedit.textarea.TextAreaPainter;
  55. import org.gjt.sp.jedit.textarea.TextAreaTransferHandler;
  56. import org.gjt.sp.util.SyntaxUtilities;
  57. import org.gjt.sp.util.ThreadUtilities;
  58. //}}}
  59. /**
  60. * A panel containing a text area.<p>
  61. *
  62. * In a BeanShell script, you can obtain the current edit pane from the
  63. * <code>editPane</code> variable.<p>
  64. *
  65. *
  66. * Each View can have multiple editPanes, one is active at a time.
  67. * Each EditPane has a single JEditTextArea, and is operating on single buffer.
  68. * The EditPane also can switch buffers.
  69. *
  70. * This is the "controller" between a JEditTextArea (view) and a buffer (model).
  71. *
  72. * This class does not have a public constructor.
  73. * Edit panes can be created and destroyed using methods in the
  74. * {@link View} class.<p>
  75. *
  76. *
  77. * @see View#splitHorizontally()
  78. * @see View#splitVertically()
  79. * @see View#unsplitCurrent()
  80. * @see View#unsplit()
  81. * @see View#getEditPane()
  82. * @see View#getEditPanes()
  83. *
  84. * @author Slava Pestov
  85. * @version $Id: EditPane.java 19592 2011-06-17 16:18:59Z kpouer $
  86. */
  87. public class EditPane extends JPanel implements BufferSetListener
  88. {
  89. //{{{ getView() method
  90. /**
  91. * Returns the view containing this edit pane.
  92. * @return the view that contains this EditPane
  93. * @since jEdit 2.5pre2
  94. */
  95. public View getView()
  96. {
  97. return view;
  98. } //}}}
  99. // {{{ get(TextArea) method
  100. /**
  101. * Returns the EditPane of a TextArea.
  102. *
  103. * @param ta the textArea
  104. * @return the EditPane containing the TextArea.
  105. */
  106. public static EditPane get(TextArea ta)
  107. {
  108. if (ta == null) return null;
  109. return (EditPane)SwingUtilities.getAncestorOfClass(EditPane.class, ta);
  110. } // }}}
  111. //{{{ getBuffer() method
  112. /**
  113. * Returns the current buffer.
  114. * @return the current buffer
  115. * @since jEdit 2.5pre2
  116. */
  117. public Buffer getBuffer()
  118. {
  119. return buffer;
  120. } //}}}
  121. //{{{ setBuffer() methods
  122. /**
  123. * Sets the current buffer.
  124. * @param buffer The buffer to edit.
  125. * @since jEdit 2.5pre2
  126. */
  127. public void setBuffer(Buffer buffer)
  128. {
  129. setBuffer(buffer, true);
  130. }
  131. /**
  132. * Sets the current buffer.
  133. * @param buffer The buffer to edit.
  134. * @param requestFocus true if the textarea should request focus, false otherwise
  135. * @since jEdit 4.3pre6
  136. */
  137. public void setBuffer(final Buffer buffer, boolean requestFocus)
  138. {
  139. if(buffer == null)
  140. throw new NullPointerException();
  141. if(this.buffer == buffer)
  142. return;
  143. if (bufferSet.indexOf(buffer) == -1)
  144. {
  145. jEdit.getBufferSetManager().addBuffer(this, buffer);
  146. }
  147. //if(buffer.insideCompoundEdit())
  148. // buffer.endCompoundEdit();
  149. EditBus.send(new BufferChanging(this, buffer));
  150. if (bufferSet.indexOf(this.buffer) != -1)
  151. {
  152. // when closing the last buffer of a bufferSet, the current buffer will still be the closed
  153. // buffer until a new empty buffer is created.
  154. // So if the current buffer is not anymore in the bufferSet, do not set the recentBuffer
  155. recentBuffer = this.buffer;
  156. }
  157. if(recentBuffer != null)
  158. saveCaretInfo();
  159. this.buffer = buffer;
  160. textArea.setBuffer(buffer);
  161. if(!init)
  162. {
  163. view.updateTitle();
  164. if(bufferSwitcher != null)
  165. {
  166. if(bufferSwitcher.getSelectedItem() != buffer)
  167. bufferSwitcher.setSelectedItem(buffer);
  168. bufferSwitcher.setToolTipText(buffer.getPath());
  169. }
  170. EditBus.send(new EditPaneUpdate(this,EditPaneUpdate
  171. .BUFFER_CHANGED));
  172. }
  173. if (requestFocus)
  174. {
  175. SwingUtilities.invokeLater(new Runnable()
  176. {
  177. public void run()
  178. {
  179. // only do this if we are the current edit pane
  180. if(view.getEditPane() == EditPane.this
  181. && (bufferSwitcher == null
  182. || !bufferSwitcher.isPopupVisible()))
  183. {
  184. textArea.requestFocus();
  185. }
  186. }
  187. });
  188. }
  189. // If the buffer is loading, the caret info will be loaded on
  190. // BufferUpdate.LOADED. Otherwise, we don't need to wait for IO.
  191. if (!buffer.isLoading())
  192. {
  193. ThreadUtilities.runInDispatchThread(new Runnable()
  194. {
  195. public void run()
  196. {
  197. // avoid a race condition
  198. // see bug #834338
  199. if(buffer == getBuffer())
  200. loadCaretInfo();
  201. }
  202. });
  203. }
  204. } //}}}
  205. //{{{ prevBuffer() method
  206. /**
  207. * Selects the previous buffer.
  208. * @since jEdit 2.7pre2
  209. */
  210. public void prevBuffer()
  211. {
  212. Buffer buffer = bufferSet.getPreviousBuffer(bufferSet.indexOf(this.buffer));
  213. setBuffer(buffer);
  214. } //}}}
  215. //{{{ nextBuffer() method
  216. /**
  217. * Selects the next buffer.
  218. * @since jEdit 2.7pre2
  219. */
  220. public void nextBuffer()
  221. {
  222. Buffer buffer = bufferSet.getNextBuffer(bufferSet.indexOf(this.buffer));
  223. setBuffer(buffer);
  224. } //}}}
  225. //{{{ recentBuffer() method
  226. /**
  227. * Selects the most recently edited buffer.
  228. * @since jEdit 2.7pre2
  229. */
  230. public void recentBuffer()
  231. {
  232. if(recentBuffer != null)
  233. setBuffer(recentBuffer);
  234. else
  235. getToolkit().beep();
  236. } //}}}
  237. //{{{ focusOnTextArea() method
  238. /**
  239. * Sets the focus onto the text area.
  240. * @since jEdit 2.5pre2
  241. */
  242. public void focusOnTextArea()
  243. {
  244. SwingUtilities.invokeLater(new Runnable()
  245. {
  246. public void run()
  247. {
  248. textArea.requestFocus();
  249. }
  250. });
  251. } //}}}
  252. //{{{ getTextArea() method
  253. /**
  254. * Returns the view's text area.
  255. * @return the text area of the edit pane
  256. * @since jEdit 2.5pre2
  257. */
  258. public JEditTextArea getTextArea()
  259. {
  260. return textArea;
  261. } //}}}
  262. //{{{ getBufferSwitcher() method
  263. /**
  264. * Returns the buffer switcher combo box instance.
  265. * @return the buffer switcher (it can be null)
  266. * @since jEdit 4.1pre8
  267. */
  268. public BufferSwitcher getBufferSwitcher()
  269. {
  270. return bufferSwitcher;
  271. } //}}}
  272. //{{{ focusBufferSwitcher() method
  273. /**
  274. * Pops up and focuses on the buffer switcher combo box.
  275. * @since jEdit 4.3pre18
  276. * (previously known as showBufferSwitcher)
  277. */
  278. public void focusBufferSwitcher()
  279. {
  280. if(bufferSwitcher == null)
  281. getToolkit().beep();
  282. else
  283. {
  284. SwingUtilities.invokeLater(new Runnable()
  285. {
  286. public void run()
  287. {
  288. bufferSwitcher.requestFocus();
  289. bufferSwitcher.showPopup();
  290. }
  291. });
  292. }
  293. } //}}}
  294. //{{{ saveCaretInfo() method
  295. /**
  296. * Saves the caret information to the current buffer.
  297. * @since jEdit 2.5pre2
  298. */
  299. public void saveCaretInfo()
  300. {
  301. if(!buffer.isLoaded())
  302. return;
  303. buffer.setIntegerProperty(Buffer.CARET,
  304. textArea.getCaretPosition());
  305. CaretInfo caretInfo = caretsForPath.get(buffer.getPath());
  306. if (caretInfo == null)
  307. {
  308. caretInfo = new CaretInfo();
  309. caretsForPath.put(buffer.getPath(), caretInfo);
  310. }
  311. caretInfo.caret = textArea.getCaretPosition();
  312. Selection[] selection = textArea.getSelection();
  313. for(int i = 0; i < selection.length; i++)
  314. selection[i] = (Selection)selection[i].clone();
  315. buffer.setProperty(Buffer.SELECTION,selection);
  316. caretInfo.selection = selection;
  317. caretInfo.rectangularSelection = textArea.isRectangularSelectionEnabled();
  318. caretInfo.multipleSelection = textArea.isMultipleSelectionEnabled();
  319. buffer.setIntegerProperty(Buffer.SCROLL_VERT,
  320. textArea.getFirstPhysicalLine());
  321. caretInfo.scrollVert = textArea.getFirstPhysicalLine();
  322. buffer.setIntegerProperty(Buffer.SCROLL_HORIZ,
  323. textArea.getHorizontalOffset());
  324. caretInfo.scrollHoriz = textArea.getHorizontalOffset();
  325. if (!buffer.isUntitled())
  326. {
  327. BufferHistory.setEntry(buffer.getPath(), textArea.getCaretPosition(),
  328. (Selection[])buffer.getProperty(Buffer.SELECTION),
  329. buffer.getStringProperty(JEditBuffer.ENCODING),
  330. buffer.getMode().getName());
  331. }
  332. } //}}}
  333. //{{{ loadCaretInfo() method
  334. /**
  335. * Loads the caret and selection information from this EditPane, fall
  336. * back to the information from the current buffer if none is already
  337. * in this EditPane.
  338. * @since jEdit 2.5pre2
  339. */
  340. public void loadCaretInfo()
  341. {
  342. // get our internal map of buffer -> CaretInfo since there might
  343. // be current info already
  344. CaretInfo caretInfo = caretsForPath.get(buffer.getPath());
  345. if (caretInfo == null)
  346. {
  347. caretInfo = new CaretInfo();
  348. }
  349. // set the position of the caret itself.
  350. // Caret position could be stored in the internal map already,
  351. // if so, use that one first. Otherwise, fall back to any
  352. // previously saved caret position that was stored in the
  353. // buffer properties.
  354. int caret = caretInfo.caret;
  355. if (caret == -1 || buffer.getBooleanProperty(Buffer.CARET_POSITIONED))
  356. {
  357. Integer i = (Integer) buffer.getProperty(Buffer.CARET);
  358. caret = i == null ? -1 : i;
  359. }
  360. buffer.unsetProperty(Buffer.CARET_POSITIONED);
  361. if(caret != -1)
  362. textArea.setCaretPosition(Math.min(caret,
  363. buffer.getLength()));
  364. // set any selections
  365. Selection[] selection = caretInfo.selection;
  366. if ( selection == null )
  367. {
  368. selection = (Selection[]) buffer.getProperty(Buffer.SELECTION);
  369. }
  370. if(selection != null)
  371. {
  372. for(int i = 0; i < selection.length; i++)
  373. {
  374. Selection s = selection[i];
  375. int max = buffer.getLength();
  376. if(s.getStart() > max || s.getEnd() > max)
  377. selection[i] = null;
  378. }
  379. }
  380. textArea.setSelection(selection);
  381. textArea.setRectangularSelectionEnabled(caretInfo.rectangularSelection);
  382. textArea.setMultipleSelectionEnabled(caretInfo.multipleSelection);
  383. // set firstLine value
  384. int firstLine = caretInfo.scrollVert;
  385. if ( firstLine == -1 )
  386. {
  387. Integer i = (Integer) buffer.getProperty(Buffer.SCROLL_VERT);
  388. firstLine = i == null ? -1 : i;
  389. }
  390. if(firstLine != -1)
  391. textArea.setFirstPhysicalLine(firstLine);
  392. // set horizontal offset
  393. int horizontalOffset = caretInfo.scrollHoriz;
  394. if (horizontalOffset == -1)
  395. {
  396. Integer i = (Integer) buffer.getProperty(Buffer.SCROLL_HORIZ);
  397. horizontalOffset = i == null ? -1 : i;
  398. }
  399. if(horizontalOffset != -1)
  400. textArea.setHorizontalOffset(horizontalOffset);
  401. /* Silly bug workaround #8694. If you look at the above code,
  402. * note that we restore the saved caret position first, then
  403. * scroll to the saved location. However, the caret changing
  404. * can itself result in scrolling to a different location than
  405. * what was saved; and since moveCaretPosition() calls
  406. * updateBracketHighlight(), the bracket highlight's out of
  407. * bounds calculation will rely on a different set of physical
  408. * first/last lines than what we will end up with eventually.
  409. * Instead of confusing the user with status messages that
  410. * appear at random when switching buffers, we simply hide the
  411. * message altogether. */
  412. view.getStatus().setMessage(null);
  413. } //}}}
  414. //{{{ bufferRenamed() method
  415. /**
  416. * This method should be called by the Buffer when the path is changing.
  417. * @param oldPath the old path of the buffer
  418. * @param newPath the new path of the buffer
  419. */
  420. void bufferRenamed(String oldPath, String newPath)
  421. {
  422. CaretInfo caretInfo = caretsForPath.remove(oldPath);
  423. if (caretInfo != null)
  424. caretsForPath.put(newPath, caretInfo);
  425. } //}}}
  426. //{{{ CaretInfo class
  427. /**
  428. * Need to track this info for each buffer that this EditPane might edit
  429. * since a buffer may be open in more than one EditPane at a time. That
  430. * means we need to track this info at the EditPane level rather than
  431. * the buffer level.
  432. */
  433. private static class CaretInfo
  434. {
  435. public int caret = -1;
  436. public Selection[] selection;
  437. public int scrollVert = -1;
  438. public int scrollHoriz = -1;
  439. public boolean rectangularSelection;
  440. public boolean multipleSelection;
  441. } //}}}
  442. //{{{ goToNextMarker() method
  443. /**
  444. * Moves the caret to the next marker.
  445. * @since jEdit 4.3pre3
  446. */
  447. public void goToNextMarker(boolean select)
  448. {
  449. java.util.List<Marker> markers = buffer.getMarkers();
  450. if(markers.isEmpty())
  451. {
  452. getToolkit().beep();
  453. return;
  454. }
  455. Marker marker = null;
  456. int caret = textArea.getCaretPosition();
  457. for(int i = 0; i < markers.size(); i++)
  458. {
  459. Marker _marker = markers.get(i);
  460. if(_marker.getPosition() > caret)
  461. {
  462. marker = _marker;
  463. break;
  464. }
  465. }
  466. // the markers list is not empty at this point
  467. if(marker == null)
  468. marker = markers.get(0);
  469. if(select)
  470. textArea.extendSelection(caret,marker.getPosition());
  471. else if(!textArea.isMultipleSelectionEnabled())
  472. textArea.selectNone();
  473. textArea.moveCaretPosition(marker.getPosition());
  474. } //}}}
  475. //{{{ goToPrevMarker() method
  476. /**
  477. * Moves the caret to the previous marker.
  478. * @since jEdit 2.7pre2
  479. */
  480. public void goToPrevMarker(boolean select)
  481. {
  482. java.util.List<Marker> markers = buffer.getMarkers();
  483. if(markers.isEmpty())
  484. {
  485. getToolkit().beep();
  486. return;
  487. }
  488. int caret = textArea.getCaretPosition();
  489. Marker marker = null;
  490. for(int i = markers.size() - 1; i >= 0; i--)
  491. {
  492. Marker _marker = markers.get(i);
  493. if(_marker.getPosition() < caret)
  494. {
  495. marker = _marker;
  496. break;
  497. }
  498. }
  499. if(marker == null)
  500. marker = markers.get(markers.size() - 1);
  501. if(select)
  502. textArea.extendSelection(caret,marker.getPosition());
  503. else if(!textArea.isMultipleSelectionEnabled())
  504. textArea.selectNone();
  505. textArea.moveCaretPosition(marker.getPosition());
  506. } //}}}
  507. //{{{ goToMarker() method
  508. /**
  509. * Moves the caret to the marker with the specified shortcut.
  510. * @param shortcut The shortcut
  511. * @param select True if the selection should be extended,
  512. * false otherwise
  513. * @since jEdit 3.2pre2
  514. */
  515. public void goToMarker(char shortcut, boolean select)
  516. {
  517. Marker marker = buffer.getMarker(shortcut);
  518. if(marker == null)
  519. {
  520. getToolkit().beep();
  521. return;
  522. }
  523. int pos = marker.getPosition();
  524. if(select)
  525. textArea.extendSelection(textArea.getCaretPosition(),pos);
  526. else if(!textArea.isMultipleSelectionEnabled())
  527. textArea.selectNone();
  528. textArea.moveCaretPosition(pos);
  529. } //}}}
  530. //{{{ addMarker() method
  531. /**
  532. * Adds a marker at the caret position.
  533. * @since jEdit 3.2pre1
  534. */
  535. public void addMarker()
  536. {
  537. int caretLine = textArea.getCaretLine();
  538. // always add markers on selected lines
  539. Selection[] selection = textArea.getSelection();
  540. for(int i = 0; i < selection.length; i++)
  541. {
  542. Selection s = selection[i];
  543. int startLine = s.getStartLine();
  544. if(startLine != s.getEndLine() && startLine != caretLine)
  545. {
  546. buffer.addMarker('\0',s.getStart());
  547. }
  548. if(s.getEndLine() != caretLine)
  549. buffer.addMarker('\0',s.getEnd());
  550. }
  551. // toggle marker on caret line
  552. buffer.addOrRemoveMarker('\0',textArea.getCaretPosition());
  553. } //}}}
  554. //{{{ swapMarkerAndCaret() method
  555. /**
  556. * Moves the caret to the marker with the specified shortcut,
  557. * then sets the marker position to the former caret position.
  558. * @param shortcut The shortcut
  559. * @since jEdit 3.2pre2
  560. */
  561. public void swapMarkerAndCaret(char shortcut)
  562. {
  563. Marker marker = buffer.getMarker(shortcut);
  564. if(marker == null)
  565. {
  566. getToolkit().beep();
  567. return;
  568. }
  569. int caret = textArea.getCaretPosition();
  570. textArea.setCaretPosition(marker.getPosition());
  571. buffer.addMarker(shortcut,caret);
  572. } //}}}
  573. //{{{ handlePropertiesChanged() method
  574. @EBHandler
  575. public void handlePropertiesChanged(PropertiesChanged msg)
  576. {
  577. propertiesChanged();
  578. loadBufferSwitcher();
  579. } //}}}
  580. //{{{ getMinimumSize() method
  581. /**
  582. * Returns 0,0 for split pane compatibility.
  583. */
  584. @Override
  585. public final Dimension getMinimumSize()
  586. {
  587. return new Dimension(0,0);
  588. } //}}}
  589. //{{{ getBufferSet() method
  590. /**
  591. * Returns the current buffer set.
  592. * This can be changed by setBufferSetScope().
  593. * @return the buffer set which is currently used by this EditPane
  594. * @since jEdit 4.3pre17
  595. */
  596. public BufferSet getBufferSet()
  597. {
  598. return bufferSet;
  599. } //}}}
  600. //{{{ bufferAdded() method
  601. /**
  602. * A buffer was added in the bufferSet.
  603. * @param buffer the added buffer
  604. * @param index the position where it was added
  605. * @since jEdit 4.3pre15
  606. */
  607. public void bufferAdded(Buffer buffer, int index)
  608. {
  609. if (buffer == null)
  610. return;
  611. if (bufferSwitcher != null)
  612. bufferSwitcher.updateBufferList();
  613. if (bufferSet.indexOf(this.buffer) == -1)
  614. {
  615. // it happens when having 1 untitled buffer if I open a file. The untitled buffer
  616. // is closed but the new buffer is not yet opened
  617. setBuffer(buffer);
  618. }
  619. } //}}}
  620. //{{{ bufferRemoved() method
  621. /**
  622. * A buffer was removed from the bufferSet.
  623. * @param buffer the removed buffer
  624. * @param index the position where it was before being removed
  625. * @since jEdit 4.3pre15
  626. */
  627. public void bufferRemoved(Buffer buffer, int index)
  628. {
  629. if (buffer.isUntitled())
  630. {
  631. // the buffer was a new file so I do not need to keep it's informations
  632. caretsForPath.remove(buffer.getPath());
  633. }
  634. if (buffer == this.buffer)
  635. {
  636. // The closed buffer is the current buffer
  637. Buffer newBuffer = recentBuffer != null ?
  638. recentBuffer : bufferSet.getPreviousBuffer(index);
  639. if(newBuffer != null && !newBuffer.isClosed())
  640. {
  641. setBuffer(newBuffer);
  642. if (bufferSet.size() > 1)
  643. {
  644. recentBuffer = bufferSet.getPreviousBuffer(index -1);
  645. }
  646. }
  647. else if(bufferSet.size() != 0)
  648. {
  649. setBuffer(bufferSet.getBuffer(0));
  650. recentBuffer = null;
  651. }
  652. }
  653. if(buffer == recentBuffer)
  654. recentBuffer = null;
  655. if (bufferSwitcher != null)
  656. bufferSwitcher.updateBufferList();
  657. } //}}}
  658. //{{{ bufferMoved() method
  659. /**
  660. * A buffer was moved in the BufferSet.
  661. * @param buffer the moved buffer
  662. * @param oldIndex the position it was before
  663. * @param newIndex the new position
  664. * @since jEdit 4.3pre15
  665. */
  666. public void bufferMoved(Buffer buffer, int oldIndex, int newIndex)
  667. {
  668. if (bufferSwitcher != null)
  669. bufferSwitcher.updateBufferList();
  670. } //}}}
  671. //{{{ bufferSetSorted() method
  672. /**
  673. * The bufferSet was sorted
  674. * @since jEdit 4.3pre16
  675. */
  676. public void bufferSetSorted()
  677. {
  678. if (bufferSwitcher != null)
  679. bufferSwitcher.updateBufferList();
  680. } //}}}
  681. //{{{ toString() method
  682. @Override
  683. public String toString()
  684. {
  685. return getClass().getName() + '['
  686. + (view.getEditPane() == this
  687. ? "active]" : "inactive]");
  688. } //}}}
  689. //{{{ Package-private members
  690. //{{{ EditPane constructor
  691. EditPane(View view, BufferSet bufferSetSource, Buffer buffer)
  692. {
  693. super(new BorderLayout());
  694. BufferSet.Scope scope = jEdit.getBufferSetManager().getScope();
  695. BufferSet source = bufferSetSource;
  696. switch (scope)
  697. {
  698. case editpane:
  699. // do nothing
  700. break;
  701. case view:
  702. {
  703. EditPane editPane = view.getEditPane();
  704. if (editPane != null)
  705. {
  706. // if we have an editpane we copy it
  707. source = editPane.getBufferSet();
  708. }
  709. }
  710. break;
  711. case global:
  712. View activeView = jEdit.getActiveView();
  713. if (activeView != null)
  714. {
  715. EditPane editPane = activeView.getEditPane();
  716. if (editPane != null)
  717. {
  718. source = editPane.getBufferSet();
  719. }
  720. }
  721. break;
  722. }
  723. bufferSet = new BufferSet(source);
  724. init = true;
  725. this.view = view;
  726. textArea = new JEditTextArea(view);
  727. bufferSet.addBufferSetListener(this);
  728. textArea.getPainter().setAntiAlias(new AntiAlias(jEdit.getProperty("view.antiAlias")));
  729. textArea.setMouseHandler(new MouseHandler(textArea));
  730. textArea.setTransferHandler(new TextAreaTransferHandler());
  731. markerHighlight = new MarkerHighlight();
  732. Gutter gutter = textArea.getGutter();
  733. gutter.setGutterEnabled(GutterOptionPane.isGutterEnabled());
  734. gutter.setMinLineNumberDigitCount(GutterOptionPane.getMinLineNumberDigits());
  735. gutter.setSelectionAreaEnabled(GutterOptionPane.isSelectionAreaEnabled());
  736. gutter.addExtension(markerHighlight);
  737. gutter.setSelectionPopupHandler(
  738. new GutterPopupHandler()
  739. {
  740. public void handlePopup(int x, int y, int line)
  741. {
  742. Buffer buffer = getBuffer();
  743. buffer.addOrRemoveMarker('\0',
  744. buffer.getLineStartOffset(line));
  745. }
  746. });
  747. textArea.addStatusListener(new StatusHandler());
  748. add(BorderLayout.CENTER,textArea);
  749. propertiesChanged();
  750. setBuffer(buffer);
  751. // need to add the buffer to the bufferSet.
  752. // It may not have been done by the setBuffer() because the EditPane is not yet known by jEdit, and for
  753. // view and global scope it is added through this list
  754. if (bufferSet.indexOf(buffer) == -1)
  755. bufferSet.addBuffer(buffer);
  756. loadBufferSwitcher();
  757. init = false;
  758. EditBus.addToBus(this);
  759. } //}}}
  760. //{{{ close() method
  761. void close()
  762. {
  763. saveCaretInfo();
  764. EditBus.send(new EditPaneUpdate(this,EditPaneUpdate.DESTROYED));
  765. EditBus.removeFromBus(this);
  766. textArea.dispose();
  767. } //}}}
  768. //}}}
  769. //{{{ Private members
  770. //{{{ Instance variables
  771. private boolean init;
  772. /** The View where the edit pane is. */
  773. private final View view;
  774. private final BufferSet bufferSet;
  775. /** The current buffer. */
  776. private Buffer buffer;
  777. private Buffer recentBuffer;
  778. private BufferSwitcher bufferSwitcher;
  779. /** The textArea inside the edit pane. */
  780. private final JEditTextArea textArea;
  781. private final MarkerHighlight markerHighlight;
  782. // A map of buffer.getPath() -> CaretInfo. This is necessary for
  783. // when the same buffer is open in more than one EditPane and the user
  784. // is switching between buffers. We want to keep the caret in the
  785. // right position in each EditPane, which won't be the case if we
  786. // just use the buffer caret property.
  787. private final Map<String, CaretInfo> caretsForPath = new HashMap<String, CaretInfo>();
  788. //}}}
  789. //{{{ propertiesChanged() method
  790. private void propertiesChanged()
  791. {
  792. TextAreaPainter painter = textArea.getPainter();
  793. initPainter(painter);
  794. Gutter gutter = textArea.getGutter();
  795. gutter.setExpanded(jEdit.getBooleanProperty(
  796. "view.gutter.lineNumbers"));
  797. int interval = jEdit.getIntegerProperty(
  798. "view.gutter.highlightInterval",5);
  799. gutter.setHighlightInterval(interval);
  800. gutter.setCurrentLineHighlightEnabled(jEdit.getBooleanProperty(
  801. "view.gutter.highlightCurrentLine"));
  802. gutter.setStructureHighlightEnabled(jEdit.getBooleanProperty(
  803. "view.gutter.structureHighlight"));
  804. gutter.setStructureHighlightColor(
  805. jEdit.getColorProperty("view.gutter.structureHighlightColor"));
  806. gutter.setBackground(
  807. jEdit.getColorProperty("view.gutter.bgColor"));
  808. gutter.setForeground(
  809. jEdit.getColorProperty("view.gutter.fgColor"));
  810. gutter.setHighlightedForeground(
  811. jEdit.getColorProperty("view.gutter.highlightColor"));
  812. gutter.setFoldColor(
  813. jEdit.getColorProperty("view.gutter.foldColor"));
  814. markerHighlight.setMarkerHighlightColor(
  815. jEdit.getColorProperty("view.gutter.markerColor"));
  816. markerHighlight.setMarkerHighlightEnabled(jEdit.getBooleanProperty(
  817. "view.gutter.markerHighlight"));
  818. gutter.setCurrentLineForeground(
  819. jEdit.getColorProperty("view.gutter.currentLineColor"));
  820. String alignment = jEdit.getProperty(
  821. "view.gutter.numberAlignment");
  822. if ("right".equals(alignment))
  823. {
  824. gutter.setLineNumberAlignment(Gutter.RIGHT);
  825. }
  826. else if ("center".equals(alignment))
  827. {
  828. gutter.setLineNumberAlignment(Gutter.CENTER);
  829. }
  830. else // left == default case
  831. {
  832. gutter.setLineNumberAlignment(Gutter.LEFT);
  833. }
  834. gutter.setFont(jEdit.getFontProperty("view.gutter.font"));
  835. gutter.setGutterEnabled(GutterOptionPane.isGutterEnabled());
  836. gutter.setMinLineNumberDigitCount(
  837. GutterOptionPane.getMinLineNumberDigits());
  838. gutter.setSelectionAreaEnabled(
  839. GutterOptionPane.isSelectionAreaEnabled());
  840. gutter.setSelectionAreaBackground(
  841. GutterOptionPane.getSelectionAreaBackground());
  842. gutter.setSelectionAreaWidth(
  843. GutterOptionPane.getSelectionAreaWidth());
  844. int width = jEdit.getIntegerProperty(
  845. "view.gutter.borderWidth",3);
  846. gutter.setBorder(width,
  847. jEdit.getColorProperty("view.gutter.focusBorderColor"),
  848. jEdit.getColorProperty("view.gutter.noFocusBorderColor"),
  849. textArea.getPainter().getBackground());
  850. gutter.setFoldPainter(textArea.getFoldPainter());
  851. textArea.setCaretBlinkEnabled(jEdit.getBooleanProperty(
  852. "view.caretBlink"));
  853. textArea.setElectricScroll(jEdit.getIntegerProperty(
  854. "view.electricBorders",0));
  855. // Set up the right-click popup menu
  856. textArea.createPopupMenu(null);
  857. // use old property name for backwards compatibility
  858. textArea.setQuickCopyEnabled(jEdit.getBooleanProperty(
  859. "view.middleMousePaste"));
  860. textArea.setDragEnabled(jEdit.getBooleanProperty(
  861. "view.dragAndDrop"));
  862. textArea.setJoinNonWordChars(jEdit.getBooleanProperty(
  863. "view.joinNonWordChars"));
  864. textArea.setCtrlForRectangularSelection(jEdit.getBooleanProperty(
  865. "view.ctrlForRectangularSelection"));
  866. textArea.propertiesChanged();
  867. if (bufferSwitcher != null)
  868. {
  869. bufferSwitcher.setMaximumRowCount(jEdit.getIntegerProperty(
  870. "bufferSwitcher.maxRowCount",10));
  871. }
  872. } //}}}
  873. //{{{ initPainter() method
  874. /**
  875. * Init the painter of a textarea.
  876. *
  877. * @param painter the painter of a textarea
  878. * @since jEdit 4.3pre12
  879. */
  880. public static void initPainter(TextAreaPainter painter)
  881. {
  882. painter.setFont(jEdit.getFontProperty("view.font"));
  883. painter.setStructureHighlightEnabled(jEdit.getBooleanProperty(
  884. "view.structureHighlight"));
  885. painter.setStructureHighlightColor(
  886. jEdit.getColorProperty("view.structureHighlightColor"));
  887. painter.setEOLMarkersPainted(jEdit.getBooleanProperty(
  888. "view.eolMarkers"));
  889. painter.setEOLMarkerColor(
  890. jEdit.getColorProperty("view.eolMarkerColor"));
  891. painter.setWrapGuidePainted(jEdit.getBooleanProperty(
  892. "view.wrapGuide"));
  893. painter.setWrapGuideColor(
  894. jEdit.getColorProperty("view.wrapGuideColor"));
  895. painter.setCaretColor(
  896. jEdit.getColorProperty("view.caretColor"));
  897. painter.setSelectionColor(
  898. jEdit.getColorProperty("view.selectionColor"));
  899. painter.setMultipleSelectionColor(
  900. jEdit.getColorProperty("view.multipleSelectionColor"));
  901. painter.setBackground(
  902. jEdit.getColorProperty("view.bgColor"));
  903. painter.setForeground(
  904. jEdit.getColorProperty("view.fgColor"));
  905. painter.setBlockCaretEnabled(jEdit.getBooleanProperty(
  906. "view.blockCaret"));
  907. painter.setThickCaretEnabled(jEdit.getBooleanProperty(
  908. "view.thickCaret"));
  909. painter.setLineHighlightEnabled(jEdit.getBooleanProperty(
  910. "view.lineHighlight"));
  911. painter.setLineHighlightColor(
  912. jEdit.getColorProperty("view.lineHighlightColor"));
  913. painter.setAntiAlias(new AntiAlias(jEdit.getProperty("view.antiAlias")));
  914. painter.setFractionalFontMetricsEnabled(jEdit.getBooleanProperty(
  915. "view.fracFontMetrics"));
  916. painter.setSelectionFgColor(jEdit.getColorProperty(
  917. "view.selectionFgColor"));
  918. painter.setSelectionFgColorEnabled(jEdit.getBooleanProperty(
  919. "view.selectionFg"));
  920. String defaultFont = jEdit.getProperty("view.font");
  921. int defaultFontSize = jEdit.getIntegerProperty("view.fontsize",12);
  922. painter.setStyles(SyntaxUtilities.loadStyles(defaultFont,defaultFontSize));
  923. SyntaxStyle[] foldLineStyle = new SyntaxStyle[4];
  924. for(int i = 0; i <= 3; i++)
  925. {
  926. foldLineStyle[i] = GUIUtilities.parseStyle(
  927. jEdit.getProperty("view.style.foldLine." + i),
  928. defaultFont,defaultFontSize);
  929. }
  930. painter.setFoldLineStyle(foldLineStyle);
  931. } //}}}
  932. //{{{ loadBufferSwitcher() method
  933. void loadBufferSwitcher()
  934. {
  935. if(jEdit.getBooleanProperty("view.showBufferSwitcher"))
  936. {
  937. if(bufferSwitcher == null)
  938. {
  939. bufferSwitcher = new BufferSwitcher(this);
  940. add(BorderLayout.NORTH,bufferSwitcher);
  941. bufferSwitcher.updateBufferList();
  942. revalidate();
  943. }
  944. }
  945. else if(bufferSwitcher != null)
  946. {
  947. remove(bufferSwitcher);
  948. revalidate();
  949. bufferSwitcher = null;
  950. }
  951. } //}}}
  952. //{{{ handleBufferUpdate() method
  953. @EBHandler
  954. public void handleBufferUpdate(BufferUpdate msg)
  955. {
  956. Buffer _buffer = msg.getBuffer();
  957. if(msg.getWhat() == BufferUpdate.CREATED)
  958. {
  959. if(bufferSwitcher != null)
  960. bufferSwitcher.updateBufferList();
  961. /* When closing the last buffer, the BufferUpdate.CLOSED
  962. * handler doesn't call setBuffer(), because null buffers
  963. * are not supported. Instead, it waits for the subsequent
  964. * 'Untitled' file creation. */
  965. if(buffer.isClosed())
  966. {
  967. // since recentBuffer will be set to the one that
  968. // was closed
  969. recentBuffer = null;
  970. }
  971. }
  972. else if(msg.getWhat() == BufferUpdate.CLOSED)
  973. {
  974. if(bufferSwitcher != null)
  975. bufferSwitcher.updateBufferList();
  976. if(_buffer == buffer)
  977. {
  978. // The closed buffer is the current buffer
  979. Buffer newBuffer = recentBuffer != null ?
  980. recentBuffer : _buffer.getPrev();
  981. if(newBuffer != null && !newBuffer.isClosed())
  982. {
  983. setBuffer(newBuffer);
  984. recentBuffer = newBuffer.getPrev();
  985. }
  986. }
  987. else if(_buffer == recentBuffer)
  988. recentBuffer = null;
  989. Buffer closedBuffer = msg.getBuffer();
  990. if (closedBuffer.isUntitled())
  991. {
  992. // the buffer was a new file so I do not need to keep it's informations
  993. caretsForPath.remove(closedBuffer.getPath());
  994. }
  995. }
  996. else if(msg.getWhat() == BufferUpdate.LOAD_STARTED)
  997. {
  998. if(_buffer == buffer)
  999. {
  1000. textArea.setCaretPosition(0);
  1001. textArea.getPainter().repaint();
  1002. }
  1003. }
  1004. else if(msg.getWhat() == BufferUpdate.LOADED)
  1005. {
  1006. if(_buffer == buffer)
  1007. {
  1008. textArea.repaint();
  1009. if(bufferSwitcher != null)
  1010. bufferSwitcher.updateBufferList();
  1011. if(view.getEditPane() == this)
  1012. {
  1013. StatusBar status = view.getStatus();
  1014. status.updateCaretStatus();
  1015. status.updateBufferStatus();
  1016. status.updateMiscStatus();
  1017. }
  1018. loadCaretInfo();
  1019. }
  1020. }
  1021. else if(msg.getWhat() == BufferUpdate.DIRTY_CHANGED)
  1022. {
  1023. if(_buffer == buffer && bufferSwitcher != null)
  1024. {
  1025. if(buffer.isDirty())
  1026. bufferSwitcher.repaint();
  1027. else
  1028. bufferSwitcher.updateBufferList();
  1029. }
  1030. }
  1031. else if(msg.getWhat() == BufferUpdate.MARKERS_CHANGED)
  1032. {
  1033. if(_buffer == buffer)
  1034. textArea.getGutter().repaint();
  1035. }
  1036. else if(msg.getWhat() == BufferUpdate.PROPERTIES_CHANGED)
  1037. {
  1038. if(_buffer == buffer && buffer.isLoaded())
  1039. {
  1040. textArea.propertiesChanged();
  1041. if(view.getEditPane() == this)
  1042. view.getStatus().updateBufferStatus();
  1043. }
  1044. }
  1045. else if(msg.getWhat() == BufferUpdate.SAVED && _buffer == buffer)
  1046. {
  1047. textArea.propertiesChanged();
  1048. }
  1049. } //}}}
  1050. //}}}
  1051. //{{{ StatusHandler class
  1052. class StatusHandler implements StatusListener
  1053. {
  1054. public void statusChanged(org.gjt.sp.jedit.textarea.TextArea textArea, int flag, boolean value)
  1055. {
  1056. StatusBar status = view.getStatus();
  1057. if(status == null)
  1058. return;
  1059. switch(flag)
  1060. {
  1061. case OVERWRITE_CHANGED:
  1062. status.setMessageAndClear(
  1063. jEdit.getProperty("view.status.overwrite-changed",
  1064. new Integer[] { value ? 1 : 0 }));
  1065. break;
  1066. case MULTI_SELECT_CHANGED:
  1067. status.setMessageAndClear(
  1068. jEdit.getProperty("view.status.multi-changed",
  1069. new Integer[] { value ? 1 : 0 }));
  1070. break;
  1071. case RECT_SELECT_CHANGED:
  1072. status.setMessageAndClear(
  1073. jEdit.getProperty("view.status.rect-select-changed",
  1074. new Integer[] { value ? 1 : 0 }));
  1075. break;
  1076. }
  1077. status.updateMiscStatus();
  1078. }
  1079. public void bracketSelected(org.gjt.sp.jedit.textarea.TextArea textArea, int line, String text)
  1080. {
  1081. StatusBar status = view.getStatus();
  1082. if(status == null)
  1083. return;
  1084. status.setMessageAndClear(jEdit.getProperty(
  1085. "view.status.bracket",new Object[] {
  1086. line, text }));
  1087. }
  1088. public void narrowActive(org.gjt.sp.jedit.textarea.TextArea textArea)
  1089. {
  1090. StatusBar status = view.getStatus();
  1091. if(status == null)
  1092. return;
  1093. status.setMessageAndClear(
  1094. jEdit.getProperty("view.status.narrow"));
  1095. }
  1096. } //}}}
  1097. //{{{ MarkerHighlight class
  1098. class MarkerHighlight extends TextAreaExtension
  1099. {
  1100. private boolean markerHighlight;
  1101. private Color markerHighlightColor;
  1102. //{{{ getMarkerHighlightColor() method
  1103. public Color getMarkerHighlightColor()
  1104. {
  1105. return markerHighlightColor;
  1106. } //}}}
  1107. //{{{ setMarkerHighlightColor() method
  1108. public void setMarkerHighlightColor(Color markerHighlightColor)
  1109. {
  1110. this.markerHighlightColor = markerHighlightColor;
  1111. } //}}}
  1112. //{{{ isMarkerHighlightEnabled() method
  1113. public boolean isMarkerHighlightEnabled()
  1114. {
  1115. return markerHighlight;
  1116. } //}}}
  1117. //{{{ isMarkerHighlightEnabled()
  1118. public void setMarkerHighlightEnabled(boolean markerHighlight)
  1119. {
  1120. this.markerHighlight = markerHighlight;
  1121. } //}}}
  1122. //{{{ paintValidLine() method
  1123. @Override
  1124. public void paintValidLine(Graphics2D gfx, int screenLine,
  1125. int physicalLine, int start, int end, int y)
  1126. {
  1127. if(isMarkerHighlightEnabled())
  1128. {
  1129. Buffer buffer = (Buffer)textArea.getBuffer();
  1130. if(buffer.getMarkerInRange(start,end) != null)
  1131. {
  1132. gfx.setColor(getMarkerHighlightColor());
  1133. int height = textArea.getPainter().getLineHeight();
  1134. gfx.fillRect(0, y, textArea.getGutter().getWidth(), height);
  1135. }
  1136. }
  1137. } //}}}
  1138. //{{{ getToolTipText() method
  1139. @Override
  1140. public String getToolTipText(int x, int y)
  1141. {
  1142. if(isMarkerHighlightEnabled())
  1143. {
  1144. int lineHeight = textArea.getPainter().getLineHeight();
  1145. if(lineHeight == 0)
  1146. return null;
  1147. int line = y / lineHeight;
  1148. int start = textArea.getScreenLineStartOffset(line);
  1149. int end = textArea.getScreenLineEndOffset(line);
  1150. if(start == -1 || end == -1)
  1151. return null;
  1152. Buffer buffer = (Buffer)textArea.getBuffer();
  1153. Marker marker = buffer.getMarkerInRange(start,end);
  1154. if(marker != null)
  1155. {
  1156. char shortcut = marker.getShortcut();
  1157. if(shortcut == '\0')
  1158. return jEdit.getProperty("view.gutter.marker.no-name");
  1159. else
  1160. {
  1161. String[] args = { String.valueOf(shortcut) };
  1162. return jEdit.getProperty("view.gutter.marker",args);
  1163. }
  1164. }
  1165. }
  1166. return null;
  1167. } //}}}
  1168. } //}}}
  1169. }