PageRenderTime 252ms CodeModel.GetById 80ms app.highlight 121ms RepoModel.GetById 20ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/textarea/JEditTextArea.java

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

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