/jEdit/tags/jedit-4-3-pre15/org/gjt/sp/jedit/EditPane.java

# · Java · 1377 lines · 939 code · 140 blank · 298 comment · 246 complexity · 5513febafd6e79cf1734d68199cc4ad3 MD5 · raw file

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