Package org.gjt.sp.jedit.textarea

Source Code of org.gjt.sp.jedit.textarea.TextAreaPainter$PaintCaret

/*
* TextAreaPainter.java - Paints the text area
* :tabSize=8:indentSize=8:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 1999, 2003 Slava Pestov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

package org.gjt.sp.jedit.textarea;

//{{{ Imports
import javax.swing.text.*;
import javax.swing.JComponent;
import java.awt.event.MouseEvent;
import java.awt.font.*;
import java.awt.*;
import java.util.HashMap;
import org.gjt.sp.jedit.buffer.IndentFoldHandler;
import org.gjt.sp.jedit.syntax.*;
import org.gjt.sp.jedit.Buffer;
import org.gjt.sp.jedit.Debug;
import org.gjt.sp.util.Log;
//}}}

/**
* The text area painter is the component responsible for displaying the
* text of the current buffer. The only methods in this class that should
* be called by plugins are those for adding and removing
* text area extensions.
*
* @see #addExtension(TextAreaExtension)
* @see #addExtension(int,TextAreaExtension)
* @see #removeExtension(TextAreaExtension)
* @see TextAreaExtension
* @see JEditTextArea
*
* @author Slava Pestov
* @version $Id: TextAreaPainter.java,v 1.100 2004/07/22 00:55:26 spestov Exp $
*/
public class TextAreaPainter extends JComponent implements TabExpander
{
  //{{{ Layers
  /**
   * The lowest possible layer.
   * @see #addExtension(int,TextAreaExtension)
   * @since jEdit 4.0pre4
   */
  public static final int LOWEST_LAYER = Integer.MIN_VALUE;

  /**
   * Below selection layer. The JDiff plugin will use this.
   * @see #addExtension(int,TextAreaExtension)
   * @since jEdit 4.0pre4
   */
  public static final int BACKGROUND_LAYER = -60;

  /**
   * The line highlight and collapsed fold highlight layer.
   * @see #addExtension(int,TextAreaExtension)
   * @since jEdit 4.0pre7
   */
  public static final int LINE_BACKGROUND_LAYER = -50;

  /**
   * Below selection layer.
   * @see #addExtension(int,TextAreaExtension)
   * @since jEdit 4.0pre4
   */
  public static final int BELOW_SELECTION_LAYER = -40;

  /**
   * Selection layer. Most extensions will be above this layer, but some
   * (eg, JDiff) will want to be below the selection.
   * @see #addExtension(int,TextAreaExtension)
   * @since jEdit 4.0pre4
   */
  public static final int SELECTION_LAYER = -30;

  /**
   * Wrap guide layer. Most extensions will be above this layer.
   * @since jEdit 4.0pre4
   */
  public static final int WRAP_GUIDE_LAYER = -20;

  /**
   * Below most extensions layer.
   * @see #addExtension(int,TextAreaExtension)
   * @since jEdit 4.0pre4
   */
  public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;

  /**
   * Default extension layer. This is above the wrap guide but below the
   * structure highlight.
   * @since jEdit 4.0pre4
   */
  public static final int DEFAULT_LAYER = 0;

  /**
   * Block caret layer. Most extensions will be below this layer.
   * @since jEdit 4.2pre1
   */
  public static final int BLOCK_CARET_LAYER = 50;

  /**
   * Bracket highlight layer. Most extensions will be below this layer.
   * @since jEdit 4.0pre4
   */
  public static final int BRACKET_HIGHLIGHT_LAYER = 100;

  /**
   * Text layer. Most extensions will be below this layer.
   * @since jEdit 4.2pre1
   */
  public static final int TEXT_LAYER = 200;

  /**
   * Caret layer. Most extensions will be below this layer.
   * @since jEdit 4.2pre1
   */
  public static final int CARET_LAYER = 300;

  /**
   * Highest possible layer.
   * @since jEdit 4.0pre4
   */
  public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
  //}}}

  //{{{ setBounds() method
  /**
   * It is a bad idea to override this, but we need to get the component
   * event before the first repaint.
   */
  public void setBounds(int x, int y, int width, int height)
  {
    if(x == getX() && y == getY() && width == getWidth()
      && height == getHeight())
    {
      return;
    }

    super.setBounds(x,y,width,height);

    textArea.recalculateVisibleLines();
    if(textArea.getBuffer().isLoaded())
      textArea.recalculateLastPhysicalLine();
    textArea.propertiesChanged();
    textArea.updateMaxHorizontalScrollWidth();
    textArea.scrollBarsInitialized = true;
  } //}}}

