PageRenderTime 278ms CodeModel.GetById 208ms app.highlight 50ms RepoModel.GetById 5ms app.codeStats 1ms

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

#
Java | 2675 lines | 1504 code | 301 blank | 870 comment | 279 complexity | 7caf13a8119e32dd613f4c6843c78385 MD5 | raw file

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

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

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