PageRenderTime 133ms CodeModel.GetById 80ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 1ms

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