  //{{{ isManagingFocus() method
  /**
   * Returns if this component can be traversed by pressing the
   * Tab key. This returns false.
   */
  public boolean isManagingFocus()
  {
    return false;
  } //}}}

  //{{{ getFocusTraversalKeysEnabled() method
  /**
   * Makes the tab key work in Java 1.4.
   * @since jEdit 3.2pre4
   */
  public boolean getFocusTraversalKeysEnabled()
  {
    return false;
  } //}}}

  //{{{ Getters and setters

  //{{{ getStyles() method
  /**
   * Returns the syntax styles used to paint colorized text. Entry <i>n</i>
   * will be used to paint tokens with id = <i>n</i>.
   * @see org.gjt.sp.jedit.syntax.Token
   */
  public final SyntaxStyle[] getStyles()
  {
    return styles;
  } //}}}

  //{{{ setStyles() method
  /**
   * Sets the syntax styles used to paint colorized text. Entry <i>n</i>
   * will be used to paint tokens with id = <i>n</i>.
   * @param styles The syntax styles
   * @see org.gjt.sp.jedit.syntax.Token
   */
  public final void setStyles(SyntaxStyle[] styles)
  {
    // assumed this is called after a font render context is set up.
    // changing font render context settings without a setStyles()
    // call will not reset cached monospaced font info.
    fonts.clear();

    this.styles = styles;
    styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
    for(int i = 0; i < styles.length; i++)
    {
      styles[i].setCharWidth(getCharWidth(styles[i].getFont()));
    }
    repaint();
  } //}}}

  //{{{ getCaretColor() method
  /**
   * Returns the caret color.
   */
  public final Color getCaretColor()
  {
    return caretColor;
  } //}}}

  //{{{ setCaretColor() method
  /**
   * Sets the caret color.
   * @param caretColor The caret color
   */
  public final void setCaretColor(Color caretColor)
  {
    this.caretColor = caretColor;
    if(textArea.getBuffer() != null)
      textArea.invalidateLine(textArea.getCaretLine());
  } //}}}

  //{{{ getSelectionColor() method
  /**
   * Returns the selection color.
   */
  public final Color getSelectionColor()
  {
    return selectionColor;
  } //}}}

  //{{{ setSelectionColor() method
  /**
   * Sets the selection color.
   * @param selectionColor The selection color
   */
  public final void setSelectionColor(Color selectionColor)
  {
    this.selectionColor = selectionColor;
    if(textArea.getBuffer() != null)
      textArea.invalidateSelectedLines();
  } //}}}

  //{{{ getMultipleSelectionColor() method
  /**
   * Returns the multiple selection color.
   * @since jEdit 4.2pre1
   */
  public final Color getMultipleSelectionColor()
  {
    return multipleSelectionColor;
  } //}}}

  //{{{ setMultipleSelectionColor() method
  /**
   * Sets the multiple selection color.
   * @param multipleSelectionColor The multiple selection color
   * @since jEdit 4.2pre1
   */
  public final void setMultipleSelectionColor(Color multipleSelectionColor)
  {
    this.multipleSelectionColor = multipleSelectionColor;
    if(textArea.getBuffer() != null)
      textArea.invalidateSelectedLines();
  } //}}}

  //{{{ getLineHighlightColor() method
  /**
   * Returns the line highlight color.
   */
  public final Color getLineHighlightColor()
  {
    return lineHighlightColor;
  } //}}}

  //{{{ setLineHighlightColor() method
  /**
   * Sets the line highlight color.
   * @param lineHighlightColor The line highlight color
   */
  public final void setLineHighlightColor(Color lineHighlightColor)
  {
    this.lineHighlightColor = lineHighlightColor;
    if(textArea.getBuffer() != null)
      textArea.invalidateLine(textArea.getCaretLine());
  } //}}}

  //{{{ isLineHighlightEnabled() method
  /**
   * Returns true if line highlight is enabled, false otherwise.
   */
  public final boolean isLineHighlightEnabled()
  {
    return lineHighlight;
  } //}}}

