PageRenderTime 207ms CodeModel.GetById 127ms app.highlight 69ms RepoModel.GetById 1ms app.codeStats 1ms

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

#
Java | 1215 lines | 664 code | 148 blank | 403 comment | 90 complexity | b9e9830d1b845c7e1564c1300f0a283a 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;
  28import java.awt.event.MouseEvent;
  29import java.awt.font.*;
  30import java.awt.geom.AffineTransform;
  31import java.awt.*;
  32import java.lang.reflect.Constructor;
  33import java.lang.reflect.Field;
  34import java.util.*;
  35import org.gjt.sp.jedit.buffer.IndentFoldHandler;
  36import org.gjt.sp.jedit.buffer.JEditBuffer;
  37import org.gjt.sp.jedit.syntax.Chunk;
  38import org.gjt.sp.jedit.syntax.SyntaxStyle;
  39import org.gjt.sp.jedit.syntax.Token;
  40import org.gjt.sp.jedit.Debug;
  41
  42import org.gjt.sp.util.Log;
  43//}}}
  44
  45/**
  46 * The text area painter is the component responsible for displaying the
  47 * text of the current buffer. The only methods in this class that should
  48 * be called by plugins are those for adding and removing
  49 * text area extensions.
  50 *
  51 * @see #addExtension(TextAreaExtension)
  52 * @see #addExtension(int,TextAreaExtension)
  53 * @see #removeExtension(TextAreaExtension)
  54 * @see TextAreaExtension
  55 * @see JEditTextArea
  56 *
  57 * @author Slava Pestov
  58 * @version $Id: TextAreaPainter.java 5354 2006-03-03 16:18:06Z ezust $
  59 */
  60public class TextAreaPainter extends JComponent implements TabExpander
  61{
  62	//{{{ Layers
  63	/**
  64	 * The lowest possible layer.
  65	 * @see #addExtension(int,TextAreaExtension)
  66	 * @since jEdit 4.0pre4
  67	 */
  68	public static final int LOWEST_LAYER = Integer.MIN_VALUE;
  69
  70	/**
  71	 * Below selection layer. The JDiff plugin will use this.
  72	 * @see #addExtension(int,TextAreaExtension)
  73	 * @since jEdit 4.0pre4
  74	 */
  75	public static final int BACKGROUND_LAYER = -60;
  76
  77	/**
  78	 * The line highlight and collapsed fold highlight layer.
  79	 * @see #addExtension(int,TextAreaExtension)
  80	 * @since jEdit 4.0pre7
  81	 */
  82	public static final int LINE_BACKGROUND_LAYER = -50;
  83
  84	/**
  85	 * Below selection layer.
  86	 * @see #addExtension(int,TextAreaExtension)
  87	 * @since jEdit 4.0pre4
  88	 */
  89	public static final int BELOW_SELECTION_LAYER = -40;
  90
  91	/**
  92	 * Selection layer. Most extensions will be above this layer, but some
  93	 * (eg, JDiff) will want to be below the selection.
  94	 * @see #addExtension(int,TextAreaExtension)
  95	 * @since jEdit 4.0pre4
  96	 */
  97	public static final int SELECTION_LAYER = -30;
  98
  99	/**
 100	 * Wrap guide layer. Most extensions will be above this layer.
 101	 * @since jEdit 4.0pre4
 102	 */
 103	public static final int WRAP_GUIDE_LAYER = -20;
 104
 105	/**
 106	 * Below most extensions layer.
 107	 * @see #addExtension(int,TextAreaExtension)
 108	 * @since jEdit 4.0pre4
 109	 */
 110	public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
 111
 112	/**
 113	 * Default extension layer. This is above the wrap guide but below the
 114	 * structure highlight.
 115	 * @since jEdit 4.0pre4
 116	 */
 117	public static final int DEFAULT_LAYER = 0;
 118
 119	/**
 120	 * Block caret layer. Most extensions will be below this layer.
 121	 * @since jEdit 4.2pre1
 122	 */
 123	public static final int BLOCK_CARET_LAYER = 50;
 124
 125	/**
 126	 * Bracket highlight layer. Most extensions will be below this layer.
 127	 * @since jEdit 4.0pre4
 128	 */
 129	public static final int BRACKET_HIGHLIGHT_LAYER = 100;
 130
 131	/**
 132	 * Text layer. Most extensions will be below this layer.
 133	 * @since jEdit 4.2pre1
 134	 */
 135	public static final int TEXT_LAYER = 200;
 136
 137	/**
 138	 * Caret layer. Most extensions will be below this layer.
 139	 * @since jEdit 4.2pre1
 140	 */
 141	public static final int CARET_LAYER = 300;
 142
 143	/**
 144	 * Highest possible layer.
 145	 * @since jEdit 4.0pre4
 146	 */
 147	public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
 148	//}}}
 149
 150	//{{{ setBounds() method
 151	/**
 152	 * It is a bad idea to override this, but we need to get the component
 153	 * event before the first repaint.
 154	 */
 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		textArea.repaintMgr.updateGraphics();
 173	} //}}}
 174
 175	//{{{ getFocusTraversalKeysEnabled() method
 176	/**
 177	 * Makes the tab key work in Java 1.4.
 178	 * @since jEdit 3.2pre4
 179	 */
 180	public boolean getFocusTraversalKeysEnabled()
 181	{
 182		return false;
 183	} //}}}
 184
 185	//{{{ Getters and setters
 186
 187	//{{{ getStyles() method
 188	/**
 189	 * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
 190	 * will be used to paint tokens with id = <i>n</i>.
 191	 * @see org.gjt.sp.jedit.syntax.Token
 192	 */
 193	public final SyntaxStyle[] getStyles()
 194	{
 195		return styles;
 196	} //}}}
 197
 198	//{{{ setStyles() method
 199	/**
 200	 * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
 201	 * will be used to paint tokens with id = <i>n</i>.
 202	 * @param styles The syntax styles
 203	 * @see org.gjt.sp.jedit.syntax.Token
 204	 */
 205	public final void setStyles(SyntaxStyle[] styles)
 206	{
 207		// assumed this is called after a font render context is set up.
 208		// changing font render context settings without a setStyles()
 209		// call will not reset cached monospaced font info.
 210		fonts.clear();
 211
 212		this.styles = styles;
 213		styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
 214		repaint();
 215	} //}}}
 216
 217	//{{{ getCaretColor() method
 218	/**
 219	 * Returns the caret color.
 220	 */
 221	public final Color getCaretColor()
 222	{
 223		return caretColor;
 224	} //}}}
 225
 226	//{{{ setCaretColor() method
 227	/**
 228	 * Sets the caret color.
 229	 * @param caretColor The caret color
 230	 */
 231	public final void setCaretColor(Color caretColor)
 232	{
 233		this.caretColor = caretColor;
 234		if(textArea.getBuffer() != null)
 235			textArea.invalidateLine(textArea.getCaretLine());
 236	} //}}}
 237
 238	//{{{ getSelectionColor() method
 239	/**
 240	 * Returns the selection color.
 241	 */
 242	public final Color getSelectionColor()
 243	{
 244		return selectionColor;
 245	} //}}}
 246
 247	//{{{ setSelectionColor() method
 248	/**
 249	 * Sets the selection color.
 250	 * @param selectionColor The selection color
 251	 */
 252	public final void setSelectionColor(Color selectionColor)
 253	{
 254		this.selectionColor = selectionColor;
 255		textArea.repaint();
 256	} //}}}
 257
 258	//{{{ getMultipleSelectionColor() method
 259	/**
 260	 * Returns the multiple selection color.
 261	 * @since jEdit 4.2pre1
 262	 */
 263	public final Color getMultipleSelectionColor()
 264	{
 265		return multipleSelectionColor;
 266	} //}}}
 267
 268	//{{{ setMultipleSelectionColor() method
 269	/**
 270	 * Sets the multiple selection color.
 271	 * @param multipleSelectionColor The multiple selection color
 272	 * @since jEdit 4.2pre1
 273	 */
 274	public final void setMultipleSelectionColor(Color multipleSelectionColor)
 275	{
 276		this.multipleSelectionColor = multipleSelectionColor;
 277		textArea.repaint();
 278	} //}}}
 279
 280	//{{{ getLineHighlightColor() method
 281	/**
 282	 * Returns the line highlight color.
 283	 */
 284	public final Color getLineHighlightColor()
 285	{
 286		return lineHighlightColor;
 287	} //}}}
 288
 289	//{{{ setLineHighlightColor() method
 290	/**
 291	 * Sets the line highlight color.
 292	 * @param lineHighlightColor The line highlight color
 293	 */
 294	public final void setLineHighlightColor(Color lineHighlightColor)
 295	{
 296		this.lineHighlightColor = lineHighlightColor;
 297		if(textArea.getBuffer() != null)
 298			textArea.invalidateLine(textArea.getCaretLine());
 299	} //}}}
 300
 301	//{{{ isLineHighlightEnabled() method
 302	/**
 303	 * Returns true if line highlight is enabled, false otherwise.
 304	 */
 305	public final boolean isLineHighlightEnabled()
 306	{
 307		return lineHighlight;
 308	} //}}}
 309
 310	//{{{ setLineHighlightEnabled() method
 311	/**
 312	 * Enables or disables current line highlighting.
 313	 * @param lineHighlight True if current line highlight should be enabled,
 314	 * false otherwise
 315	 */
 316	public final void setLineHighlightEnabled(boolean lineHighlight)
 317	{
 318		this.lineHighlight = lineHighlight;
 319		textArea.repaint();
 320	} //}}}
 321
 322	//{{{ getStructureHighlightColor() method
 323	/**
 324	 * Returns the structure highlight color.
 325	 * @since jEdit 4.2pre3
 326	 */
 327	public final Color getStructureHighlightColor()
 328	{
 329		return structureHighlightColor;
 330	} //}}}
 331
 332	//{{{ setStructureHighlightColor() method
 333	/**
 334	 * Sets the structure highlight color.
 335	 * @param structureHighlightColor The bracket highlight color
 336	 * @since jEdit 4.2pre3
 337	 */
 338	public final void setStructureHighlightColor(
 339		Color structureHighlightColor)
 340	{
 341		this.structureHighlightColor = structureHighlightColor;
 342		textArea.invalidateStructureMatch();
 343	} //}}}
 344
 345	//{{{ isStructureHighlightEnabled() method
 346	/**
 347	 * Returns true if structure highlighting is enabled, false otherwise.
 348	 * @since jEdit 4.2pre3
 349	 */
 350	public final boolean isStructureHighlightEnabled()
 351	{
 352		return structureHighlight;
 353	} //}}}
 354
 355	//{{{ setStructureHighlightEnabled() method
 356	/**
 357	 * Enables or disables structure highlighting.
 358	 * @param structureHighlight True if structure highlighting should be
 359	 * enabled, false otherwise
 360	 * @since jEdit 4.2pre3
 361	 */
 362	public final void setStructureHighlightEnabled(boolean structureHighlight)
 363	{
 364		this.structureHighlight = structureHighlight;
 365		textArea.invalidateStructureMatch();
 366	} //}}}
 367
 368	//{{{ isBlockCaretEnabled() method
 369	/**
 370	 * Returns true if the caret should be drawn as a block, false otherwise.
 371	 */
 372	public final boolean isBlockCaretEnabled()
 373	{
 374		return blockCaret;
 375	} //}}}
 376
 377	//{{{ setBlockCaretEnabled() method
 378	/**
 379	 * Sets if the caret should be drawn as a block, false otherwise.
 380	 * @param blockCaret True if the caret should be drawn as a block,
 381	 * false otherwise.
 382	 */
 383	public final void setBlockCaretEnabled(boolean blockCaret)
 384	{
 385		this.blockCaret = blockCaret;
 386		extensionMgr.removeExtension(caretExtension);
 387		if(blockCaret)
 388			addExtension(BLOCK_CARET_LAYER,caretExtension);
 389		else
 390			addExtension(CARET_LAYER,caretExtension);
 391		if(textArea.getBuffer() != null)
 392			textArea.invalidateLine(textArea.getCaretLine());
 393	} //}}}
 394
 395	//{{{ getEOLMarkerColor() method
 396	/**
 397	 * Returns the EOL marker color.
 398	 */
 399	public final Color getEOLMarkerColor()
 400	{
 401		return eolMarkerColor;
 402	} //}}}
 403
 404	//{{{ setEOLMarkerColor() method
 405	/**
 406	 * Sets the EOL marker color.
 407	 * @param eolMarkerColor The EOL marker color
 408	 */
 409	public final void setEOLMarkerColor(Color eolMarkerColor)
 410	{
 411		this.eolMarkerColor = eolMarkerColor;
 412		repaint();
 413	} //}}}
 414
 415	//{{{ getEOLMarkersPainted() method
 416	/**
 417	 * Returns true if EOL markers are drawn, false otherwise.
 418	 */
 419	public final boolean getEOLMarkersPainted()
 420	{
 421		return eolMarkers;
 422	} //}}}
 423
 424	//{{{ setEOLMarkersPainted() method
 425	/**
 426	 * Sets if EOL markers are to be drawn.
 427	 * @param eolMarkers True if EOL markers should be drawn, false otherwise
 428	 */
 429	public final void setEOLMarkersPainted(boolean eolMarkers)
 430	{
 431		this.eolMarkers = eolMarkers;
 432		repaint();
 433	} //}}}
 434
 435	//{{{ getWrapGuideColor() method
 436	/**
 437	 * Returns the wrap guide color.
 438	 */
 439	public final Color getWrapGuideColor()
 440	{
 441		return wrapGuideColor;
 442	} //}}}
 443
 444	//{{{ setWrapGuideColor() method
 445	/**
 446	 * Sets the wrap guide color.
 447	 * @param wrapGuideColor The wrap guide color
 448	 */
 449	public final void setWrapGuideColor(Color wrapGuideColor)
 450	{
 451		this.wrapGuideColor = wrapGuideColor;
 452		repaint();
 453	} //}}}
 454
 455	//{{{ isWrapGuidePainted() method
 456	/**
 457	 * Returns true if the wrap guide is drawn, false otherwise.
 458	 * @since jEdit 4.0pre4
 459	 */
 460	public final boolean isWrapGuidePainted()
 461	{
 462		return wrapGuide;
 463	} //}}}
 464
 465	//{{{ setWrapGuidePainted() method
 466	/**
 467	 * Sets if the wrap guide is to be drawn.
 468	 * @param wrapGuide True if the wrap guide should be drawn, false otherwise
 469	 */
 470	public final void setWrapGuidePainted(boolean wrapGuide)
 471	{
 472		this.wrapGuide = wrapGuide;
 473		repaint();
 474	} //}}}
 475
 476	//{{{ getFoldLineStyle() method
 477	/**
 478	 * Returns the fold line style. The first element is the style for
 479	 * lines with a fold level greater than 3. The remaining elements
 480	 * are for fold levels 1 to 3.
 481	 */
 482	public final SyntaxStyle[] getFoldLineStyle()
 483	{
 484		return foldLineStyle;
 485	} //}}}
 486
 487	//{{{ setFoldLineStyle() method
 488	/**
 489	 * Sets the fold line style. The first element is the style for
 490	 * lines with a fold level greater than 3. The remaining elements
 491	 * are for fold levels 1 to 3.
 492	 * @param foldLineStyle The fold line style
 493	 */
 494	public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle)
 495	{
 496		this.foldLineStyle = foldLineStyle;
 497		repaint();
 498	} //}}}
 499
 500	//{{{ setAntiAliasEnabled() method
 501	/**
 502	 * @deprecated use setAntiAlias(AntiAlias newMode)
 503	 */
 504	public void setAntiAliasEnabled(boolean isEnabled) {
 505		
 506		setAntiAlias(new AntiAlias(isEnabled));
 507	}
 508	/**
 509	 * As of jEdit 4.3pre4, a new JDK 1.6 subpixel antialias mode is supported.
 510	 * 
 511	 * @since jEdit 4.2pre4
 512	 */
 513	public void setAntiAlias(AntiAlias newValue)
 514	{
 515		this.antiAlias = newValue;
 516		updateRenderingHints();
 517	} //}}}
 518
 519	/**
 520	 * @return the AntiAlias value that is currently used for TextAreas.
 521	 * @since jedit 4.3pre4
 522	 */
 523	public AntiAlias getAntiAlias() {
 524		return antiAlias;
 525	}
 526	
 527	//{{{ isAntiAliasEnabled() method
 528	/**
 529	 * Returns if anti-aliasing is enabled.
 530	 * @since jEdit 3.2pre6
 531	 * @deprecated - use @ref getAntiAlias()
 532	 */
 533	public boolean isAntiAliasEnabled()
 534	{
 535		return antiAlias.val() > 0;
 536	} //}}}
 537
 538	//{{{ setFractionalFontMetricsEnabled() method
 539	/**
 540	 * Sets if fractional font metrics should be enabled. Has no effect when
 541	 * running on Java 1.1.
 542	 * @since jEdit 3.2pre6
 543	 */
 544	public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
 545	{
 546		this.fracFontMetrics = fracFontMetrics;
 547		updateRenderingHints();
 548	} //}}}
 549
 550	//{{{ isFractionalFontMetricsEnabled() method
 551	/**
 552	 * Returns if fractional font metrics are enabled.
 553	 * @since jEdit 3.2pre6
 554	 */
 555	public boolean isFractionalFontMetricsEnabled()
 556	{
 557		return fracFontMetrics;
 558	} //}}}
 559
 560	//{{{ getFontRenderContext() method
 561	/**
 562	 * Returns the font render context.
 563	 * @since jEdit 4.0pre4
 564	 */
 565	public FontRenderContext getFontRenderContext()
 566	{
 567		return fontRenderContext;
 568	} //}}}
 569
 570	//}}}
 571
 572	//{{{ addExtension() method
 573	/**
 574	 * Adds a text area extension, which can perform custom painting and
 575	 * tool tip handling.
 576	 * @param extension The extension
 577	 * @since jEdit 4.0pre4
 578	 */
 579	public void addExtension(TextAreaExtension extension)
 580	{
 581		extensionMgr.addExtension(DEFAULT_LAYER,extension);
 582		repaint();
 583	} //}}}
 584
 585	//{{{ addExtension() method
 586	/**
 587	 * Adds a text area extension, which can perform custom painting and
 588	 * tool tip handling.
 589	 * @param layer The layer to add the extension to. Note that more than
 590	 * extension can share the same layer.
 591	 * @param extension The extension
 592	 * @since jEdit 4.0pre4
 593	 */
 594	public void addExtension(int layer, TextAreaExtension extension)
 595	{
 596		extensionMgr.addExtension(layer,extension);
 597		repaint();
 598	} //}}}
 599
 600	//{{{ removeExtension() method
 601	/**
 602	 * Removes a text area extension. It will no longer be asked to
 603	 * perform custom painting and tool tip handling.
 604	 * @param extension The extension
 605	 * @since jEdit 4.0pre4
 606	 */
 607	public void removeExtension(TextAreaExtension extension)
 608	{
 609		extensionMgr.removeExtension(extension);
 610		repaint();
 611	} //}}}
 612
 613	//{{{ getExtensions() method
 614	/**
 615	 * Returns an array of registered text area extensions. Useful for
 616	 * debugging purposes.
 617	 * @since jEdit 4.1pre5
 618	 */
 619	public TextAreaExtension[] getExtensions()
 620	{
 621		return extensionMgr.getExtensions();
 622	} //}}}
 623
 624	//{{{ getToolTipText() method
 625	/**
 626	 * Returns the tool tip to display at the specified location.
 627	 * @param evt The mouse event
 628	 */
 629	public String getToolTipText(MouseEvent evt)
 630	{
 631		if(textArea.getBuffer().isLoading())
 632			return null;
 633
 634		return extensionMgr.getToolTipText(evt.getX(),evt.getY());
 635	} //}}}
 636
 637	//{{{ getFontMetrics() method
 638	/**
 639	 * Returns the font metrics used by this component.
 640	 */
 641	public FontMetrics getFontMetrics()
 642	{
 643		return fm;
 644	} //}}}
 645
 646	//{{{ setFont() method
 647	/**
 648	 * Sets the font for this component. This is overridden to update the
 649	 * cached font metrics and to recalculate which lines are visible.
 650	 * @param font The font
 651	 */
 652	public void setFont(Font font)
 653	{
 654		super.setFont(font);
 655		fm = getFontMetrics(font);
 656		textArea.recalculateVisibleLines();
 657		if(textArea.getBuffer() != null
 658			&& !textArea.getBuffer().isLoading())
 659			textArea.recalculateLastPhysicalLine();
 660		textArea.propertiesChanged();
 661	} //}}}
 662
 663	//{{{ getStringWidth() method
 664	/**
 665	 * Returns the width of the given string, in pixels, using the text
 666	 * area's current font.
 667	 *
 668	 * @since jEdit 4.2final
 669	 */
 670	public float getStringWidth(String str)
 671	{
 672		if(textArea.charWidth != 0)
 673			return textArea.charWidth * str.length();
 674		else
 675		{
 676			return (float)getFont().getStringBounds(
 677				str,getFontRenderContext()).getWidth();
 678		}
 679	} //}}}
 680
 681	//{{{ update() method
 682	/**
 683	 * Repaints the text.
 684	 * @param _gfx The graphics context
 685	 */
 686	public void update(Graphics _gfx)
 687	{
 688		paint(_gfx);
 689	} //}}}
 690	
 691	//{{{ paint() method
 692	/**
 693	 * Repaints the text.
 694	 * @param _gfx The graphics context
 695	 */
 696	public void paint(Graphics _gfx)
 697	{
 698		Graphics2D gfx = textArea.repaintMgr.getGraphics();
 699
 700		gfx.setRenderingHints(renderingHints);
 701		fontRenderContext = gfx.getFontRenderContext();
 702
 703		Rectangle clipRect = _gfx.getClipBounds();
 704
 705		JEditBuffer buffer = textArea.getBuffer();
 706		int height = fm.getHeight();
 707		if(height == 0 || buffer.isLoading())
 708		{
 709			_gfx.setColor(getBackground());
 710			_gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
 711		}
 712		else
 713		{
 714			long prepareTime = System.currentTimeMillis();
 715			FastRepaintManager.RepaintLines lines
 716				= textArea.repaintMgr.prepareGraphics(clipRect,
 717				textArea.getFirstLine(),gfx);
 718			prepareTime = (System.currentTimeMillis() - prepareTime);
 719
 720			long linesTime = System.currentTimeMillis();
 721			int numLines = (lines.last - lines.first + 1);
 722
 723			int y = lines.first * height;
 724			gfx.fillRect(0,y,getWidth(),numLines * height);
 725
 726			extensionMgr.paintScreenLineRange(textArea,gfx,
 727				lines.first,lines.last,y,height);
 728			linesTime = (System.currentTimeMillis() - linesTime);
 729
 730			textArea.repaintMgr.setFastScroll(
 731				clipRect.equals(new Rectangle(0,0,
 732				getWidth(),getHeight())));
 733
 734			long blitTime = System.currentTimeMillis();
 735			textArea.repaintMgr.paint(_gfx);
 736			blitTime = (System.currentTimeMillis() - blitTime);
 737
 738			if(Debug.PAINT_TIMER && numLines >= 1)
 739				Log.log(Log.DEBUG,this,"repainting " + numLines + " lines took " + prepareTime + "/" + linesTime + "/" + blitTime + " ms");
 740		}
 741
 742		textArea.updateMaxHorizontalScrollWidth();
 743	} //}}}
 744
 745	//{{{ nextTabStop() method
 746	/**
 747	 * Implementation of TabExpander interface. Returns next tab stop after
 748	 * a specified point.
 749	 * @param x The x co-ordinate
 750	 * @param tabOffset Ignored
 751	 * @return The next tab stop after <i>x</i>
 752	 */
 753	public float nextTabStop(float x, int tabOffset)
 754	{
 755		int ntabs = (int)(x / textArea.tabSize);
 756		return (ntabs + 1) * textArea.tabSize;
 757	} //}}}
 758
 759	//{{{ getPreferredSize() method
 760	/**
 761	 * Returns the painter's preferred size.
 762	 */
 763	public Dimension getPreferredSize()
 764	{
 765		Dimension dim = new Dimension();
 766
 767		char[] foo = new char[80];
 768		for(int i = 0; i < foo.length; i++)
 769			foo[i] = ' ';
 770		dim.width = (int)getStringWidth(new String(foo));
 771		dim.height = fm.getHeight() * 25;
 772		return dim;
 773	} //}}}
 774
 775	//{{{ getMinimumSize() method
 776	/**
 777	 * Returns the painter's minimum size.
 778	 */
 779	public Dimension getMinimumSize()
 780	{
 781		return getPreferredSize();
 782	} //}}}
 783
 784	//{{{ Package-private members
 785
 786	//{{{ Instance variables
 787	/* package-private since they are accessed by inner classes and we
 788	 * want this to be fast */
 789	JEditTextArea textArea;
 790
 791	SyntaxStyle[] styles;
 792	Color caretColor;
 793	Color selectionColor;
 794	Color multipleSelectionColor;
 795	Color lineHighlightColor;
 796	Color structureHighlightColor;
 797	Color eolMarkerColor;
 798	Color wrapGuideColor;
 799
 800	SyntaxStyle[] foldLineStyle;
 801
 802	boolean blockCaret;
 803	boolean lineHighlight;
 804	boolean structureHighlight;
 805	boolean eolMarkers;
 806	boolean wrapGuide;
 807	AntiAlias antiAlias;
 808	boolean fracFontMetrics;
 809
 810	// should try to use this as little as possible.
 811	FontMetrics fm;
 812	//}}}
 813
 814	//{{{ TextAreaPainter constructor
 815	/**
 816	 * Creates a new painter. Do not create instances of this class
 817	 * directly.
 818	 */
 819	TextAreaPainter(JEditTextArea textArea)
 820	{
 821		enableEvents(AWTEvent.FOCUS_EVENT_MASK
 822			| AWTEvent.KEY_EVENT_MASK
 823			| AWTEvent.MOUSE_EVENT_MASK);
 824
 825		this.textArea = textArea;
 826		antiAlias = AntiAlias.textArea();
 827		fonts = new HashMap();
 828		extensionMgr = new ExtensionManager();
 829
 830		setAutoscrolls(true);
 831		setOpaque(true);
 832		setRequestFocusEnabled(false);
 833		setDoubleBuffered(false);
 834
 835		setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
 836
 837		fontRenderContext = new FontRenderContext(null,false,false);
 838
 839		addExtension(LINE_BACKGROUND_LAYER,new PaintLineBackground());
 840		addExtension(SELECTION_LAYER,new PaintSelection());
 841		addExtension(WRAP_GUIDE_LAYER,new PaintWrapGuide());
 842		addExtension(BRACKET_HIGHLIGHT_LAYER,new StructureMatcher
 843			.Highlight(textArea));
 844		addExtension(TEXT_LAYER,new PaintText());
 845		caretExtension = new PaintCaret();
 846	} //}}}
 847
 848	//}}}
 849
 850	//{{{ Private members
 851
 852	//{{{ Instance variables
 853	private ExtensionManager extensionMgr;
 854	private PaintCaret caretExtension;
 855	private RenderingHints renderingHints;
 856	private FontRenderContext fontRenderContext;
 857	private HashMap fonts;
 858	//}}}
 859
 860	private static Object sm_hrgbRender = null;
 861	private static Constructor sm_frcConstructor = null;
 862	
 863	static 
 864	{
 865			try
 866			{
 867				Field f = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_HRGB");
 868				sm_hrgbRender = f.get(null);
 869				Class[] fracFontMetricsTypeList = new Class[] {AffineTransform.class, Object.class, Object.class};
 870				sm_frcConstructor = FontRenderContext.class.getConstructor(fracFontMetricsTypeList);
 871			}
 872			catch (NullPointerException npe) {}
 873			catch (SecurityException se) {}
 874			catch (NoSuchFieldException nsfe) {}
 875			catch (IllegalArgumentException iae) {}
 876			catch (IllegalAccessException iae) {}
 877			catch (NoSuchMethodException nsme) {}
 878	}
 879	//{{{ updateRenderingHints() method
 880	private void updateRenderingHints()
 881	{
 882		HashMap hints = new HashMap();
 883
 884		hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
 885			fracFontMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
 886				: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
 887
 888		if (antiAlias.val() == 0) {
 889			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
 890			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
 891		}
 892		/** LCD HRGB mode - works with JRE 1.6 only, which is why we use reflection */
 893		else if ((antiAlias.val() == 2) && (sm_hrgbRender != null )) 
 894		{ 
 895			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, sm_hrgbRender);
 896			Object fontRenderHint = fracFontMetrics ? 
 897				RenderingHints.VALUE_FRACTIONALMETRICS_ON :
 898				RenderingHints.VALUE_FRACTIONALMETRICS_OFF;
 899			Object[] paramList = new Object[] {null, sm_hrgbRender, fontRenderHint};
 900			try 
 901			{
 902				fontRenderContext = (FontRenderContext) sm_frcConstructor.newInstance(paramList);
 903			}
 904			catch (Exception e) 
 905			{
 906				fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
 907			}
 908		}
 909		else /** Standard Antialias Version */  
 910		{
 911			hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
 912			hints.put(RenderingHints.KEY_ANTIALIASING,  RenderingHints.VALUE_ANTIALIAS_ON);
 913			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 914			fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
 915		} 
 916
 917		renderingHints = new RenderingHints(hints);
 918	       
 919		
 920	} //}}}
 921
 922	//}}}
 923
 924	//{{{ Inner classes
 925
 926	//{{{ PaintLineBackground class
 927	class PaintLineBackground extends TextAreaExtension
 928	{
 929		//{{{ shouldPaintLineHighlight() method
 930		private boolean shouldPaintLineHighlight(int caret, int start, int end)
 931		{
 932			if(!isLineHighlightEnabled()
 933				|| caret < start || caret >= end)
 934			{
 935				return false;
 936			}
 937
 938			int count = textArea.getSelectionCount();
 939			if(count == 1)
 940			{
 941				Selection s = textArea.getSelection(0);
 942				return s.getStartLine() == s.getEndLine();
 943			}
 944			else
 945				return (count == 0);
 946		} //}}}
 947		
 948		//{{{ paintValidLine() method
 949		public void paintValidLine(Graphics2D gfx, int screenLine,
 950			int physicalLine, int start, int end, int y)
 951		{
 952			// minimise access$ methods
 953			JEditTextArea textArea = TextAreaPainter.this.textArea;
 954			JEditBuffer buffer = textArea.getBuffer();
 955
 956			//{{{ Paint line highlight and collapsed fold highlight
 957			boolean collapsedFold =
 958				(physicalLine < buffer.getLineCount() - 1
 959				&& buffer.isFoldStart(physicalLine)
 960				&& !textArea.displayManager
 961				.isLineVisible(physicalLine + 1));
 962
 963			SyntaxStyle foldLineStyle = null;
 964			if(collapsedFold)
 965			{
 966				int level = buffer.getFoldLevel(physicalLine + 1);
 967				if(buffer.getFoldHandler() instanceof IndentFoldHandler)
 968					level = Math.max(1,level / buffer.getIndentSize());
 969				if(level > 3)
 970					level = 0;
 971				foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
 972			}
 973
 974			int caret = textArea.getCaretPosition();
 975			boolean paintLineHighlight = shouldPaintLineHighlight(
 976				caret,start,end);
 977
 978			Color bgColor;
 979			if(paintLineHighlight)
 980				bgColor = lineHighlightColor;
 981			else if(collapsedFold)
 982			{
 983				bgColor = foldLineStyle.getBackgroundColor();
 984				if(bgColor == null)
 985					bgColor = getBackground();
 986			}
 987			else
 988				bgColor = getBackground();
 989
 990			if(paintLineHighlight || collapsedFold)
 991			{
 992				gfx.setColor(bgColor);
 993				gfx.fillRect(0,y,getWidth(),fm.getHeight());
 994			} //}}}
 995
 996			//{{{ Paint token backgrounds
 997			ChunkCache.LineInfo lineInfo = textArea.chunkCache
 998				.getLineInfo(screenLine);
 999
1000			if(lineInfo.chunks != null)
1001			{
1002				float baseLine = y + fm.getHeight()
1003					- fm.getLeading() - fm.getDescent();
1004				Chunk.paintChunkBackgrounds(
1005					lineInfo.chunks,gfx,
1006					textArea.getHorizontalOffset(),
1007					baseLine);
1008			} //}}}
1009		} //}}}
1010	} //}}}
1011
1012	//{{{ PaintSelection class
1013	class PaintSelection extends TextAreaExtension
1014	{
1015		//{{{ paintValidLine() method
1016		public void paintValidLine(Graphics2D gfx, int screenLine,
1017			int physicalLine, int start, int end, int y)
1018		{
1019			if(textArea.getSelectionCount() == 0)
1020				return;
1021
1022			gfx.setColor(textArea.isMultipleSelectionEnabled()
1023				? getMultipleSelectionColor()
1024				: getSelectionColor());
1025
1026			Iterator iter = textArea.getSelectionIterator();
1027			while(iter.hasNext())
1028			{
1029				Selection s = (Selection)iter.next();
1030				paintSelection(gfx,screenLine,physicalLine,y,s);
1031			}
1032		} //}}}
1033
1034		//{{{ paintSelection() method
1035		private void paintSelection(Graphics2D gfx, int screenLine,
1036			int physicalLine, int y, Selection s)
1037		{
1038			int[] selectionStartAndEnd
1039				= textArea.selectionManager
1040				.getSelectionStartAndEnd(
1041				screenLine,physicalLine,s);
1042			if(selectionStartAndEnd == null)
1043				return;
1044
1045			int x1 = selectionStartAndEnd[0];
1046			int x2 = selectionStartAndEnd[1];
1047
1048			gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
1049		} //}}}
1050	} //}}}
1051
1052	//{{{ PaintWrapGuide class
1053	class PaintWrapGuide extends TextAreaExtension
1054	{
1055		public void paintScreenLineRange(Graphics2D gfx, int firstLine,
1056			int lastLine, int[] physicalLines, int[] start,
1057			int[] end, int y, int lineHeight)
1058		{
1059			if(textArea.wrapMargin != 0
1060				&& !textArea.wrapToWidth
1061				&& isWrapGuidePainted())
1062			{
1063				gfx.setColor(getWrapGuideColor());
1064				int x = textArea.getHorizontalOffset()
1065					+ textArea.wrapMargin;
1066				gfx.drawLine(x,y,x,y + (lastLine - firstLine
1067					+ 1) * lineHeight);
1068			}
1069		}
1070
1071		public String getToolTipText(int x, int y)
1072		{
1073			if(textArea.wrapMargin != 0
1074				&& !textArea.wrapToWidth
1075				&& isWrapGuidePainted())
1076			{
1077				int wrapGuidePos = textArea.wrapMargin
1078					+ textArea.getHorizontalOffset();
1079				if(Math.abs(x - wrapGuidePos) < 5)
1080				{
1081					return String.valueOf(textArea.getBuffer()
1082						.getProperty("maxLineLen"));
1083				}
1084			}
1085
1086			return null;
1087		}
1088	} //}}}
1089
1090	//{{{ PaintText class
1091	class PaintText extends TextAreaExtension
1092	{
1093		public void paintValidLine(Graphics2D gfx, int screenLine,
1094			int physicalLine, int start, int end, int y)
1095		{
1096			ChunkCache.LineInfo lineInfo = textArea.chunkCache
1097				.getLineInfo(screenLine);
1098
1099			Font defaultFont = getFont();
1100			Color defaultColor = getForeground();
1101
1102			gfx.setFont(defaultFont);
1103			gfx.setColor(defaultColor);
1104
1105			int x = textArea.getHorizontalOffset();
1106			int originalX = x;
1107
1108			float baseLine = y + fm.getHeight()
1109				- fm.getLeading() - fm.getDescent();
1110
1111			if(lineInfo.chunks != null)
1112			{
1113				x += Chunk.paintChunkList(lineInfo.chunks,
1114					gfx,textArea.getHorizontalOffset(),
1115					baseLine,!Debug.DISABLE_GLYPH_VECTOR);
1116			}
1117
1118			JEditBuffer buffer = textArea.getBuffer();
1119
1120			if(!lineInfo.lastSubregion)
1121			{
1122				gfx.setFont(defaultFont);
1123				gfx.setColor(eolMarkerColor);
1124				gfx.drawString(":",Math.max(x,
1125					textArea.getHorizontalOffset()
1126					+ textArea.wrapMargin + textArea.charWidth),
1127					baseLine);
1128				x += textArea.charWidth;
1129			}
1130			else if(physicalLine < buffer.getLineCount() - 1
1131				&& buffer.isFoldStart(physicalLine)
1132				&& !textArea.displayManager
1133				.isLineVisible(physicalLine + 1))
1134			{
1135				int level = buffer.getFoldLevel(physicalLine + 1);
1136				if(buffer.getFoldHandler() instanceof IndentFoldHandler)
1137					level = Math.max(1,level / buffer.getIndentSize());
1138				if(level > 3)
1139					level = 0;
1140				SyntaxStyle foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
1141
1142				Font font = foldLineStyle.getFont();
1143				gfx.setFont(font);
1144				gfx.setColor(foldLineStyle.getForegroundColor());
1145
1146				int nextLine;
1147				int nextScreenLine = screenLine + 1;
1148				if(nextScreenLine < textArea.getVisibleLines())
1149				{
1150					nextLine = textArea.chunkCache.getLineInfo(nextScreenLine)
1151						.physicalLine;
1152				}
1153				else
1154				{
1155					nextLine = textArea.displayManager
1156						.getNextVisibleLine(physicalLine);
1157				}
1158
1159				if(nextLine == -1)
1160					nextLine = textArea.getLineCount();
1161
1162				int count = nextLine - physicalLine - 1;
1163				String str = " [" + count + " lines]";
1164
1165				float width = getStringWidth(str);
1166
1167				gfx.drawString(str,x,baseLine);
1168				x += width;
1169			}
1170			else if(eolMarkers)
1171			{
1172				gfx.setFont(defaultFont);
1173				gfx.setColor(eolMarkerColor);
1174				gfx.drawString(".",x,baseLine);
1175				x += textArea.charWidth;
1176			}
1177
1178			lineInfo.width = (x - originalX);
1179		}
1180	} //}}}
1181
1182	//{{{ PaintCaret class
1183	class PaintCaret extends TextAreaExtension
1184	{
1185		public void paintValidLine(Graphics2D gfx, int screenLine,
1186			int physicalLine, int start, int end, int y)
1187		{
1188			if(!textArea.isCaretVisible())
1189				return;
1190
1191			int caret = textArea.getCaretPosition();
1192			if(caret < start || caret >= end)
1193				return;
1194
1195			int offset = caret - textArea.getLineStartOffset(physicalLine);
1196			textArea.offsetToXY(physicalLine, offset, textArea.offsetXY);
1197			int caretX = textArea.offsetXY.x;
1198			int height = fm.getHeight();
1199
1200			gfx.setColor(caretColor);
1201
1202			if(textArea.isOverwriteEnabled())
1203			{
1204				gfx.drawLine(caretX,y + height - 1,
1205					caretX + textArea.charWidth,y + height - 1);
1206			}
1207			else if(blockCaret)
1208				gfx.drawRect(caretX,y,textArea.charWidth - 1,height - 1);
1209			else
1210				gfx.drawLine(caretX,y,caretX,y + height - 1);
1211		}
1212	} //}}}
1213
1214	//}}}
1215}