PageRenderTime 59ms CodeModel.GetById 26ms app.highlight 25ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
Java | 1050 lines | 580 code | 130 blank | 340 comment | 89 complexity | 5213ca126863237a3bdeb8afb670bbb9 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
   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, 2000, 2001, 2002 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.*;
  31import java.awt.*;
  32import java.util.ArrayList;
  33import java.util.HashMap;
  34import org.gjt.sp.jedit.syntax.*;
  35import org.gjt.sp.jedit.Buffer;
  36import org.gjt.sp.util.Log;
  37//}}}
  38
  39/**
  40 * The text area painter is the component responsible for displaying the
  41 * text of the current buffer. The only methods in this class that should
  42 * be called by plugins are those for adding and removing
  43 * text area extensions.
  44 *
  45 * @see #addExtension(TextAreaExtension)
  46 * @see #addExtension(int,TextAreaExtension)
  47 * @see #removeExtension(TextAreaExtension)
  48 * @see TextAreaExtension
  49 * @see JEditTextArea
  50 *
  51 * @author Slava Pestov
  52 * @version $Id: TextAreaPainter.java 4012 2002-02-05 06:28:10Z spestov $
  53 */
  54public class TextAreaPainter extends JComponent implements TabExpander
  55{
  56	//{{{ Layers
  57	/**
  58	 * The lowest possible layer.
  59	 * @see #addExtension(int,TextAreaExtension)
  60	 * @since jEdit 4.0pre4
  61	 */
  62	public static final int LOWEST_LAYER = Integer.MIN_VALUE;
  63
  64	/**
  65	 * Below selection layer. The JDiff plugin will use this.
  66	 * @see #addExtension(int,TextAreaExtension)
  67	 * @since jEdit 4.0pre4
  68	 */
  69	public static final int BELOW_SELECTION_LAYER = -40;
  70
  71	/**
  72	 * Selection layer. Most extensions will be above this layer, but some
  73	 * (eg, JDiff) will want to be below the selection.
  74	 * @see #addExtension(int,TextAreaExtension)
  75	 * @since jEdit 4.0pre4
  76	 */
  77	public static final int SELECTION_LAYER = -30;
  78
  79	/**
  80	 * Wrap guide layer. Most extensions will be above this layer.
  81	 * @since jEdit 4.0pre4
  82	 */
  83	public static final int WRAP_GUIDE_LAYER = -20;
  84
  85	/**
  86	 * Below most extensions layer.
  87	 * @see #addExtension(int,TextAreaExtension)
  88	 * @since jEdit 4.0pre4
  89	 */
  90	public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;
  91
  92	/**
  93	 * Default extension layer. This is above the wrap guide but below the
  94	 * bracket highlight.
  95	 * @since jEdit 4.0pre4
  96	 */
  97	public static final int DEFAULT_LAYER = 0;
  98
  99	/**
 100	 * Bracket highlight layer. Most extensions will be below this layer.
 101	 * @since jEdit 4.0pre4
 102	 */
 103	public static final int BRACKET_HIGHLIGHT_LAYER = 100;
 104
 105	/**
 106	 * Highest possible layer.
 107	 * @since jEdit 4.0pre4
 108	 */
 109	public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
 110	//}}}
 111
 112	//{{{ TextAreaPainter constructor
 113	/**
 114	 * Creates a new painter. Do not create instances of this class
 115	 * directly.
 116	 */
 117	public TextAreaPainter(JEditTextArea textArea)
 118	{
 119		enableEvents(AWTEvent.FOCUS_EVENT_MASK
 120			| AWTEvent.KEY_EVENT_MASK
 121			| AWTEvent.MOUSE_EVENT_MASK);
 122
 123		this.textArea = textArea;
 124
 125		extensionMgr = new ExtensionManager();
 126
 127		setAutoscrolls(true);
 128		setOpaque(true);
 129
 130		setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
 131
 132		fontRenderContext = new FontRenderContext(null,false,false);
 133
 134		addExtension(SELECTION_LAYER,new PaintSelection());
 135		addExtension(WRAP_GUIDE_LAYER,new WrapGuide());
 136		addExtension(BRACKET_HIGHLIGHT_LAYER,new BracketHighlight());
 137	} //}}}
 138
 139	//{{{ isManagingFocus() method
 140	/**
 141	 * Returns if this component can be traversed by pressing the
 142	 * Tab key. This returns false.
 143	 */
 144	public boolean isManagingFocus()
 145	{
 146		return false;
 147	} //}}}
 148
 149	//{{{ getFocusTraversalKeysEnabled() method
 150	/**
 151	 * Makes the tab key work in Java 1.4.
 152	 * @since jEdit 3.2pre4
 153	 */
 154	public boolean getFocusTraversalKeysEnabled()
 155	{
 156		return false;
 157	} //}}}
 158
 159	//{{{ Getters and setters
 160
 161	//{{{ getStyles() method
 162	/**
 163	 * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
 164	 * will be used to paint tokens with id = <i>n</i>.
 165	 * @see org.gjt.sp.jedit.syntax.Token
 166	 */
 167	public final SyntaxStyle[] getStyles()
 168	{
 169		return styles;
 170	} //}}}
 171
 172	//{{{ setStyles() method
 173	/**
 174	 * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
 175	 * will be used to paint tokens with id = <i>n</i>.
 176	 * @param styles The syntax styles
 177	 * @see org.gjt.sp.jedit.syntax.Token
 178	 */
 179	public final void setStyles(SyntaxStyle[] styles)
 180	{
 181		this.styles = styles;
 182		styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
 183		repaint();
 184	} //}}}
 185
 186	//{{{ getCaretColor() method
 187	/**
 188	 * Returns the caret color.
 189	 */
 190	public final Color getCaretColor()
 191	{
 192		return caretColor;
 193	} //}}}
 194
 195	//{{{ setCaretColor() method
 196	/**
 197	 * Sets the caret color.
 198	 * @param caretColor The caret color
 199	 */
 200	public final void setCaretColor(Color caretColor)
 201	{
 202		this.caretColor = caretColor;
 203		if(textArea.getBuffer() != null)
 204			textArea.invalidateLine(textArea.getCaretLine());
 205	} //}}}
 206
 207	//{{{ getSelectionColor() method
 208	/**
 209	 * Returns the selection color.
 210	 */
 211	public final Color getSelectionColor()
 212	{
 213		return selectionColor;
 214	} //}}}
 215
 216	//{{{ setSelectionColor() method
 217	/**
 218	 * Sets the selection color.
 219	 * @param selectionColor The selection color
 220	 */
 221	public final void setSelectionColor(Color selectionColor)
 222	{
 223		this.selectionColor = selectionColor;
 224		if(textArea.getBuffer() != null)
 225			textArea.invalidateSelectedLines();
 226	} //}}}
 227
 228	//{{{ getLineHighlightColor() method
 229	/**
 230	 * Returns the line highlight color.
 231	 */
 232	public final Color getLineHighlightColor()
 233	{
 234		return lineHighlightColor;
 235	} //}}}
 236
 237	//{{{ setLineHighlightColor() method
 238	/**
 239	 * Sets the line highlight color.
 240	 * @param lineHighlightColor The line highlight color
 241	 */
 242	public final void setLineHighlightColor(Color lineHighlightColor)
 243	{
 244		this.lineHighlightColor = lineHighlightColor;
 245		if(textArea.getBuffer() != null)
 246			textArea.invalidateLine(textArea.getCaretLine());
 247	} //}}}
 248
 249	//{{{ isLineHighlightEnabled() method
 250	/**
 251	 * Returns true if line highlight is enabled, false otherwise.
 252	 */
 253	public final boolean isLineHighlightEnabled()
 254	{
 255		return lineHighlight;
 256	} //}}}
 257
 258	//{{{ setLineHighlightEnabled() method
 259	/**
 260	 * Enables or disables current line highlighting.
 261	 * @param lineHighlight True if current line highlight should be enabled,
 262	 * false otherwise
 263	 */
 264	public final void setLineHighlightEnabled(boolean lineHighlight)
 265	{
 266		this.lineHighlight = lineHighlight;
 267		if(textArea.getBuffer() != null)
 268			textArea.invalidateSelectedLines();
 269	} //}}}
 270
 271	//{{{ getFoldedLineColor() method
 272	/**
 273	 * Returns the background color of a collapsed fold line.
 274	 */
 275	public final Color getFoldedLineColor()
 276	{
 277		return foldedLineColor;
 278	} //}}}
 279
 280	//{{{ setFoldedLineColor() method
 281	/**
 282	 * Sets the background color of a collapsed fold line.
 283	 * @param foldedLineColor The folded line color
 284	 */
 285	public final void setFoldedLineColor(Color foldedLineColor)
 286	{
 287		this.foldedLineColor = foldedLineColor;
 288		repaint();
 289	} //}}}
 290
 291	//{{{ getBracketHighlightColor() method
 292	/**
 293	 * Returns the bracket highlight color.
 294	 */
 295	public final Color getBracketHighlightColor()
 296	{
 297		return bracketHighlightColor;
 298	} //}}}
 299
 300	//{{{ setBracketHighlightColor() method
 301	/**
 302	 * Sets the bracket highlight color.
 303	 * @param bracketHighlightColor The bracket highlight color
 304	 */
 305	public final void setBracketHighlightColor(Color bracketHighlightColor)
 306	{
 307		this.bracketHighlightColor = bracketHighlightColor;
 308		if(textArea.getBuffer() != null)
 309			textArea.invalidateLine(textArea.getBracketLine());
 310	} //}}}
 311
 312	//{{{ isBracketHighlightEnabled() method
 313	/**
 314	 * Returns true if bracket highlighting is enabled, false otherwise.
 315	 * When bracket highlighting is enabled, the bracket matching the
 316	 * one before the caret (if any) is highlighted.
 317	 */
 318	public final boolean isBracketHighlightEnabled()
 319	{
 320		return bracketHighlight;
 321	} //}}}
 322
 323	//{{{ setBracketHighlightEnabled() method
 324	/**
 325	 * Enables or disables bracket highlighting.
 326	 * When bracket highlighting is enabled, the bracket matching the
 327	 * one before the caret (if any) is highlighted.
 328	 * @param bracketHighlight True if bracket highlighting should be
 329	 * enabled, false otherwise
 330	 */
 331	public final void setBracketHighlightEnabled(boolean bracketHighlight)
 332	{
 333		this.bracketHighlight = bracketHighlight;
 334		if(textArea.getBuffer() != null)
 335			textArea.invalidateLine(textArea.getBracketLine());
 336	} //}}}
 337
 338	//{{{ isBlockCaretEnabled() method
 339	/**
 340	 * Returns true if the caret should be drawn as a block, false otherwise.
 341	 */
 342	public final boolean isBlockCaretEnabled()
 343	{
 344		return blockCaret;
 345	} //}}}
 346
 347	//{{{ setBlockCaretEnabled() method
 348	/**
 349	 * Sets if the caret should be drawn as a block, false otherwise.
 350	 * @param blockCaret True if the caret should be drawn as a block,
 351	 * false otherwise.
 352	 */
 353	public final void setBlockCaretEnabled(boolean blockCaret)
 354	{
 355		this.blockCaret = blockCaret;
 356		if(textArea.getBuffer() != null)
 357			textArea.invalidateLine(textArea.getCaretLine());
 358	} //}}}
 359
 360	//{{{ getEOLMarkerColor() method
 361	/**
 362	 * Returns the EOL marker color.
 363	 */
 364	public final Color getEOLMarkerColor()
 365	{
 366		return eolMarkerColor;
 367	} //}}}
 368
 369	//{{{ setEOLMarkerColor() method
 370	/**
 371	 * Sets the EOL marker color.
 372	 * @param eolMarkerColor The EOL marker color
 373	 */
 374	public final void setEOLMarkerColor(Color eolMarkerColor)
 375	{
 376		this.eolMarkerColor = eolMarkerColor;
 377		repaint();
 378	} //}}}
 379
 380	//{{{ getEOLMarkersPainted() method
 381	/**
 382	 * Returns true if EOL markers are drawn, false otherwise.
 383	 */
 384	public final boolean getEOLMarkersPainted()
 385	{
 386		return eolMarkers;
 387	} //}}}
 388
 389	//{{{ setEOLMarkersPainted() method
 390	/**
 391	 * Sets if EOL markers are to be drawn.
 392	 * @param eolMarkers True if EOL markers should be drawn, false otherwise
 393	 */
 394	public final void setEOLMarkersPainted(boolean eolMarkers)
 395	{
 396		this.eolMarkers = eolMarkers;
 397		repaint();
 398	} //}}}
 399
 400	//{{{ getWrapGuideColor() method
 401	/**
 402	 * Returns the wrap guide color.
 403	 */
 404	public final Color getWrapGuideColor()
 405	{
 406		return wrapGuideColor;
 407	} //}}}
 408
 409	//{{{ setWrapGuideColor() method
 410	/**
 411	 * Sets the wrap guide color.
 412	 * @param wrapGuideColor The wrap guide color
 413	 */
 414	public final void setWrapGuideColor(Color wrapGuideColor)
 415	{
 416		this.wrapGuideColor = wrapGuideColor;
 417		repaint();
 418	} //}}}
 419
 420	//{{{ isWrapGuidePainted() method
 421	/**
 422	 * Returns true if the wrap guide is drawn, false otherwise.
 423	 * @since jEdit 4.0pre4
 424	 */
 425	public final boolean isWrapGuidePainted()
 426	{
 427		return wrapGuide;
 428	} //}}}
 429
 430	//{{{ setWrapGuidePainted() method
 431	/**
 432	 * Sets if the wrap guide is to be drawn.
 433	 * @param wrapGuide True if the wrap guide should be drawn, false otherwise
 434	 */
 435	public final void setWrapGuidePainted(boolean wrapGuide)
 436	{
 437		this.wrapGuide = wrapGuide;
 438		repaint();
 439	} //}}}
 440
 441	//{{{ setAntiAliasEnabled() method
 442	/**
 443	 * Sets if anti-aliasing should be enabled. Has no effect when
 444	 * running on Java 1.1.
 445	 * @since jEdit 3.2pre6
 446	 */
 447	public void setAntiAliasEnabled(boolean antiAlias)
 448	{
 449		this.antiAlias = antiAlias;
 450		updateRenderingHints();
 451	} //}}}
 452
 453	//{{{ isAntiAliasEnabled() method
 454	/**
 455	 * Returns if anti-aliasing is enabled.
 456	 * @since jEdit 3.2pre6
 457	 */
 458	public boolean isAntiAliasEnabled()
 459	{
 460		return antiAlias;
 461	} //}}}
 462
 463	//{{{ setFractionalFontMetricsEnabled() method
 464	/**
 465	 * Sets if fractional font metrics should be enabled. Has no effect when
 466	 * running on Java 1.1.
 467	 * @since jEdit 3.2pre6
 468	 */
 469	public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
 470	{
 471		this.fracFontMetrics = fracFontMetrics;
 472		updateRenderingHints();
 473	} //}}}
 474
 475	//{{{ isFractionalFontMetricsEnabled() method
 476	/**
 477	 * Returns if fractional font metrics are enabled.
 478	 * @since jEdit 3.2pre6
 479	 */
 480	public boolean isFractionalFontMetricsEnabled()
 481	{
 482		return fracFontMetrics;
 483	} //}}}
 484
 485	//{{{ getFontRenderContext() method
 486	/**
 487	 * Returns the font render context.
 488	 * @since jEdit 4.0pre4
 489	 */
 490	public FontRenderContext getFontRenderContext()
 491	{
 492		return fontRenderContext;
 493	} //}}}
 494
 495	//}}}
 496
 497	//{{{ addCustomHighlight() method
 498	/**
 499	 * @deprecated Write a <code>TextAreaExtension</code> instead.
 500	 */
 501	public void addCustomHighlight(TextAreaHighlight highlight)
 502	{
 503		Log.log(Log.WARNING,this,"Old highlighter API not supported: "
 504			+ highlight);
 505	} //}}}
 506
 507	//{{{ removeCustomHighlight() method
 508	/**
 509	 * @deprecated Write a <code>TextAreaExtension</code> instead.
 510	 */
 511	public void removeCustomHighlight(TextAreaHighlight highlight)
 512	{
 513		Log.log(Log.WARNING,this,"Old highlighter API not supported: "
 514			+ highlight);
 515	} //}}}
 516
 517	//{{{ addExtension() method
 518	/**
 519	 * Adds a text area extension, which can perform custom painting and
 520	 * tool tip handling.
 521	 * @param extension The extension
 522	 * @since jEdit 4.0pre4
 523	 */
 524	public void addExtension(TextAreaExtension extension)
 525	{
 526		extensionMgr.addExtension(DEFAULT_LAYER,extension);
 527		repaint();
 528	} //}}}
 529
 530	//{{{ addExtension() method
 531	/**
 532	 * Adds a text area extension, which can perform custom painting and
 533	 * tool tip handling.
 534	 * @param layer The layer to add the extension to. Note that more than
 535	 * extension can share the same layer.
 536	 * @param extension The extension
 537	 * @since jEdit 4.0pre4
 538	 */
 539	public void addExtension(int layer, TextAreaExtension extension)
 540	{
 541		extensionMgr.addExtension(layer,extension);
 542		repaint();
 543	} //}}}
 544
 545	//{{{ removeExtension() method
 546	/**
 547	 * Removes a text area extension. It will no longer be asked to
 548	 * perform custom painting and tool tip handling.
 549	 * @param extension The extension
 550	 * @since jEdit 4.0pre4
 551	 */
 552	public void removeExtension(TextAreaExtension extension)
 553	{
 554		extensionMgr.removeExtension(extension);
 555		repaint();
 556	} //}}}
 557
 558	//{{{ getToolTipText() method
 559	/**
 560	 * Returns the tool tip to display at the specified location.
 561	 * @param evt The mouse event
 562	 */
 563	public String getToolTipText(MouseEvent evt)
 564	{
 565		if(!textArea.getBuffer().isLoaded())
 566			return null;
 567
 568		return extensionMgr.getToolTipText(evt.getX(),evt.getY());
 569	} //}}}
 570
 571	//{{{ getFontMetrics() method
 572	/**
 573	 * Returns the font metrics used by this component.
 574	 */
 575	public FontMetrics getFontMetrics()
 576	{
 577		return fm;
 578	} //}}}
 579
 580	//{{{ setFont() method
 581	/**
 582	 * Sets the font for this component. This is overridden to update the
 583	 * cached font metrics and to recalculate which lines are visible.
 584	 * @param font The font
 585	 */
 586	public void setFont(Font font)
 587	{
 588		super.setFont(font);
 589		fm = getFontMetrics(font);
 590		textArea.recalculateVisibleLines();
 591	} //}}}
 592
 593	//{{{ paintComponent() method
 594	/**
 595	 * Repaints the text.
 596	 * @param g The graphics context
 597	 */
 598	public void paintComponent(Graphics _gfx)
 599	{
 600		Graphics2D gfx = (Graphics2D)_gfx;
 601		gfx.setRenderingHints(renderingHints);
 602		fontRenderContext = gfx.getFontRenderContext();
 603
 604		Rectangle clipRect = gfx.getClipBounds();
 605
 606		gfx.setColor(getBackground());
 607		gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
 608
 609		Buffer buffer = textArea.getBuffer();
 610		if(!buffer.isLoaded())
 611			return;
 612
 613		int x = textArea.getHorizontalOffset();
 614
 615		int height = fm.getHeight();
 616		int firstInvalid = clipRect.y / height;
 617		// Because the clipRect's height is usually an even multiple
 618		// of the font height, we subtract 1 from it, otherwise one
 619		// too many lines will always be painted.
 620		int lastInvalid = (clipRect.y + clipRect.height - 1) / height;
 621
 622		textArea.chunkCache.updateChunksUpTo(lastInvalid);
 623
 624		int lineCount = textArea.getVirtualLineCount();
 625
 626		int y = (clipRect.y - clipRect.y % height);
 627
 628		try
 629		{
 630			boolean updateMaxHorizontalScrollWidth = false;
 631
 632			for(int line = firstInvalid; line <= lastInvalid; line++)
 633			{
 634				ChunkCache.LineInfo lineInfo = textArea.chunkCache
 635					.getLineInfo(line);
 636				if(!lineInfo.chunksValid)
 637					System.err.println("text area painter: not valid");
 638
 639				lineInfo.width = paintLine(gfx,buffer,lineInfo,line,x,y) - x;
 640				if(lineInfo.width > textArea.maxHorizontalScrollWidth)
 641					updateMaxHorizontalScrollWidth = true;
 642
 643				y += height;
 644			}
 645
 646			if(buffer.isNextLineRequested())
 647			{
 648				int h = clipRect.y + clipRect.height;
 649				textArea.chunkCache.invalidateChunksFrom(lastInvalid + 1);
 650				repaint(0,h,getWidth(),getHeight() - h);
 651			}
 652
 653			if(updateMaxHorizontalScrollWidth)
 654				textArea.updateMaxHorizontalScrollWidth();
 655		}
 656		catch(Exception e)
 657		{
 658			Log.log(Log.ERROR,this,"Error repainting line"
 659				+ " range {" + firstInvalid + ","
 660				+ lastInvalid + "}:");
 661			Log.log(Log.ERROR,this,e);
 662		}
 663	} //}}}
 664
 665	//{{{ nextTabStop() method
 666	/**
 667	 * Implementation of TabExpander interface. Returns next tab stop after
 668	 * a specified point.
 669	 * @param x The x co-ordinate
 670	 * @param tabOffset Ignored
 671	 * @return The next tab stop after <i>x</i>
 672	 */
 673	public float nextTabStop(float x, int tabOffset)
 674	{
 675		int ntabs = (int)(x / textArea.tabSize);
 676		return (ntabs + 1) * textArea.tabSize;
 677	} //}}}
 678
 679	//{{{ getPreferredSize() method
 680	/**
 681	 * Returns the painter's preferred size.
 682	 */
 683	public Dimension getPreferredSize()
 684	{
 685		Dimension dim = new Dimension();
 686
 687		char[] foo = new char[80];
 688		for(int i = 0; i < foo.length; i++)
 689			foo[i] = ' ';
 690		dim.width = (int)(getFont().getStringBounds(foo,0,foo.length,
 691			fontRenderContext).getWidth());
 692		dim.height = fm.getHeight() * 25;
 693		return dim;
 694	} //}}}
 695
 696	//{{{ getMinimumSize() method
 697	/**
 698	 * Returns the painter's minimum size.
 699	 */
 700	public Dimension getMinimumSize()
 701	{
 702		return getPreferredSize();
 703	} //}}}
 704
 705	//{{{ Private members
 706
 707	//{{{ Instance variables
 708	private JEditTextArea textArea;
 709
 710	private SyntaxStyle[] styles;
 711	private Color caretColor;
 712	private Color selectionColor;
 713	private Color lineHighlightColor;
 714	private Color foldedLineColor;
 715	private Color bracketHighlightColor;
 716	private Color eolMarkerColor;
 717	private Color wrapGuideColor;
 718
 719	private boolean blockCaret;
 720	private boolean lineHighlight;
 721	private boolean bracketHighlight;
 722	private boolean eolMarkers;
 723	private boolean wrapGuide;
 724	private boolean antiAlias;
 725	private boolean fracFontMetrics;
 726
 727	// should try to use this as little as possible.
 728	private FontMetrics fm;
 729
 730	private ExtensionManager extensionMgr;
 731
 732	private RenderingHints renderingHints;
 733	private FontRenderContext fontRenderContext;
 734	//}}}
 735
 736	//{{{ updateRenderingHints() method
 737	private void updateRenderingHints()
 738	{
 739		HashMap hints = new HashMap();
 740
 741		if(antiAlias)
 742		{
 743			hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
 744			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 745			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 746		}
 747		else
 748		{
 749			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
 750			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
 751		}
 752
 753		hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
 754			fracFontMetrics ?
 755				RenderingHints.VALUE_FRACTIONALMETRICS_ON
 756				: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
 757
 758		renderingHints = new RenderingHints(hints);
 759		fontRenderContext = new FontRenderContext(null,antiAlias,
 760			fracFontMetrics);
 761	} //}}}
 762
 763	//{{{ paintLine() method
 764	private int paintLine(Graphics2D gfx, Buffer buffer,
 765		ChunkCache.LineInfo lineInfo, int screenLine,
 766		int x, int y)
 767	{
 768		int physicalLine = lineInfo.physicalLine;
 769
 770		if(physicalLine == -1)
 771			extensionMgr.paintInvalidLine(gfx,screenLine,y);
 772		else
 773		{
 774			boolean collapsedFold = (physicalLine < buffer.getLineCount() - 1
 775				&& buffer.isFoldStart(physicalLine)
 776				&& !textArea.getFoldVisibilityManager()
 777				.isLineVisible(physicalLine + 1)
 778				&& lineInfo.firstSubregion);
 779			int start = textArea.getScreenLineStartOffset(screenLine);
 780			int end = textArea.getScreenLineEndOffset(screenLine);
 781
 782			int caret = textArea.getCaretPosition();
 783			boolean paintLineHighlight = lineHighlight
 784				&& caret >= start && caret < end
 785				&& textArea.selection.size() == 0;
 786
 787			Color bgColor;
 788
 789			if(paintLineHighlight)
 790				bgColor = lineHighlightColor;
 791			else if(collapsedFold)
 792				bgColor = foldedLineColor;
 793			else
 794				bgColor = getBackground();
 795
 796			if(paintLineHighlight || collapsedFold)
 797			{
 798				gfx.setColor(bgColor);
 799				gfx.fillRect(0,y,getWidth(),fm.getHeight());
 800			}
 801
 802			extensionMgr.paintValidLine(gfx,screenLine,physicalLine,
 803				start,end,y);
 804
 805			Font defaultFont = getFont();
 806			Color defaultColor = getForeground();
 807
 808			gfx.setFont(defaultFont);
 809			gfx.setColor(defaultColor);
 810
 811			float baseLine = y + fm.getHeight()
 812				- fm.getLeading() - fm.getDescent();
 813
 814			if(lineInfo.chunks != null)
 815			{
 816				x += ChunkCache.paintChunkList(
 817					lineInfo.chunks,gfx,x,baseLine,
 818					getWidth(),bgColor,true);
 819			}
 820
 821			gfx.setFont(defaultFont);
 822			gfx.setColor(eolMarkerColor);
 823
 824			if(!lineInfo.lastSubregion)
 825			{
 826				gfx.drawString(":",Math.max(x,
 827					textArea.getHorizontalOffset()
 828					+ textArea.wrapMargin),
 829					baseLine);
 830				x += textArea.charWidth;
 831			}
 832			else if(collapsedFold)
 833			{
 834				int nextLine = textArea.getFoldVisibilityManager()
 835					.getNextVisibleLine(physicalLine);
 836				if(nextLine == -1)
 837					nextLine = buffer.getLineCount();
 838
 839				int count = nextLine - physicalLine - 1;
 840				String str = " [" + count + " lines]";
 841				gfx.drawString(str,x,baseLine);
 842				x += (int)(getFont().getStringBounds(
 843					str,fontRenderContext)
 844					.getWidth());
 845			}
 846			else if(eolMarkers)
 847			{
 848				gfx.drawString(".",x,baseLine);
 849				x += textArea.charWidth;
 850			}
 851
 852			paintCaret(gfx,physicalLine,start,end,y,bgColor);
 853		}
 854
 855		return x;
 856	} //}}}
 857
 858	//{{{ paintCaret() method
 859	private void paintCaret(Graphics2D gfx, int physicalLine,
 860		int start, int end, int y, Color bgColor)
 861	{
 862		if(!textArea.isCaretVisible())
 863			return;
 864
 865		int caret = textArea.getCaretPosition();
 866		if(caret < start || caret >= end)
 867			return;
 868
 869		int offset = caret - textArea.getLineStartOffset(physicalLine);
 870		textArea.offsetToXY(physicalLine,offset,textArea.returnValue);
 871		int caretX = textArea.returnValue.x;
 872		int height = fm.getHeight();
 873
 874		gfx.setColor(caretColor);
 875
 876		if(textArea.isOverwriteEnabled())
 877		{
 878			gfx.drawLine(caretX,y + height - 1,
 879				caretX + textArea.charWidth,y + height - 1);
 880		}
 881		else if(blockCaret)
 882		{
 883			// Workaround for bug in Graphics2D in JDK1.4 under
 884			// Windows; calling setPaintMode() does not reset
 885			// graphics mode.
 886			Graphics2D blockgfx = (Graphics2D)gfx.create();
 887			blockgfx.setXORMode(bgColor);
 888			blockgfx.fillRect(caretX,y,textArea.charWidth,height);
 889			blockgfx.dispose();
 890		}
 891		else
 892		{
 893			gfx.drawLine(caretX,y,caretX,y + height - 1);
 894		}
 895	} //}}}
 896
 897	//}}}
 898
 899	//{{{ PaintSelection class
 900	class PaintSelection extends TextAreaExtension
 901	{
 902		//{{{ paintValidLine() method
 903		public void paintValidLine(Graphics2D gfx, int screenLine,
 904			int physicalLine, int start, int end, int y)
 905		{
 906			if(textArea.selection.size() == 0)
 907				return;
 908
 909			gfx.setColor(getSelectionColor());
 910			for(int i = textArea.selection.size() - 1;
 911				i >= 0; i--)
 912			{
 913				paintSelection(gfx,screenLine,
 914					physicalLine,start,end,y,
 915					(Selection)textArea.selection
 916					.get(i));
 917			}
 918		} //}}}
 919
 920		//{{{ paintSelection() method
 921		private void paintSelection(Graphics2D gfx, int screenLine,
 922			int physicalLine, int start, int end, int y, Selection s)
 923		{
 924			if(end <= s.start || start > s.end)
 925				return;
 926
 927			int selStartScreenLine = textArea.getScreenLineOfOffset(s.start);
 928			int selEndScreenLine = textArea.getScreenLineOfOffset(s.end);
 929
 930			int lineStart = textArea.getLineStartOffset(physicalLine);
 931			int x1, x2;
 932
 933			if(s instanceof Selection.Rect)
 934			{
 935				int lineLen = textArea.getLineLength(physicalLine);
 936				x1 = textArea.offsetToXY(physicalLine,Math.min(lineLen,
 937					s.start - textArea.getLineStartOffset(
 938					s.startLine)),textArea.returnValue).x;
 939				x2 = textArea.offsetToXY(physicalLine,Math.min(lineLen,
 940					s.end - textArea.getLineStartOffset(
 941					s.endLine)),textArea.returnValue).x;
 942
 943				if(x1 > x2)
 944				{
 945					int tmp = x2;
 946					x2 = x1;
 947					x1 = tmp;
 948				}
 949			}
 950			else if(selStartScreenLine == selEndScreenLine
 951				&& selStartScreenLine != -1)
 952			{
 953				x1 = textArea.offsetToXY(physicalLine,
 954					s.start - lineStart,textArea.returnValue).x;
 955				x2 = textArea.offsetToXY(physicalLine,
 956					s.end - lineStart,textArea.returnValue).x;
 957			}
 958			else if(screenLine == selStartScreenLine)
 959			{
 960				x1 = textArea.offsetToXY(physicalLine,
 961					s.start - lineStart,textArea.returnValue).x;
 962				x2 = getWidth();
 963			}
 964			else if(screenLine == selEndScreenLine)
 965			{
 966				x1 = 0;
 967				x2 = textArea.offsetToXY(physicalLine,
 968					s.end - lineStart,textArea.returnValue).x;
 969			}
 970			else
 971			{
 972				x1 = 0;
 973				x2 = getWidth();
 974			}
 975
 976			if(x1 == x2)
 977				x2++;
 978
 979			gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
 980		} //}}}
 981	} //}}}
 982
 983	//{{{ WrapGuide class
 984	class WrapGuide extends TextAreaExtension
 985	{
 986		public void paintValidLine(Graphics2D gfx, int screenLine,
 987			int physicalLine, int start, int end, int y)
 988		{
 989			paintInvalidLine(gfx,screenLine,y);
 990		}
 991
 992		public void paintInvalidLine(Graphics2D gfx, int screenLine, int y)
 993		{
 994			if(!textArea.wrapToWidth && textArea.wrapMargin != 0
 995				&& isWrapGuidePainted())
 996			{
 997				gfx.setColor(getWrapGuideColor());
 998				int x = textArea.getHorizontalOffset() + textArea.wrapMargin;
 999				gfx.drawLine(x,y,x,y + fm.getHeight());
1000			}
1001		}
1002
1003		public String getToolTipText(int x, int y)
1004		{
1005			if(!textArea.wrapToWidth && textArea.wrapMargin != 0
1006				&& isWrapGuidePainted())
1007			{
1008				int wrapGuidePos = textArea.wrapMargin
1009					+ textArea.getHorizontalOffset();
1010				if(Math.abs(x - wrapGuidePos) < 5)
1011				{
1012					return String.valueOf(textArea.getBuffer()
1013						.getProperty("maxLineLen"));
1014				}
1015			}
1016
1017			return null;
1018		}
1019	} //}}}
1020
1021	//{{{ BracketHighlight class
1022	class BracketHighlight extends TextAreaExtension
1023	{
1024		public void paintValidLine(Graphics2D gfx, int screenLine,
1025			int physicalLine, int start, int end, int y)
1026		{
1027			if(!isBracketHighlightEnabled() || !textArea.isBracketHighlightVisible())
1028				return;
1029
1030			int bracketLine = textArea.getBracketLine();
1031			int bracketOffset = textArea.getBracketPosition();
1032			if(bracketLine == -1 || bracketOffset == -1)
1033				return;
1034
1035			int bracketLineStart = textArea.getLineStartOffset(bracketLine);
1036			if(bracketOffset + bracketLineStart < start
1037				|| bracketOffset + bracketLineStart >= end)
1038				return;
1039
1040			textArea.offsetToXY(bracketLine,bracketOffset,textArea.returnValue);
1041			gfx.setColor(getBracketHighlightColor());
1042			// Hack!!! Since there is no fast way to get the character
1043			// from the bracket matching routine, we use ( since all
1044			// brackets probably have the same width anyway
1045			gfx.drawRect(textArea.returnValue.x,y,(int)gfx.getFont().getStringBounds(
1046				"(",getFontRenderContext()).getWidth() - 1,
1047				fm.getHeight() - 1);
1048		}
1049	} //}}}
1050}