PageRenderTime 409ms CodeModel.GetById 272ms app.highlight 105ms RepoModel.GetById 15ms app.codeStats 2ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/textarea/JEditTextArea.java

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

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