PageRenderTime 218ms CodeModel.GetById 101ms app.highlight 100ms RepoModel.GetById 1ms app.codeStats 2ms

/jEdit/tags/jedit-4-2-pre4/org/gjt/sp/jedit/textarea/JEditTextArea.java

#
Java | 2867 lines | 1682 code | 348 blank | 837 comment | 379 complexity | ca5078213baa152f7ee737459a1769ba MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file