PageRenderTime 375ms CodeModel.GetById 304ms app.highlight 55ms RepoModel.GetById 0ms app.codeStats 2ms

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

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

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