  //{{{ setLineHighlightEnabled() method
  /**
   * Enables or disables current line highlighting.
   * @param lineHighlight True if current line highlight should be enabled,
   * false otherwise
   */
  public final void setLineHighlightEnabled(boolean lineHighlight)
  {
    this.lineHighlight = lineHighlight;
    if(textArea.getBuffer() != null)
      textArea.invalidateSelectedLines();
  } //}}}

  //{{{ getStructureHighlightColor() method
  /**
   * Returns the structure highlight color.
   * @since jEdit 4.2pre3
   */
  public final Color getStructureHighlightColor()
  {
    return structureHighlightColor;
  } //}}}

  //{{{ setStructureHighlightColor() method
  /**
   * Sets the structure highlight color.
   * @param structureHighlightColor The bracket highlight color
   * @since jEdit 4.2pre3
   */
  public final void setStructureHighlightColor(
    Color structureHighlightColor)
  {
    this.structureHighlightColor = structureHighlightColor;
    StructureMatcher.Match match = textArea.getStructureMatch();
    if(match != null)
    {
      textArea.invalidateLineRange(
        match.startLine,match.endLine
      );
    }
  } //}}}

  //{{{ isStructureHighlightEnabled() method
  /**
   * Returns true if structure highlighting is enabled, false otherwise.
   * @since jEdit 4.2pre3
   */
  public final boolean isStructureHighlightEnabled()
  {
    return structureHighlight;
  } //}}}

  //{{{ setStructureHighlightEnabled() method
  /**
   * Enables or disables structure highlighting.
   * @param structureHighlight True if structure highlighting should be
   * enabled, false otherwise
   * @since jEdit 4.2pre3
   */
  public final void setStructureHighlightEnabled(boolean structureHighlight)
  {
    this.structureHighlight = structureHighlight;
    StructureMatcher.Match match = textArea.getStructureMatch();
    if(match != null)
    {
      textArea.invalidateLineRange(
        match.startLine,
        match.endLine
      );
    }
  } //}}}

  //{{{ isBlockCaretEnabled() method
  /**
   * Returns true if the caret should be drawn as a block, false otherwise.
   */
  public final boolean isBlockCaretEnabled()
  {
    return blockCaret;
  } //}}}

  //{{{ setBlockCaretEnabled() method
  /**
   * Sets if the caret should be drawn as a block, false otherwise.
   * @param blockCaret True if the caret should be drawn as a block,
   * false otherwise.
   */
  public final void setBlockCaretEnabled(boolean blockCaret)
  {
    this.blockCaret = blockCaret;
    extensionMgr.removeExtension(caretExtension);
    if(blockCaret)
      addExtension(BLOCK_CARET_LAYER,caretExtension);
    else
      addExtension(CARET_LAYER,caretExtension);
    if(textArea.getBuffer() != null)
      textArea.invalidateLine(textArea.getCaretLine());
  } //}}}

  //{{{ getEOLMarkerColor() method
  /**
   * Returns the EOL marker color.
   */
  public final Color getEOLMarkerColor()
  {
    return eolMarkerColor;
  } //}}}

  //{{{ setEOLMarkerColor() method
  /**
   * Sets the EOL marker color.
   * @param eolMarkerColor The EOL marker color
   */
  public final void setEOLMarkerColor(Color eolMarkerColor)
  {
    this.eolMarkerColor = eolMarkerColor;
    repaint();
  } //}}}

  //{{{ getEOLMarkersPainted() method
  /**
   * Returns true if EOL markers are drawn, false otherwise.
   */
  public final boolean getEOLMarkersPainted()
  {
    return eolMarkers;
  } //}}}

  //{{{ setEOLMarkersPainted() method
  /**
   * Sets if EOL markers are to be drawn.
   * @param eolMarkers True if EOL markers should be drawn, false otherwise
   */
  public final void setEOLMarkersPainted(boolean eolMarkers)
  {
    this.eolMarkers = eolMarkers;
    repaint();
  } //}}}

  //{{{ getWrapGuideColor() method
  /**
   * Returns the wrap guide color.
   */
  public final Color getWrapGuideColor()
  {
    return wrapGuideColor;
  } //}}}

