PageRenderTime 223ms CodeModel.GetById 163ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 1500 lines | 847 code | 165 blank | 488 comment | 122 complexity | 9d554921d9af61646586fcfa8155141c MD5 | raw file
   1/*
   2 * TextAreaPainter.java - Paints the text area
   3 * :tabSize=8:indentSize=8:noTabs=false:
   4 * :folding=explicit:collapseFolds=1:
   5 *
   6 * Copyright (C) 1999, 2005 Slava Pestov
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * as published by the Free Software Foundation; either version 2
  11 * of the License, or any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  21 */
  22
  23package org.gjt.sp.jedit.textarea;
  24
  25//{{{ Imports
  26import javax.swing.text.*;
  27import javax.swing.JComponent;
  28
  29import java.awt.event.MouseEvent;
  30import java.awt.font.*;
  31import java.awt.*;
  32import java.util.*;
  33import org.gjt.sp.jedit.buffer.IndentFoldHandler;
  34import org.gjt.sp.jedit.buffer.JEditBuffer;
  35import org.gjt.sp.jedit.syntax.Chunk;
  36import org.gjt.sp.jedit.syntax.DefaultTokenHandler;
  37import org.gjt.sp.jedit.syntax.SyntaxStyle;
  38import org.gjt.sp.jedit.syntax.Token;
  39import org.gjt.sp.jedit.Debug;
  40
  41import org.gjt.sp.util.Log;
  42//}}}
  43
  44/**
  45 * The text area painter is the component responsible for displaying the
  46 * text of the current buffer. The only methods in this class that should
  47 * be called by plugins are those for adding and removing
  48 * text area extensions.
  49 *
  50 * @see #addExtension(TextAreaExtension)
  51 * @see #addExtension(int,TextAreaExtension)
  52 * @see #removeExtension(TextAreaExtension)
  53 * @see TextAreaExtension
  54 * @see TextArea
  55 *
  56 * @author Slava Pestov
  57 * @version $Id: TextAreaPainter.java 20109 2011-10-18 12:25:29Z evanpw $
  58 */
  59public class TextAreaPainter extends JComponent implements TabExpander
  60{
  61	//{{{ Layers
  62	/**
  63	 * The lowest possible layer.
  64	 * @see #addExtension(int,TextAreaExtension)
  65	 * @since jEdit 4.0pre4
  66	 */
  67	public static final int LOWEST_LAYER = Integer.MIN_VALUE;
  68
  69	/**
  70	 * Below selection layer. The JDiff plugin will use this.
  71	 * @see #addExtension(int,TextAreaExtension)
  72	 * @since jEdit 4.0pre4
  73	 */
  74	public static final int BACKGROUND_LAYER = -60;
  75
  76	/**
  77	 * The line highlight and collapsed fold highlight layer.
  78	 * @see #addExtension(int,TextAreaExtension)
  79	 * @since jEdit 4.0pre7
  80	 */
  81	public static final int LINE_BACKGROUND_LAYER = -50;
  82
  83	/**
  84	 * Below selection layer.
  85	 * @see #addExtension(int,TextAreaExtension)
  86	 * @since jEdit 4.0pre4
  87	 */
  88	public static final int BELOW_SELECTION_LAYER = -40;
  89
  90	/**
  91	 * Selection layer. Most extensions will be above this layer, but some
  92	 * (eg, JDiff) will want to be below the selection.
  93	 * @see #addExtension(int,TextAreaExtension)
  94	 * @since jEdit 4.0pre4
  95	 */
  96	public static final int SELECTION_LAYER = -30;
  97
  98	/**
  99	 * Wrap guide layer. Most extensions will be above this layer.
 100	 * @since jEdit 4.0pre4
 101	 */
 102	public static final int WRAP_GUIDE_LAYER = -20;
 103
 104	/**
 105	 * Below most extensions layer.
 106	 * @see #addExtension(int,TextAreaExtension)
 107	 * @since jEdit 4.0pre4
 108	 */
 109	public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
 110
 111	/**
 112	 * Default extension layer. This is above the wrap guide but below the
 113	 * structure highlight.
 114	 * @since jEdit 4.0pre4
 115	 */
 116	public static final int DEFAULT_LAYER = 0;
 117
 118	/**
 119	 * Block caret layer. Most extensions will be below this layer.
 120	 * @since jEdit 4.2pre1
 121	 */
 122	public static final int BLOCK_CARET_LAYER = 50;
 123
 124	/**
 125	 * Bracket highlight layer. Most extensions will be below this layer.
 126	 * @since jEdit 4.0pre4
 127	 */
 128	public static final int BRACKET_HIGHLIGHT_LAYER = 100;
 129
 130	/**
 131	 * Text layer. Most extensions will be below this layer.
 132	 * @since jEdit 4.2pre1
 133	 */
 134	public static final int TEXT_LAYER = 200;
 135
 136	/**
 137	 * Caret layer. Most extensions will be below this layer.
 138	 * @since jEdit 4.2pre1
 139	 */
 140	public static final int CARET_LAYER = 300;
 141
 142	/**
 143	 * Highest possible layer.
 144	 * @since jEdit 4.0pre4
 145	 */
 146	public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
 147	//}}}
 148
 149	//{{{ setBounds() method
 150	/**
 151	 * It is a bad idea to override this, but we need to get the component
 152	 * event before the first repaint.
 153	 */
 154	@Override
 155	public void setBounds(int x, int y, int width, int height)
 156	{
 157		if(x == getX() && y == getY() && width == getWidth()
 158			&& height == getHeight())
 159		{
 160			return;
 161		}
 162
 163		super.setBounds(x,y,width,height);
 164
 165		textArea.recalculateVisibleLines();
 166		if(!textArea.getBuffer().isLoading())
 167			textArea.recalculateLastPhysicalLine();
 168		textArea.propertiesChanged();
 169		textArea.updateMaxHorizontalScrollWidth();
 170		textArea.scrollBarsInitialized = true;
 171	} //}}}
 172
 173	//{{{ addNotify() method
 174	@Override
 175	public void addNotify()
 176	{
 177		super.addNotify();
 178		hiddenCursor = getToolkit().createCustomCursor(
 179			getGraphicsConfiguration()
 180			.createCompatibleImage(16,16,
 181			Transparency.BITMASK),
 182			new Point(0,0),"Hidden");
 183	} //}}}
 184
 185	//{{{ setCursor() method
 186	/**
 187	 * Change the mouse cursor.
 188	 * If the cursor is hiddenCursor or TEXT_CURSOR, it is the default cursor and the cursor will not disappear
 189	 * anymore while typing until {@link #resetCursor()} is called.
 190	 * @param cursor the new cursor
 191	 * @since jEdit 4.4pre1
 192	 */
 193	public void setCursor(Cursor cursor)
 194	{
 195		defaultCursor = cursor == hiddenCursor || cursor.getType() == Cursor.TEXT_CURSOR;
 196		super.setCursor(cursor);
 197	} //}}}
 198
 199	//{{{ setCursor() method
 200	/**
 201	 * Reset the cursor to it's default value.
 202	 * @since jEdit 4.4pre1
 203	 */
 204	public void resetCursor()
 205	{
 206		defaultCursor = true;
 207	} //}}}
 208
 209	//{{{ showCursor() method
 210	/**
 211	 * Show the cursor if it is the default cursor
 212	 */
 213	void showCursor()
 214	{
 215		if (defaultCursor)
 216		{
 217		       setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
 218		}
 219	} //}}}
 220
 221	//{{{ hideCursor() method
 222	/**
 223	 * Hide the cursor if it is the default cursor
 224	 */
 225	void hideCursor()
 226	{
 227		if (defaultCursor)
 228		{
 229			setCursor(hiddenCursor);
 230		}
 231	} //}}}
 232
 233	//{{{ getFocusTraversalKeysEnabled() method
 234	/**
 235	 * Makes the tab key work in Java 1.4.
 236	 * @since jEdit 3.2pre4
 237	 */
 238	@Override
 239	public boolean getFocusTraversalKeysEnabled()
 240	{
 241		return false;
 242	} //}}}
 243
 244	//{{{ Getters and setters
 245
 246	//{{{ getStyles() method
 247	/**
 248	 * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
 249	 * will be used to paint tokens with id = <i>n</i>.
 250	 * @return an array of SyntaxStyles
 251	 * @see org.gjt.sp.jedit.syntax.Token
 252	 */
 253	public final SyntaxStyle[] getStyles()
 254	{
 255		return styles;
 256	} //}}}
 257
 258	//{{{ setStyles() method
 259	/**
 260	 * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
 261	 * will be used to paint tokens with id = <i>n</i>.
 262	 * @param styles The syntax styles
 263	 * @see org.gjt.sp.jedit.syntax.Token
 264	 */
 265	public final void setStyles(SyntaxStyle[] styles)
 266	{
 267		this.styles = styles;
 268		styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
 269		textArea.chunkCache.invalidateAll();
 270		repaint();
 271	} //}}}
 272
 273	//{{{ getCaretColor() method
 274	/**
 275	 * Returns the caret color.
 276	 */
 277	public final Color getCaretColor()
 278	{
 279		return caretColor;
 280	} //}}}
 281
 282	//{{{ setCaretColor() method
 283	/**
 284	 * Sets the caret color.
 285	 * @param caretColor The caret color
 286	 */
 287	public final void setCaretColor(Color caretColor)
 288	{
 289		this.caretColor = caretColor;
 290		if(textArea.getBuffer() != null)
 291			textArea.invalidateLine(textArea.getCaretLine());
 292	} //}}}
 293
 294	//{{{ getSelectionColor() method
 295	/**
 296	 * Returns the selection color.
 297	 */
 298	public final Color getSelectionColor()
 299	{
 300		return selectionColor;
 301	} //}}}
 302
 303	//{{{ setSelectionColor() method
 304	/**
 305	 * Sets the selection color.
 306	 * @param selectionColor The selection color
 307	 */
 308	public final void setSelectionColor(Color selectionColor)
 309	{
 310		this.selectionColor = selectionColor;
 311		textArea.repaint();
 312	} //}}}
 313
 314	//{{{ getMultipleSelectionColor() method
 315	/**
 316	 * Returns the multiple selection color.
 317	 * @since jEdit 4.2pre1
 318	 */
 319	public final Color getMultipleSelectionColor()
 320	{
 321		return multipleSelectionColor;
 322	} //}}}
 323
 324	//{{{ setMultipleSelectionColor() method
 325	/**
 326	 * Sets the multiple selection color.
 327	 * @param multipleSelectionColor The multiple selection color
 328	 * @since jEdit 4.2pre1
 329	 */
 330	public final void setMultipleSelectionColor(Color multipleSelectionColor)
 331	{
 332		this.multipleSelectionColor = multipleSelectionColor;
 333		textArea.repaint();
 334	} //}}}
 335
 336	//{{{ getLineHighlightColor() method
 337	/**
 338	 * Returns the line highlight color.
 339	 */
 340	public final Color getLineHighlightColor()
 341	{
 342		return lineHighlightColor;
 343	} //}}}
 344
 345	//{{{ setLineHighlightColor() method
 346	/**
 347	 * Sets the line highlight color.
 348	 * @param lineHighlightColor The line highlight color
 349	 */
 350	public final void setLineHighlightColor(Color lineHighlightColor)
 351	{
 352		this.lineHighlightColor = lineHighlightColor;
 353		if(textArea.getBuffer() != null)
 354			textArea.invalidateLine(textArea.getCaretLine());
 355	} //}}}
 356
 357	//{{{ isLineHighlightEnabled() method
 358	/**
 359	 * Returns true if line highlight is enabled, false otherwise.
 360	 */
 361	public final boolean isLineHighlightEnabled()
 362	{
 363		return lineHighlight;
 364	} //}}}
 365
 366	//{{{ setLineHighlightEnabled() method
 367	/**
 368	 * Enables or disables current line highlighting.
 369	 * @param lineHighlight True if current line highlight should be enabled,
 370	 * false otherwise
 371	 */
 372	public final void setLineHighlightEnabled(boolean lineHighlight)
 373	{
 374		this.lineHighlight = lineHighlight;
 375		textArea.repaint();
 376	} //}}}
 377
 378	//{{{ getSelectionFgColor() method
 379	/**
 380	 * Returns the selection foreground color, if one is set.
 381	 * @since jEdit 4.4.1
 382	 */
 383	public final Color getSelectionFgColor()
 384	{
 385		return selectionFgColor;
 386	} //}}}
 387
 388	//{{{ setSelectionFgColor() method
 389	/**
 390	 * Sets the selection foreground color.
 391	 * @param selectionFgColor The selection foreground color
 392	 * @since jEdit 4.4.1
 393	 */
 394	public final void setSelectionFgColor(Color selectionFgColor)
 395	{
 396		this.selectionFgColor = selectionFgColor;
 397		if (isSelectionFgColorEnabled())
 398			textArea.repaint();
 399	} //}}}
 400
 401	//{{{ isSelectionFgColorEnabled() method
 402	/**
 403	 * Returns true if selection foreground color is enabled - i.e. a specific
 404	 * color is used for the selection foreground instead of the syntax highlight
 405	 * color.
 406	 * @since jEdit 4.4.1
 407	 */
 408	public final boolean isSelectionFgColorEnabled()
 409	{
 410		return selectionFg;
 411	} //}}}
 412
 413	//{{{ setSelectionFgColorEnabled() method
 414	/**
 415	 * Enables or disables selection foreground color.
 416	 * @param selectionFg True if selection foreground should be enabled,
 417	 * false otherwise
 418	 * @since jEdit 4.4.1
 419	 */
 420	public final void setSelectionFgColorEnabled(boolean selectionFg)
 421	{
 422		this.selectionFg = selectionFg;
 423		textArea.repaint();
 424	} //}}}
 425
 426	//{{{ getStructureHighlightColor() method
 427	/**
 428	 * Returns the structure highlight color.
 429	 * @since jEdit 4.2pre3
 430	 */
 431	public final Color getStructureHighlightColor()
 432	{
 433		return structureHighlightColor;
 434	} //}}}
 435
 436	//{{{ setStructureHighlightColor() method
 437	/**
 438	 * Sets the structure highlight color.
 439	 * @param structureHighlightColor The bracket highlight color
 440	 * @since jEdit 4.2pre3
 441	 */
 442	public final void setStructureHighlightColor(
 443		Color structureHighlightColor)
 444	{
 445		this.structureHighlightColor = structureHighlightColor;
 446		textArea.invalidateStructureMatch();
 447	} //}}}
 448
 449	//{{{ isStructureHighlightEnabled() method
 450	/**
 451	 * Returns true if structure highlighting is enabled, false otherwise.
 452	 * @since jEdit 4.2pre3
 453	 */
 454	public final boolean isStructureHighlightEnabled()
 455	{
 456		return structureHighlight;
 457	} //}}}
 458
 459	//{{{ setStructureHighlightEnabled() method
 460	/**
 461	 * Enables or disables structure highlighting.
 462	 * @param structureHighlight True if structure highlighting should be
 463	 * enabled, false otherwise
 464	 * @since jEdit 4.2pre3
 465	 */
 466	public final void setStructureHighlightEnabled(boolean structureHighlight)
 467	{
 468		this.structureHighlight = structureHighlight;
 469		textArea.invalidateStructureMatch();
 470	} //}}}
 471
 472	//{{{ isBlockCaretEnabled() method
 473	/**
 474	 * Returns true if the caret should be drawn as a block, false otherwise.
 475	 */
 476	public final boolean isBlockCaretEnabled()
 477	{
 478		return blockCaret;
 479	} //}}}
 480
 481	//{{{ setBlockCaretEnabled() method
 482	/**
 483	 * Sets if the caret should be drawn as a block, false otherwise.
 484	 * @param blockCaret True if the caret should be drawn as a block,
 485	 * false otherwise.
 486	 */
 487	public final void setBlockCaretEnabled(boolean blockCaret)
 488	{
 489		this.blockCaret = blockCaret;
 490		extensionMgr.removeExtension(caretExtension);
 491		if(blockCaret)
 492			addExtension(BLOCK_CARET_LAYER,caretExtension);
 493		else
 494			addExtension(CARET_LAYER,caretExtension);
 495		if(textArea.getBuffer() != null)
 496			textArea.invalidateLine(textArea.getCaretLine());
 497	} //}}}
 498
 499	//{{{ isThickCaretEnabled() method
 500	/**
 501	 * Returns true if the caret should be drawn with a thick line, false otherwise.
 502	 * @since jEdit 4.3pre15
 503	 */
 504	public final boolean isThickCaretEnabled()
 505	{
 506		return thickCaret;
 507	} //}}}
 508
 509	//{{{ setThickCaretEnabled() method
 510	/**
 511	 * Sets if the caret should be drawn with a thick line.
 512	 * @param thickCaret
 513	 *     True if the caret should be drawn as a block, false otherwise.
 514	 * @since jEdit 4.3pre15
 515	 */
 516	public final void setThickCaretEnabled(boolean thickCaret)
 517	{
 518		this.thickCaret = thickCaret;
 519		if(textArea.getBuffer() != null)
 520			textArea.invalidateLine(textArea.getCaretLine());
 521	} //}}}
 522
 523	//{{{ getEOLMarkerColor() method
 524	/**
 525	 * Returns the EOL marker color.
 526	 */
 527	public final Color getEOLMarkerColor()
 528	{
 529		return eolMarkerColor;
 530	} //}}}
 531
 532	//{{{ setEOLMarkerColor() method
 533	/**
 534	 * Sets the EOL marker color.
 535	 * @param eolMarkerColor The EOL marker color
 536	 */
 537	public final void setEOLMarkerColor(Color eolMarkerColor)
 538	{
 539		this.eolMarkerColor = eolMarkerColor;
 540		repaint();
 541	} //}}}
 542
 543	//{{{ getEOLMarkersPainted() method
 544	/**
 545	 * Returns true if EOL markers are drawn, false otherwise.
 546	 */
 547	public final boolean getEOLMarkersPainted()
 548	{
 549		return eolMarkers;
 550	} //}}}
 551
 552	//{{{ setEOLMarkersPainted() method
 553	/**
 554	 * Sets if EOL markers are to be drawn.
 555	 * @param eolMarkers True if EOL markers should be drawn, false otherwise
 556	 */
 557	public final void setEOLMarkersPainted(boolean eolMarkers)
 558	{
 559		this.eolMarkers = eolMarkers;
 560		repaint();
 561	} //}}}
 562
 563	//{{{ getWrapGuideColor() method
 564	/**
 565	 * Returns the wrap guide color.
 566	 */
 567	public final Color getWrapGuideColor()
 568	{
 569		return wrapGuideColor;
 570	} //}}}
 571
 572	//{{{ setWrapGuideColor() method
 573	/**
 574	 * Sets the wrap guide color.
 575	 * @param wrapGuideColor The wrap guide color
 576	 */
 577	public final void setWrapGuideColor(Color wrapGuideColor)
 578	{
 579		this.wrapGuideColor = wrapGuideColor;
 580		repaint();
 581	} //}}}
 582
 583	//{{{ isWrapGuidePainted() method
 584	/**
 585	 * Returns true if the wrap guide is drawn, false otherwise.
 586	 * @since jEdit 4.0pre4
 587	 */
 588	public final boolean isWrapGuidePainted()
 589	{
 590		return wrapGuide;
 591	} //}}}
 592
 593	//{{{ setWrapGuidePainted() method
 594	/**
 595	 * Sets if the wrap guide is to be drawn.
 596	 * @param wrapGuide True if the wrap guide should be drawn, false otherwise
 597	 */
 598	public final void setWrapGuidePainted(boolean wrapGuide)
 599	{
 600		this.wrapGuide = wrapGuide;
 601		repaint();
 602	} //}}}
 603
 604	//{{{ getFoldLineStyle() method
 605	/**
 606	 * Returns the fold line style. The first element is the style for
 607	 * lines with a fold level greater than 3. The remaining elements
 608	 * are for fold levels 1 to 3.
 609	 */
 610	public final SyntaxStyle[] getFoldLineStyle()
 611	{
 612		return foldLineStyle;
 613	} //}}}
 614
 615	//{{{ setFoldLineStyle() method
 616	/**
 617	 * Sets the fold line style. The first element is the style for
 618	 * lines with a fold level greater than 3. The remaining elements
 619	 * are for fold levels 1 to 3.
 620	 * @param foldLineStyle The fold line style
 621	 */
 622	public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle)
 623	{
 624		this.foldLineStyle = foldLineStyle;
 625		textArea.chunkCache.invalidateAll();
 626		repaint();
 627	} //}}}
 628
 629	//{{{ setAntiAliasEnabled() method
 630	/**
 631	 * As of jEdit 4.3pre4, a new JDK 1.6 subpixel antialias mode is supported.
 632	 *
 633	 * @since jEdit 4.2pre4
 634	 */
 635	public void setAntiAlias(AntiAlias newValue)
 636	{
 637		antiAlias = newValue;
 638		updateRenderingHints();
 639	} //}}}
 640
 641	//{{{ getAntiAlias() method
 642	/**
 643	 * @return the AntiAlias value that is currently used for TextAreas.
 644	 * @since jedit 4.3pre4
 645	 */
 646	public AntiAlias getAntiAlias()
 647	{
 648		return antiAlias;
 649	} //}}}
 650
 651	//{{{ setFractionalFontMetricsEnabled() method
 652	/**
 653	 * Sets if fractional font metrics should be enabled. Has no effect when
 654	 * running on Java 1.1.
 655	 * @since jEdit 3.2pre6
 656	 */
 657	public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
 658	{
 659		this.fracFontMetrics = fracFontMetrics;
 660		updateRenderingHints();
 661	} //}}}
 662
 663	//{{{ isFractionalFontMetricsEnabled() method
 664	/**
 665	 * Returns if fractional font metrics are enabled.
 666	 * @since jEdit 3.2pre6
 667	 */
 668	public boolean isFractionalFontMetricsEnabled()
 669	{
 670		return fracFontMetrics;
 671	} //}}}
 672
 673	//{{{ getFontRenderContext() method
 674	/**
 675	 * Returns the font render context.
 676	 * @since jEdit 4.0pre4
 677	 */
 678	public FontRenderContext getFontRenderContext()
 679	{
 680		return fontRenderContext;
 681	} //}}}
 682
 683	//}}}
 684
 685	//{{{ addExtension() method
 686	/**
 687	 * Adds a text area extension, which can perform custom painting and
 688	 * tool tip handling.
 689	 * @param extension The extension
 690	 * @since jEdit 4.0pre4
 691	 */
 692	public void addExtension(TextAreaExtension extension)
 693	{
 694		extensionMgr.addExtension(DEFAULT_LAYER,extension);
 695		repaint();
 696	} //}}}
 697
 698	//{{{ addExtension() method
 699	/**
 700	 * Adds a text area extension, which can perform custom painting and
 701	 * tool tip handling.
 702	 * @param layer The layer to add the extension to. Note that more than
 703	 * extension can share the same layer.
 704	 * @param extension The extension
 705	 * @since jEdit 4.0pre4
 706	 */
 707	public void addExtension(int layer, TextAreaExtension extension)
 708	{
 709		extensionMgr.addExtension(layer,extension);
 710		repaint();
 711	} //}}}
 712
 713	//{{{ removeExtension() method
 714	/**
 715	 * Removes a text area extension. It will no longer be asked to
 716	 * perform custom painting and tool tip handling.
 717	 * @param extension The extension
 718	 * @since jEdit 4.0pre4
 719	 */
 720	public void removeExtension(TextAreaExtension extension)
 721	{
 722		extensionMgr.removeExtension(extension);
 723		repaint();
 724	} //}}}
 725
 726	//{{{ getExtensions() method
 727	/**
 728	 * Returns an array of registered text area extensions. Useful for
 729	 * debugging purposes.
 730	 * @since jEdit 4.1pre5
 731	 */
 732	public TextAreaExtension[] getExtensions()
 733	{
 734		return extensionMgr.getExtensions();
 735	} //}}}
 736
 737	//{{{ getToolTipText() method
 738	/**
 739	 * Returns the tool tip to display at the specified location.
 740	 * @param evt The mouse event
 741	 */
 742	@Override
 743	public String getToolTipText(MouseEvent evt)
 744	{
 745		if(textArea.getBuffer().isLoading())
 746			return null;
 747
 748		return extensionMgr.getToolTipText(evt.getX(),evt.getY());
 749	} //}}}
 750
 751	//{{{ getFontMetrics() method
 752	/**
 753	 * Returns the font metrics used by this component.
 754	 */
 755	public FontMetrics getFontMetrics()
 756	{
 757		return fm;
 758	} //}}}
 759	
 760	//{{{ getLineHeight() method
 761	/**
 762	 * Returns the line height as given by the font metrics plus the 
 763	 * added line spacing.
 764	 */
 765	public int getLineHeight()
 766	{
 767		return fm.getHeight() + extraLineSpacing;
 768	} //}}}
 769	
 770	//{{{ getFontHeight() method
 771	/**
 772	 * Returns the font height as given by the font metrics.
 773	 */
 774	public int getFontHeight()
 775	{
 776		return fm.getHeight();
 777	} //}}}
 778	
 779	//{{{ getLineExtraSpacing() method
 780	/**
 781	 * Returns the number of pixels from the start of the line to the start
 782	 * of text (the extra line spacing).
 783	 */
 784	public int getLineExtraSpacing()
 785	{
 786		return extraLineSpacing;
 787	} //}}}
 788
 789	//{{{ setLineExtraSpacing() method
 790	/**
 791	 * Sets extra spacing between lines in pixels.
 792	 */
 793	public void setLineExtraSpacing(int spacing)
 794	{
 795		extraLineSpacing = spacing;
 796	} //}}}
 797	
 798	//{{{ setFont() method
 799	/**
 800	 * Sets the font for this component. This is overridden to update the
 801	 * cached font metrics and to recalculate which lines are visible.
 802	 * @param font The font
 803	 */
 804	@Override
 805	public void setFont(Font font)
 806	{
 807		super.setFont(font);
 808		fm = getFontMetrics(font);
 809		textArea.recalculateVisibleLines();
 810		if(textArea.getBuffer() != null
 811			&& !textArea.getBuffer().isLoading())
 812			textArea.recalculateLastPhysicalLine();
 813		//textArea.propertiesChanged();
 814	} //}}}
 815
 816	//{{{ getStringWidth() method
 817	/**
 818	 * Returns the width of the given string, in pixels, using the text
 819	 * area's current font.
 820	 *
 821	 * @since jEdit 4.2final
 822	 */
 823	public float getStringWidth(String str)
 824	{
 825		if(textArea.charWidth != 0)
 826			return textArea.charWidth * str.length();
 827		else
 828		{
 829			return (float)getFont().getStringBounds(
 830				str,getFontRenderContext()).getWidth();
 831		}
 832	} //}}}
 833
 834	//{{{ getRenderingHints() method
 835	/**
 836	 * Returns the rendering hints used by the Graphics2D class; in this
 837         * case, for anti-aliasing of text.
 838	 *
 839	 * @since jEdit 4.5pre1
 840	 */
 841        public RenderingHints getRenderingHints()
 842	{
 843            return renderingHints;
 844        } //}}}
 845
 846	//{{{ update() method
 847	/**
 848	 * Repaints the text.
 849	 * @param _gfx The graphics context
 850	 */
 851	@Override
 852	public void update(Graphics _gfx)
 853	{
 854		paint(_gfx);
 855	} //}}}
 856
 857	//{{{ paint() method
 858	/**
 859	 * Repaints the text.
 860	 * @param _gfx The graphics context
 861	 */
 862	@Override
 863	public void paint(Graphics _gfx)
 864	{
 865		assert _gfx instanceof Graphics2D;
 866		Graphics2D gfx = (Graphics2D)_gfx;
 867		gfx.setRenderingHints(renderingHints);
 868		fontRenderContext = gfx.getFontRenderContext();
 869
 870		Rectangle clipRect = gfx.getClipBounds();
 871		int lineHeight = getLineHeight();
 872		int charHeight = getFontHeight();
 873		if(lineHeight == 0 || textArea.getBuffer().isLoading())
 874		{
 875			gfx.setColor(getBackground());
 876			gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
 877		}
 878		else
 879		{
 880			long prepareTime = System.nanoTime();
 881			// Because the clipRect's height is usually an even multiple
 882			// of the font height, we subtract 1 from it, otherwise one
 883			// too many lines will always be painted.
 884			int firstLine = clipRect.y / lineHeight;
 885			int lastLine = (clipRect.y + clipRect.height - 1) / lineHeight;
 886			gfx.setColor(getBackground());
 887			gfx.setFont(getFont());
 888			prepareTime = System.nanoTime() - prepareTime;
 889
 890			long linesTime = System.nanoTime();
 891			int numLines = lastLine - firstLine + 1;
 892			int y = firstLine * lineHeight;
 893			gfx.fillRect(0,y,getWidth(),numLines * lineHeight);
 894			extensionMgr.paintScreenLineRange(textArea,gfx,
 895							  firstLine,lastLine,
 896							  y, lineHeight);
 897			linesTime = System.nanoTime() - linesTime;
 898
 899			if(Debug.PAINT_TIMER && numLines >= 1)
 900				Log.log(Log.DEBUG,this,"repainting " + numLines + " lines took " + prepareTime + "/" + linesTime + " ns");
 901		}
 902
 903		textArea.updateMaxHorizontalScrollWidth();
 904	} //}}}
 905	
 906	//{{{ nextTabStop() method
 907	/**
 908	 * Implementation of TabExpander interface. Returns next tab stop after
 909	 * a specified point.
 910	 * @param x The x co-ordinate
 911	 * @param tabOffset Ignored
 912	 * @return The next tab stop after <i>x</i>
 913	 */
 914	public float nextTabStop(float x, int tabOffset)
 915	{
 916		int ntabs = (int)(x / textArea.tabSize);
 917		return (ntabs + 1) * textArea.tabSize;
 918	} //}}}
 919
 920	//{{{ getPreferredSize() method
 921	/**
 922	 * Returns the painter's preferred size.
 923	 */
 924	@Override
 925	public Dimension getPreferredSize()
 926	{
 927		Dimension dim = new Dimension();
 928
 929		char[] foo = new char[80];
 930		for(int i = 0; i < foo.length; i++)
 931			foo[i] = ' ';
 932		dim.width = (int)getStringWidth(new String(foo));
 933		dim.height = getLineHeight() * 25;
 934		return dim;
 935	} //}}}
 936
 937	//{{{ getMinimumSize() method
 938	/**
 939	 * Returns the painter's minimum size.
 940	 */
 941	@Override
 942	public Dimension getMinimumSize()
 943	{
 944		return getPreferredSize();
 945	} //}}}
 946
 947	//{{{ Package-private members
 948
 949	//{{{ Instance variables
 950	/* package-private since they are accessed by inner classes and we
 951	 * want this to be fast */
 952	TextArea textArea;
 953
 954	SyntaxStyle[] styles;
 955	Color caretColor;
 956	Color selectionColor;
 957	Color multipleSelectionColor;
 958	Color lineHighlightColor;
 959	Color structureHighlightColor;
 960	Color eolMarkerColor;
 961	Color wrapGuideColor;
 962
 963	SyntaxStyle[] foldLineStyle;
 964
 965	boolean blockCaret;
 966	boolean thickCaret;
 967	boolean lineHighlight;
 968	boolean structureHighlight;
 969	boolean eolMarkers;
 970	boolean wrapGuide;
 971	AntiAlias antiAlias;
 972	boolean fracFontMetrics;
 973	RenderingHints renderingHints;
 974	boolean selectionFg;
 975	Color selectionFgColor;
 976	// should try to use this as little as possible.
 977	FontMetrics fm;
 978	int extraLineSpacing;
 979	//}}}
 980
 981	//{{{ TextAreaPainter constructor
 982	/**
 983	 * Creates a new painter. Do not create instances of this class
 984	 * directly.
 985	 */
 986	TextAreaPainter(TextArea textArea)
 987	{
 988		enableEvents(AWTEvent.FOCUS_EVENT_MASK
 989			| AWTEvent.KEY_EVENT_MASK
 990			| AWTEvent.MOUSE_EVENT_MASK);
 991
 992		this.textArea = textArea;
 993		antiAlias = new AntiAlias(0);
 994		extensionMgr = new ExtensionManager();
 995
 996		setAutoscrolls(true);
 997		setOpaque(true);
 998		setRequestFocusEnabled(false);
 999		setDoubleBuffered(false);
1000
1001		setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
1002
1003		fontRenderContext = new FontRenderContext(null,false,false);
1004
1005		addExtension(LINE_BACKGROUND_LAYER,new PaintLineBackground());
1006		addExtension(SELECTION_LAYER,new PaintSelection());
1007		addExtension(WRAP_GUIDE_LAYER,new PaintWrapGuide());
1008		addExtension(BRACKET_HIGHLIGHT_LAYER,new StructureMatcher
1009			.Highlight(textArea));
1010		addExtension(TEXT_LAYER,new PaintText());
1011		addExtension(TEXT_LAYER,new PaintSelectionText());
1012		caretExtension = new PaintCaret();
1013		
1014		extraLineSpacing = 0;
1015	} //}}}
1016
1017	//}}}
1018
1019	//{{{ Private members
1020
1021	//{{{ Instance variables
1022	private final ExtensionManager extensionMgr;
1023	private final PaintCaret caretExtension;
1024	private FontRenderContext fontRenderContext;
1025	private Cursor hiddenCursor;
1026	private boolean defaultCursor = true;
1027	//}}}
1028	
1029	//{{{ updateRenderingHints() method
1030	private void updateRenderingHints()
1031	{
1032		Map<RenderingHints.Key,Object> hints = new HashMap<RenderingHints.Key,Object>();
1033
1034		hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
1035			fracFontMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
1036				: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
1037
1038		hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, antiAlias.renderHint());
1039		
1040
1041		if (antiAlias.val() == 0)
1042		{
1043			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
1044			fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
1045		}
1046		/* Subpixel antialiasing mode */
1047		else if (antiAlias.val() > 1) 
1048		{
1049			Object fontRenderHint = fracFontMetrics ?
1050				RenderingHints.VALUE_FRACTIONALMETRICS_ON :
1051				RenderingHints.VALUE_FRACTIONALMETRICS_OFF;
1052			fontRenderContext = new FontRenderContext(null, antiAlias.renderHint(), 
1053				fontRenderHint);			
1054		}
1055		else /** Standard Antialias Version */
1056		{
1057			hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
1058			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
1059			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1060			fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
1061		}
1062
1063		renderingHints = new RenderingHints(hints);
1064
1065
1066	} //}}}
1067
1068	//}}}
1069
1070	//{{{ Inner classes
1071
1072	//{{{ PaintLineBackground class
1073	private class PaintLineBackground extends TextAreaExtension
1074	{
1075		//{{{ shouldPaintLineHighlight() method
1076		private boolean shouldPaintLineHighlight(int caret, int start, int end)
1077		{
1078			if(!isLineHighlightEnabled()
1079				|| caret < start || caret >= end)
1080			{
1081				return false;
1082			}
1083
1084			int count = textArea.getSelectionCount();
1085			if(count == 1)
1086			{
1087				Selection s = textArea.getSelection(0);
1088				return s.getStartLine() == s.getEndLine();
1089			}
1090			else
1091				return count == 0;
1092		} //}}}
1093
1094		//{{{ paintValidLine() method
1095		@Override
1096		public void paintValidLine(Graphics2D gfx, int screenLine,
1097			int physicalLine, int start, int end, int y)
1098		{
1099			// minimise access$ methods
1100			TextArea textArea = TextAreaPainter.this.textArea;
1101			JEditBuffer buffer = textArea.getBuffer();
1102
1103			//{{{ Paint line highlight and collapsed fold highlight
1104			boolean collapsedFold =
1105				physicalLine < buffer.getLineCount() - 1
1106				&& buffer.isFoldStart(physicalLine)
1107				&& !textArea.displayManager
1108				.isLineVisible(physicalLine + 1);
1109
1110			SyntaxStyle foldLineStyle = null;
1111			if(collapsedFold)
1112			{
1113				int level = buffer.getFoldLevel(physicalLine + 1);
1114				if(buffer.getFoldHandler() instanceof IndentFoldHandler)
1115					level = Math.max(1,level / buffer.getIndentSize());
1116				if(level > 3)
1117					level = 0;
1118				foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
1119			}
1120
1121			int caret = textArea.getCaretPosition();
1122			boolean paintLineHighlight = shouldPaintLineHighlight(
1123				caret,start,end);
1124
1125			Color bgColor;
1126			if(paintLineHighlight)
1127				bgColor = lineHighlightColor;
1128			else if(collapsedFold)
1129			{
1130				bgColor = foldLineStyle.getBackgroundColor();
1131				if(bgColor == null)
1132					bgColor = getBackground();
1133			}
1134			else
1135				bgColor = getBackground();
1136
1137			if(paintLineHighlight || collapsedFold)
1138			{
1139				gfx.setColor(bgColor);
1140				gfx.fillRect(0,y,getWidth(),getLineHeight());
1141			} //}}}
1142
1143			//{{{ Paint token backgrounds
1144			ChunkCache.LineInfo lineInfo = textArea.chunkCache
1145				.getLineInfo(screenLine);
1146
1147			if(lineInfo.chunks != null)
1148			{
1149				float baseLine = y + getLineHeight() - (fm.getLeading()+1) - fm.getDescent();
1150				Chunk.paintChunkBackgrounds(
1151					lineInfo.chunks,gfx,
1152					textArea.getHorizontalOffset(),
1153					baseLine, getLineHeight());
1154			} //}}}
1155		} //}}}
1156	} //}}}
1157
1158	//{{{ PaintSelection class
1159	private class PaintSelection extends TextAreaExtension
1160	{
1161		//{{{ paintValidLine() method
1162		@Override
1163		public void paintValidLine(Graphics2D gfx, int screenLine,
1164			int physicalLine, int start, int end, int y)
1165		{
1166			if(textArea.getSelectionCount() == 0)
1167				return;
1168
1169			gfx.setColor(textArea.isMultipleSelectionEnabled()
1170				? getMultipleSelectionColor()
1171				: getSelectionColor());
1172
1173			Iterator<Selection> iter = textArea.getSelectionIterator();
1174			while(iter.hasNext())
1175			{
1176				Selection s = iter.next();
1177				paintSelection(gfx,screenLine,physicalLine,y,s);
1178			}
1179		} //}}}
1180
1181		//{{{ paintSelection() method
1182		private void paintSelection(Graphics2D gfx, int screenLine,
1183			int physicalLine, int y, Selection s)
1184		{
1185			int[] selectionStartAndEnd
1186				= textArea.selectionManager
1187				.getSelectionStartAndEnd(
1188				screenLine,physicalLine,s);
1189			if(selectionStartAndEnd == null)
1190				return;
1191
1192			int x1 = selectionStartAndEnd[0];
1193			int x2 = selectionStartAndEnd[1];
1194
1195			gfx.fillRect(x1, y, x2 - x1, getLineHeight());
1196		} //}}}
1197	} //}}}
1198
1199	//{{{ PaintSelectionText class
1200	private class PaintSelectionText extends TextAreaExtension
1201	{
1202		// All screen lines of the same physical line use the same indentation
1203		private float indent;
1204		private boolean indentFound = false;
1205
1206		//{{{ paintValidLine() method
1207		@Override
1208		public void paintValidLine(Graphics2D gfx, int screenLine,
1209			int physicalLine, int start, int end, int y)
1210		{
1211			if(textArea.getSelectionCount() == 0)
1212				return;
1213			if ((! isSelectionFgColorEnabled()) || (getSelectionFgColor() == null)) 
1214				return;
1215
1216			Iterator<Selection> iter = textArea.getSelectionIterator();
1217			while(iter.hasNext())
1218			{
1219				Selection s = iter.next();
1220				paintSelection(gfx,screenLine,physicalLine,y,s);
1221			}
1222		} //}}}
1223
1224		//{{{ paintSelection() method
1225		private void paintSelection(Graphics2D gfx, int screenLine,
1226			int physicalLine, int y, Selection s)
1227		{
1228			if ((physicalLine < s.getStartLine()) || (physicalLine > s.getEndLine()))
1229				return;
1230
1231			float x = indent = textArea.getHorizontalOffset();
1232			float baseLine = y + fm.getHeight() -
1233				(fm.getLeading()+1) - fm.getDescent();
1234
1235			DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
1236			textArea.getBuffer().markTokens(physicalLine, tokenHandler);
1237			Token token = tokenHandler.getTokens();
1238
1239			int lineStart = textArea.getLineStartOffset(physicalLine);
1240			int startOffset, endOffset;
1241			if (s instanceof Selection.Rect)
1242			{
1243				Selection.Rect r = (Selection.Rect)s;
1244				startOffset = r.getStart(textArea.getBuffer(), physicalLine);
1245				endOffset = r.getEnd(textArea.getBuffer(), physicalLine);
1246			}
1247			else
1248			{
1249				startOffset = (s.getStart() > lineStart) ? s.getStart() : lineStart;
1250				endOffset = s.getEnd();
1251			}
1252			// Soft-wrap
1253			int screenLineStart = textArea.getScreenLineStartOffset(screenLine);
1254			int screenLineEnd = textArea.getScreenLineEndOffset(screenLine);
1255			if (screenLineStart > startOffset)
1256				startOffset = screenLineStart;
1257			if (screenLineEnd < endOffset)
1258				endOffset = screenLineEnd;
1259			indentFound = false;
1260
1261			int tokenStart = lineStart;
1262			while(token.id != Token.END)
1263			{
1264				int next = tokenStart + token.length;
1265				String sub = null;
1266				SyntaxStyle style = styles[token.id];
1267				if (next > startOffset)	// Reached selection start
1268				{
1269					if (tokenStart >= endOffset)	// Got past selection
1270						break;
1271					if(style != null)
1272					{
1273						gfx.setFont(style.getFont());
1274						gfx.setColor(getSelectionFgColor());
1275						int strStart;
1276						if (startOffset > tokenStart)
1277						{
1278							strStart = startOffset;
1279							x = nextX(x, style, sub, tokenStart, startOffset);
1280						}
1281						else
1282							strStart = tokenStart; 
1283						int strEnd = (endOffset > next) ? next : endOffset;
1284						sub = textArea.getText(strStart, strEnd - strStart);
1285						gfx.drawString(sub, x, baseLine);
1286						x = nextX(x, style, sub, strStart, strEnd);
1287					}
1288				}
1289				if (sub == null)
1290					x = nextX(x, style, null, tokenStart, next);
1291				tokenStart = next;
1292				token = token.next;
1293				if (tokenStart == screenLineStart)
1294					x = indent;
1295			}
1296		} //}}}
1297
1298		//{{{
1299		float nextX(float x, SyntaxStyle style, String s, int startOffset,
1300			int endOffset)
1301		{
1302			if (s == null)
1303				s = textArea.getText(startOffset, endOffset - startOffset);
1304			if (s.equals("\t"))
1305			{
1306				int horzOffset = textArea.getHorizontalOffset();
1307				x = nextTabStop(x - horzOffset, endOffset) + horzOffset;
1308			}
1309			else
1310			{
1311				if ((! indentFound) && (! s.equals(" ")))
1312				{
1313					indentFound = true;
1314					indent = x;
1315				}
1316				Font font = (style != null) ? style.getFont() : getFont();
1317				x += font.getStringBounds(s, getFontRenderContext()).getWidth();
1318			}
1319			return x;
1320		}
1321	} //}}}
1322
1323	//{{{ PaintWrapGuide class
1324	private class PaintWrapGuide extends TextAreaExtension
1325	{
1326		@Override
1327		public void paintScreenLineRange(Graphics2D gfx, int firstLine,
1328			int lastLine, int[] physicalLines, int[] start,
1329			int[] end, int y, int lineHeight)
1330		{
1331			if(textArea.wrapMargin != 0
1332				&& !textArea.wrapToWidth
1333				&& isWrapGuidePainted())
1334			{
1335				gfx.setColor(getWrapGuideColor());
1336				int x = textArea.getHorizontalOffset()
1337					+ textArea.wrapMargin;
1338				gfx.drawLine(x,y,x,y + (lastLine - firstLine
1339					+ 1) * lineHeight);
1340			}
1341		}
1342
1343		@Override
1344		public String getToolTipText(int x, int y)
1345		{
1346			if(textArea.wrapMargin != 0
1347				&& !textArea.wrapToWidth
1348				&& isWrapGuidePainted())
1349			{
1350				int wrapGuidePos = textArea.wrapMargin
1351					+ textArea.getHorizontalOffset();
1352				if(Math.abs(x - wrapGuidePos) < 5)
1353				{
1354					return String.valueOf(textArea.getBuffer()
1355						.getProperty("maxLineLen"));
1356				}
1357			}
1358
1359			return null;
1360		}
1361	} //}}}
1362
1363	//{{{ PaintText class
1364	private class PaintText extends TextAreaExtension
1365	{
1366		@Override
1367		public void paintValidLine(Graphics2D gfx, int screenLine,
1368			int physicalLine, int start, int end, int y)
1369		{
1370			ChunkCache.LineInfo lineInfo = textArea.chunkCache
1371				.getLineInfo(screenLine);
1372			Font defaultFont = getFont();
1373			Color defaultColor = getForeground();
1374
1375			gfx.setFont(defaultFont);
1376			gfx.setColor(defaultColor);
1377
1378			int x = textArea.getHorizontalOffset();
1379			int originalX = x;
1380
1381			float baseLine = y + getLineHeight() - (fm.getLeading()+1) - fm.getDescent();
1382
1383			if(lineInfo.chunks != null)
1384			{
1385				x += Chunk.paintChunkList(lineInfo.chunks,
1386					gfx,textArea.getHorizontalOffset(),
1387					baseLine,!Debug.DISABLE_GLYPH_VECTOR);
1388			}
1389
1390			JEditBuffer buffer = textArea.getBuffer();
1391
1392			if(!lineInfo.lastSubregion)
1393			{
1394				gfx.setFont(defaultFont);
1395				gfx.setColor(eolMarkerColor);
1396				gfx.drawString(":",Math.max(x,
1397					textArea.getHorizontalOffset()
1398					+ textArea.wrapMargin + textArea.charWidth),
1399					baseLine);
1400				x += textArea.charWidth;
1401			}
1402			else if(physicalLine < buffer.getLineCount() - 1
1403				&& buffer.isFoldStart(physicalLine)
1404				&& !textArea.displayManager
1405				.isLineVisible(physicalLine + 1))
1406			{
1407				int level = buffer.getFoldLevel(physicalLine + 1);
1408				if(buffer.getFoldHandler() instanceof IndentFoldHandler)
1409					level = Math.max(1,level / buffer.getIndentSize());
1410				if(level > 3)
1411					level = 0;
1412				SyntaxStyle foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
1413
1414				Font font = foldLineStyle.getFont();
1415				gfx.setFont(font);
1416				gfx.setColor(foldLineStyle.getForegroundColor());
1417
1418				int nextLine;
1419				int nextScreenLine = screenLine + 1;
1420				if(nextScreenLine < textArea.getVisibleLines())
1421				{
1422					nextLine = textArea.chunkCache.getLineInfo(nextScreenLine)
1423						.physicalLine;
1424				}
1425				else
1426				{
1427					nextLine = textArea.displayManager
1428						.getNextVisibleLine(physicalLine);
1429				}
1430
1431				if(nextLine == -1)
1432					nextLine = textArea.getLineCount();
1433
1434				int count = nextLine - physicalLine - 1;
1435				String str = " [" + count + " lines]";
1436
1437				float width = getStringWidth(str);
1438
1439				gfx.drawString(str,x,baseLine);
1440				x += width;
1441			}
1442			else if(eolMarkers)
1443			{
1444				gfx.setFont(defaultFont);
1445				gfx.setColor(eolMarkerColor);
1446				gfx.drawString(".",x,baseLine);
1447				x += textArea.charWidth;
1448			}
1449
1450			lineInfo.width = x - originalX;
1451		}
1452	} //}}}
1453
1454	//{{{ PaintCaret class
1455	private class PaintCaret extends TextAreaExtension
1456	{
1457		@Override
1458		public void paintValidLine(Graphics2D gfx, int screenLine,
1459			int physicalLine, int start, int end, int y)
1460		{
1461			if(!textArea.isCaretVisible())
1462				return;
1463
1464			int caret = textArea.getCaretPosition();
1465			if(caret < start || caret >= end)
1466				return;
1467
1468			int offset = caret - textArea.getLineStartOffset(physicalLine);
1469			textArea.offsetToXY(physicalLine,
1470					    offset, textArea.offsetXY);
1471			int caretX = textArea.offsetXY.x;
1472			int lineHeight = getLineHeight();
1473			int charHeight = getFontHeight();
1474			int charOffset = lineHeight - charHeight;
1475
1476			gfx.setColor(caretColor);
1477
1478			if(textArea.isOverwriteEnabled())
1479			{
1480				gfx.drawLine(caretX, y + lineHeight - 1,
1481					     caretX + textArea.charWidth,
1482					     y + lineHeight - 1);
1483			}
1484			else if(blockCaret)
1485				gfx.drawRect(caretX, y + charOffset, textArea.charWidth - 1, charHeight - 1);
1486			else
1487			{
1488				if (thickCaret)
1489					gfx.drawRect(caretX, y + charOffset, 1, charHeight - 1);
1490				else
1491					gfx.drawLine(caretX, y + charOffset, caretX, 
1492						     y + charOffset + charHeight - 1);
1493			}
1494		}
1495	} //}}}
1496
1497	//}}}
1498
1499	//}}}
1500}