  //{{{ setWrapGuideColor() method
  /**
   * Sets the wrap guide color.
   * @param wrapGuideColor The wrap guide color
   */
  public final void setWrapGuideColor(Color wrapGuideColor)
  {
    this.wrapGuideColor = wrapGuideColor;
    repaint();
  } //}}}

  //{{{ isWrapGuidePainted() method
  /**
   * Returns true if the wrap guide is drawn, false otherwise.
   * @since jEdit 4.0pre4
   */
  public final boolean isWrapGuidePainted()
  {
    return wrapGuide;
  } //}}}

  //{{{ setWrapGuidePainted() method
  /**
   * Sets if the wrap guide is to be drawn.
   * @param wrapGuide True if the wrap guide should be drawn, false otherwise
   */
  public final void setWrapGuidePainted(boolean wrapGuide)
  {
    this.wrapGuide = wrapGuide;
    repaint();
  } //}}}

  //{{{ getFoldLineStyle() method
  /**
   * Returns the fold line style. The first element is the style for
   * lines with a fold level greater than 3. The remaining elements
   * are for fold levels 1 to 3.
   */
  public final SyntaxStyle[] getFoldLineStyle()
  {
    return foldLineStyle;
  } //}}}

  //{{{ setFoldLineStyle() method
  /**
   * Sets the fold line style. The first element is the style for
   * lines with a fold level greater than 3. The remaining elements
   * are for fold levels 1 to 3.
   * @param foldLineStyle The fold line style
   */
  public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle)
  {
    this.foldLineStyle = foldLineStyle;
    repaint();
  } //}}}

  //{{{ setAntiAliasEnabled() method
  /**
   * Sets if anti-aliasing should be enabled. Has no effect when
   * running on Java 1.1.
   * @since jEdit 3.2pre6
   */
  public void setAntiAliasEnabled(boolean antiAlias)
  {
    this.antiAlias = antiAlias;
    updateRenderingHints();
  } //}}}

  //{{{ isAntiAliasEnabled() method
  /**
   * Returns if anti-aliasing is enabled.
   * @since jEdit 3.2pre6
   */
  public boolean isAntiAliasEnabled()
  {
    return antiAlias;
  } //}}}

  //{{{ setFractionalFontMetricsEnabled() method
  /**
   * Sets if fractional font metrics should be enabled. Has no effect when
   * running on Java 1.1.
   * @since jEdit 3.2pre6
   */
  public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
  {
    this.fracFontMetrics = fracFontMetrics;
    updateRenderingHints();
  } //}}}

  //{{{ isFractionalFontMetricsEnabled() method
  /**
   * Returns if fractional font metrics are enabled.
   * @since jEdit 3.2pre6
   */
  public boolean isFractionalFontMetricsEnabled()
  {
    return fracFontMetrics;
  } //}}}

  //{{{ getFontRenderContext() method
  /**
   * Returns the font render context.
   * @since jEdit 4.0pre4
   */
  public FontRenderContext getFontRenderContext()
  {
    return fontRenderContext;
  } //}}}

  //}}}

  //{{{ addExtension() method
  /**
   * Adds a text area extension, which can perform custom painting and
   * tool tip handling.
   * @param extension The extension
   * @since jEdit 4.0pre4
   */
  public void addExtension(TextAreaExtension extension)
  {
    extensionMgr.addExtension(DEFAULT_LAYER,extension);
    repaint();
  } //}}}

  //{{{ addExtension() method
  /**
   * Adds a text area extension, which can perform custom painting and
   * tool tip handling.
   * @param layer The layer to add the extension to. Note that more than
   * extension can share the same layer.
   * @param extension The extension
   * @since jEdit 4.0pre4
   */
  public void addExtension(int layer, TextAreaExtension extension)
  {
    extensionMgr.addExtension(layer,extension);
    repaint();
  } //}}}

  //{{{ removeExtension() method
  /**
   * Removes a text area extension. It will no longer be asked to
   * perform custom painting and tool tip handling.
   * @param extension The extension
   * @since jEdit 4.0pre4
   */
  public void removeExtension(TextAreaExtension extension)
  {
    extensionMgr.removeExtension(extension);
    repaint();
  } //}}}

  //{{{ getExtensions() method
  /**
   * Returns an array of registered text area extensions. Useful for
   * debugging purposes.
   * @since jEdit 4.1pre5
   */
  public TextAreaExtension[] getExtensions()
  {
    return extensionMgr.getExtensions();
  } //}}}

  //{{{ getToolTipText() method
  /**
   * Returns the tool tip to display at the specified location.
   * @param evt The mouse event
   */
  public String getToolTipText(MouseEvent evt)
  {
    if(!textArea.getBuffer().isLoaded())
      return null;

    return extensionMgr.getToolTipText(evt.getX(),evt.getY());
  } //}}}

  //{{{ getFontMetrics() method
  /**
   * Returns the font metrics used by this component.
   */
  public FontMetrics getFontMetrics()
  {
    return fm;
  } //}}}

  //{{{ setFont() method
  /**
   * Sets the font for this component. This is overridden to update the
   * cached font metrics and to recalculate which lines are visible.
   * @param font The font
   */
  public void setFont(Font font)
  {
    super.setFont(font);
    fm = getFontMetrics(font);
    textArea.recalculateVisibleLines();
    if(textArea.getBuffer() != null
      && textArea.getBuffer().isLoaded())
      textArea.recalculateLastPhysicalLine();
    textArea.propertiesChanged();
  } //}}}

  //{{{ getStringWidth() method
  /**
   * Returns the width of the given string, in pixels, using the text
   * area's current font.
   *
   * @since jEdit 4.2final
   */
  public float getStringWidth(String str)
  {
    if(textArea.charWidth != 0)
      return textArea.charWidth * str.length();
    else
    {
      return (float)getFont().getStringBounds(
        str,getFontRenderContext()).getWidth();
    }
  } //}}}

  //{{{ paintComponent() method
  /**
   * Repaints the text.
   * @param _gfx The graphics context
   */
  public void paintComponent(Graphics _gfx)
  {
    Graphics2D gfx = (Graphics2D)_gfx;
    gfx.setRenderingHints(renderingHints);
    fontRenderContext = gfx.getFontRenderContext();

    Rectangle clipRect = gfx.getClipBounds();

    gfx.setColor(getBackground());
    gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);

    Buffer buffer = textArea.getBuffer();
    if(!buffer.isLoaded())
      return;

    int height = fm.getHeight();
    if(height == 0)
      return;

    int firstInvalid = clipRect.y / height;
    // Because the clipRect's height is usually an even multiple
    // of the font height, we subtract 1 from it, otherwise one
    // too many lines will always be painted.
    int lastInvalid = (clipRect.y + clipRect.height - 1) / height;

    if(Debug.PAINT_TIMER && lastInvalid - firstInvalid >= 1)
      Log.log(Log.DEBUG,this,"repainting " + (lastInvalid - firstInvalid) + " lines");

    int y = (clipRect.y - clipRect.y % height);

    extensionMgr.paintScreenLineRange(textArea,gfx,
      firstInvalid,lastInvalid,y,height);

    textArea.updateMaxHorizontalScrollWidth();
    textArea.displayManager._notifyScreenLineChanges();
  } //}}}

  //{{{ nextTabStop() method
  /**
   * Implementation of TabExpander interface. Returns next tab stop after
   * a specified point.
   * @param x The x co-ordinate
   * @param tabOffset Ignored
   * @return The next tab stop after <i>x</i>
   */
  public float nextTabStop(float x, int tabOffset)
  {
    int ntabs = (int)(x / textArea.tabSize);
    return (ntabs + 1) * textArea.tabSize;
  } //}}}

  //{{{ getPreferredSize() method
  /**
   * Returns the painter's preferred size.
   */
  public Dimension getPreferredSize()
  {
    Dimension dim = new Dimension();

    char[] foo = new char[80];
    for(int i = 0; i < foo.length; i++)
      foo[i] = ' ';
    dim.width = (int)getStringWidth(new String(foo));
    dim.height = fm.getHeight() * 25;
    return dim;
  } //}}}

  //{{{ getMinimumSize() method
  /**
   * Returns the painter's minimum size.
   */
  public Dimension getMinimumSize()
  {
    return getPreferredSize();
  } //}}}

  //{{{ Package-private members

  //{{{ Instance variables
  /* package-private since they are accessed by inner classes and we
   * want this to be fast */
  JEditTextArea textArea;

  SyntaxStyle[] styles;
  Color caretColor;
  Color selectionColor;
  Color multipleSelectionColor;
  Color lineHighlightColor;
  Color structureHighlightColor;
  Color eolMarkerColor;
  Color wrapGuideColor;

  SyntaxStyle[] foldLineStyle;

  boolean blockCaret;
  boolean lineHighlight;
  boolean structureHighlight;
  boolean eolMarkers;
  boolean wrapGuide;
  boolean antiAlias;
  boolean fracFontMetrics;

  // should try to use this as little as possible.
  FontMetrics fm;
  //}}}

  //{{{ TextAreaPainter constructor
  /**
   * Creates a new painter. Do not create instances of this class
   * directly.
   */
  TextAreaPainter(JEditTextArea textArea)
  {
    enableEvents(AWTEvent.FOCUS_EVENT_MASK
      | AWTEvent.KEY_EVENT_MASK
      | AWTEvent.MOUSE_EVENT_MASK);

    this.textArea = textArea;

    fonts = new HashMap();
    extensionMgr = new ExtensionManager();

    setAutoscrolls(true);
    setOpaque(true);
    setRequestFocusEnabled(false);

    setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));

    fontRenderContext = new FontRenderContext(null,false,false);

    addExtension(LINE_BACKGROUND_LAYER,new PaintLineBackground());
    addExtension(SELECTION_LAYER,new PaintSelection());
    addExtension(WRAP_GUIDE_LAYER,new PaintWrapGuide());
    addExtension(BRACKET_HIGHLIGHT_LAYER,new StructureMatcher
      .Highlight(textArea));
    addExtension(TEXT_LAYER,new PaintText());
    caretExtension = new PaintCaret();
  } //}}}

  //}}}

  //{{{ Private members

  //{{{ Instance variables
  private ExtensionManager extensionMgr;
  private PaintCaret caretExtension;
  private RenderingHints renderingHints;
  private FontRenderContext fontRenderContext;
  private HashMap fonts;
  //}}}

  //{{{ updateRenderingHints() method
  private void updateRenderingHints()
  {
    HashMap hints = new HashMap();

    if(antiAlias)
    {
      //hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
      hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    }
    else
    {
      hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
      hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    }

    hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
      fracFontMetrics ?
        RenderingHints.VALUE_FRACTIONALMETRICS_ON
        : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);

    renderingHints = new RenderingHints(hints);
    fontRenderContext = new FontRenderContext(null,antiAlias,
      fracFontMetrics);
  } //}}}

  //{{{ getCharWidth() method
  private int getCharWidth(Font font)
  {
    Integer returnValue = (Integer)fonts.get(font);
    if(returnValue == null)
    {
      int minWidth = Integer.MAX_VALUE;
      int maxWidth = Integer.MIN_VALUE;
      FontMetrics fm = getFontMetrics(font);
      int[] widths = fm.getWidths();
      for(int i = 0; i < widths.length; i++)
      {
        int width = widths[i];
        if(width == 0 || !font.canDisplay((char)i))
          continue;
        minWidth = Math.min(width,minWidth);
        maxWidth = Math.max(width,maxWidth);
      }

      String str = "iwiwiwiau1234";
      double width1 = font.createGlyphVector(textArea.getPainter()
        .getFontRenderContext(),str).getLogicalBounds()
        .getWidth();
      double width2 = str.length() * maxWidth;
      if(minWidth == maxWidth
        && (int)width1 == (int)width2)
      {
        Log.log(Log.DEBUG,this,"Using monospaced font optimization: " + font);
        returnValue = new Integer(maxWidth);
      }
      else
      {
        Log.log(Log.DEBUG,this,"Not using monospaced font optimization: " + font);
        Log.log(Log.DEBUG,this,"Minimum width = " + minWidth
          + ", maximum width = " + maxWidth
          + ", width 1 = " + width1
          + ", width 2 = " + width2);
        returnValue = new Integer(0);
      }

      fonts.put(font,returnValue);
    }
    return returnValue.intValue();
  } //}}}

  //}}}

  //{{{ Inner classes

  //{{{ PaintLineBackground class
  class PaintLineBackground extends TextAreaExtension
  {
    //{{{ paintValidLine() method
    public void paintValidLine(Graphics2D gfx, int screenLine,
      int physicalLine, int start, int end, int y)
    {
      // minimise access$ methods
      JEditTextArea textArea = TextAreaPainter.this.textArea;
      Buffer buffer = textArea.getBuffer();

      //{{{ Paint line highlight and collapsed fold highlight
      boolean collapsedFold =
        (physicalLine < buffer.getLineCount() - 1
        && buffer.isFoldStart(physicalLine)
        && !textArea.displayManager
        .isLineVisible(physicalLine + 1));

      SyntaxStyle foldLineStyle = null;
      if(collapsedFold)
      {
        int level = buffer.getFoldLevel(physicalLine + 1);
        if(buffer.getFoldHandler() instanceof IndentFoldHandler)
          level = Math.max(1,level / buffer.getIndentSize());
        if(level > 3)
          level = 0;
        foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
      }

      int caret = textArea.getCaretPosition();
      boolean paintLineHighlight = isLineHighlightEnabled()
        && caret >= start && caret < end
        && textArea.selection.size() == 0;

      Color bgColor;
      if(paintLineHighlight)
        bgColor = lineHighlightColor;
      else if(collapsedFold)
      {
        bgColor = foldLineStyle.getBackgroundColor();
        if(bgColor == null)
          bgColor = getBackground();
      }
      else
        bgColor = getBackground();

      if(paintLineHighlight || collapsedFold)
      {
        gfx.setColor(bgColor);
        gfx.fillRect(0,y,getWidth(),fm.getHeight());
      } //}}}

      //{{{ Paint token backgrounds
      ChunkCache.LineInfo lineInfo = textArea.chunkCache
        .getLineInfo(screenLine);

      if(lineInfo.chunks != null)
      {
        float baseLine = y + fm.getHeight()
          - fm.getLeading() - fm.getDescent();
        Chunk.paintChunkBackgrounds(
          lineInfo.chunks,gfx,
          textArea.getHorizontalOffset(),
          baseLine);
      } //}}}
    } //}}}
  } //}}}

  //{{{ PaintSelection class
  class PaintSelection extends TextAreaExtension
  {
    //{{{ paintValidLine() method
    public void paintValidLine(Graphics2D gfx, int screenLine,
      int physicalLine, int start, int end, int y)
    {
      if(textArea.selection.size() == 0)
        return;

      gfx.setColor(textArea.isMultipleSelectionEnabled()
        ? getMultipleSelectionColor()
        : getSelectionColor());
      for(int i = textArea.selection.size() - 1; i >= 0; i--)
      {
        paintSelection(gfx,screenLine,physicalLine,y,
          (Selection)textArea.selection.get(i));
      }
    } //}}}

    //{{{ paintSelection() method
    private void paintSelection(Graphics2D gfx, int screenLine,
      int physicalLine, int y, Selection s)
    {
      int[] selectionStartAndEnd
        = textArea.getSelectionStartAndEnd(
        screenLine,physicalLine,s);
      if(selectionStartAndEnd == null)
        return;

      int x1 = selectionStartAndEnd[0];
      int x2 = selectionStartAndEnd[1];

      gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
    } //}}}
  } //}}}

  //{{{ PaintWrapGuide class
  class PaintWrapGuide extends TextAreaExtension
  {
    public void paintScreenLineRange(Graphics2D gfx, int firstLine,
      int lastLine, int[] physicalLines, int[] start,
      int[] end, int y, int lineHeight)
    {
      if(textArea.getDisplayManager().wrapMargin != 0
        && isWrapGuidePainted())
      {
        gfx.setColor(getWrapGuideColor());
        int x = textArea.getHorizontalOffset()
          + textArea.getDisplayManager()
          .wrapMargin;
        gfx.drawLine(x,y,x,y + (lastLine - firstLine
          + 1) * lineHeight);
      }
    }

    public String getToolTipText(int x, int y)
    {
      if(textArea.getDisplayManager().wrapMargin != 0 && isWrapGuidePainted())
      {
        int wrapGuidePos = textArea.getDisplayManager().wrapMargin
          + textArea.getHorizontalOffset();
        if(Math.abs(x - wrapGuidePos) < 5)
        {
          return String.valueOf(textArea.getBuffer()
            .getProperty("maxLineLen"));
        }
      }

      return null;
    }
  } //}}}

  //{{{ PaintText class
  class PaintText extends TextAreaExtension
  {
    public void paintValidLine(Graphics2D gfx, int screenLine,
      int physicalLine, int start, int end, int y)
    {
      ChunkCache.LineInfo lineInfo = textArea.chunkCache
        .getLineInfo(screenLine);

      Font defaultFont = getFont();
      Color defaultColor = getForeground();

      gfx.setFont(defaultFont);
      gfx.setColor(defaultColor);

      int x = textArea.getHorizontalOffset();
      int originalX = x;

      float baseLine = y + fm.getHeight()
        - fm.getLeading() - fm.getDescent();

      if(lineInfo.chunks != null)
      {
        x += Chunk.paintChunkList(lineInfo.chunks,
          gfx,textArea.getHorizontalOffset(),
          baseLine,!Debug.DISABLE_GLYPH_VECTOR);
      }

      Buffer buffer = textArea.getBuffer();

      if(!lineInfo.lastSubregion)
      {
        gfx.setFont(defaultFont);
        gfx.setColor(eolMarkerColor);
        gfx.drawString(":",Math.max(x,
          textArea.getHorizontalOffset()
          + textArea.getDisplayManager().wrapMargin + textArea.charWidth),
          baseLine);
        x += textArea.charWidth;
      }
      else if(physicalLine < buffer.getLineCount() - 1
        && buffer.isFoldStart(physicalLine)
        && !textArea.displayManager
        .isLineVisible(physicalLine + 1))
      {
        int level = buffer.getFoldLevel(physicalLine + 1);
        if(buffer.getFoldHandler() instanceof IndentFoldHandler)
          level = Math.max(1,level / buffer.getIndentSize());
        if(level > 3)
          level = 0;
        SyntaxStyle foldLineStyle = TextAreaPainter.this.foldLineStyle[level];

        Font font = foldLineStyle.getFont();
        gfx.setFont(font);
        gfx.setColor(foldLineStyle.getForegroundColor());

        int nextLine;
        int nextScreenLine = screenLine + 1;
        if(nextScreenLine < textArea.getVisibleLines())
        {
          nextLine = textArea.chunkCache.getLineInfo(nextScreenLine)
            .physicalLine;
        }
        else
        {
          nextLine = textArea.displayManager
            .getNextVisibleLine(physicalLine);
        }

        if(nextLine == -1)
          nextLine = textArea.getLineCount();

        int count = nextLine - physicalLine - 1;
        String str = " [" + count + " lines]";

        float width = getStringWidth(str);

        gfx.drawString(str,x,baseLine);
        x += width;
      }
      else if(eolMarkers)
      {
        gfx.setFont(defaultFont);
        gfx.setColor(eolMarkerColor);
        gfx.drawString(".",x,baseLine);
        x += textArea.charWidth;
      }

      lineInfo.width = (x - originalX);
    }
  } //}}}

  //{{{ PaintCaret class
  class PaintCaret extends TextAreaExtension
  {
    public void paintValidLine(Graphics2D gfx, int screenLine,
      int physicalLine, int start, int end, int y)
    {
      if(!textArea.isCaretVisible())
        return;

      int caret = textArea.getCaretPosition();
      if(caret < start || caret >= end)
        return;

      int offset = caret - textArea.getLineStartOffset(physicalLine);
      textArea.offsetToXY(physicalLine,offset,textArea.returnValue);
      int caretX = textArea.returnValue.x;
      int height = fm.getHeight();

      gfx.setColor(caretColor);

      if(textArea.isOverwriteEnabled())
      {
        gfx.drawLine(caretX,y + height - 1,
          caretX + textArea.charWidth,y + height - 1);
      }
      else if(blockCaret)
        gfx.drawRect(caretX,y,textArea.charWidth - 1,height - 1);
      else
        gfx.drawLine(caretX,y,caretX,y + height - 1);
    }
  } //}}}

  //}}}
}
TOP

Related Classes of org.gjt.sp.jedit.textarea.TextAreaPainter$PaintCaret

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.