Chunk.java :  » Scripting » seco » seco » notebook » syntax » Java Open Source

Java Open Source » Scripting » seco 
seco » seco » notebook » syntax » Chunk.java
/*
 * This file is part of the Scriba source distribution. This is free, open-source 
 * software. For full licensing information, please see the LicensingInformation file
 * at the root level of the distribution.
 *
 * Copyright (c) 2006-2007 Kobrix Software, Inc.
 */
/*
 * Chunk.java - A syntax token with extra information required for painting it
 * on screen
 * :tabSize=8:indentSize=8:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 2001, 2002 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 seco.notebook.syntax;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;

import javax.swing.text.Segment;
import javax.swing.text.TabExpander;

/**
 * A syntax token with extra information required for painting it on screen.
 * @since jEdit 4.1pre1
 */
public class Chunk extends Token implements TabExpander
{
  /**
   * Paints a chunk list.
   * @param chunks The chunk list
   * @param gfx The graphics context
   * @param x The x co-ordinate
   * @param y The y co-ordinate
   * @return The width of the painted text
   * @since jEdit 4.2pre1
   */
  public static float paintChunkListNoWrap(Chunk chunks, Graphics2D gfx,
      float x, float y, boolean glyphVector)
  {
    Rectangle clipRect = gfx.getClipBounds();
    float _x = 0.0f;
    while (chunks != null)
    {
      // only paint visible chunks
      if (x + _x + chunks.width > clipRect.x
          && x + _x < clipRect.x + clipRect.width)
      {
        if (chunks.accessable && chunks.visible)
        {
          gfx.setFont(chunks.style.getFont());
          gfx.setColor(chunks.style.getForegroundColor());
          if (glyphVector && chunks.gv != null)
            gfx.drawGlyphVector(chunks.gv, x + _x, y);
          else if (chunks.str != null)
          {
            gfx.drawString(chunks.str, (int) (x + _x), (int) y);
          }
        }
      }
      _x += chunks.width;
      chunks = (Chunk) chunks.next;
    }
    return _x;
  }

  public static float paintChunkList(Chunk chunks, Graphics2D gfx, float x,
      float y, int start, int end)
  {
    float _x = 0.0f;
    chunks = getChunkAtOffset(chunks, start);
    while (chunks != null)
    {
      if (chunks.offset >= end) return _x;
      if (chunks.accessable && chunks.visible)
      {
        gfx.setFont(chunks.style.getFont());
        if(!Color.white.equals(gfx.getColor()))
        //  System.out.println("GlyphPainter -sel: " + start + ":" + end + gfx.getColor());
        //else  
          gfx.setColor(chunks.style.getForegroundColor());
        //System.out.println("paintChunkList: " + chunks.str + ":" + x
        // + ":" + _x  + ":" + start + ":" + end + ":" + chunks.gv.getFont());
        if (chunks.str != null)
        {
          if (chunks.offset + chunks.length <= end
              && chunks.offset >= start)
          {
            if(chunks.gv != null)
              gfx.drawGlyphVector(chunks.gv, x + _x, y);
            else
               gfx.drawString(chunks.str, (int) (x + _x), (int) y);
          } else if (end - chunks.offset > 0
              && end - chunks.offset < chunks.length)
          {
            int part = end - chunks.offset;
            //System.out.println("SplitChunk: " + end + ":" + part + 
            //    ":" + chunks.offset + ":" +  chunks.length);
            chunks = splitChunk(chunks, part);
            //System.out.println("SplittedChunk: " + chunks.str + ":" + chunks.offset + ":"
            //    + ":" + chunks.length + ":" + ((Chunk)chunks.next).str);
            gfx.drawGlyphVector(chunks.gv, x + _x, y);
            
            //int part = end - chunks.offset;
            //int part0 = start - chunks.offset;
            //if (part0 < 0) part0 = 0;
            //gfx.drawString(chunks.str.substring(part0, part),
            //    (int) (x + _x), (int) y);
          } else if(chunks.offset > -1 && chunks.offset < start)
          {
            int part = start - chunks.offset; 
            String rem = chunks.str.substring(part);
            gfx.drawString(rem, (int) (x + _x), (int) y);
            GlyphVector g = chunks.style.getFont().createGlyphVector(
                chunks.gv.getFontRenderContext(), rem);
            float w = (float) g.getLogicalBounds().getWidth();
            // System.out.println("End Chunk: " + rem
            // + ":" + chunks.offset + ":" + chunks.length + ":" + start
            // + ":" + end + ":" + w + ":" + x + ":" + chunks.width);
            _x += w;
          }
          if (chunks.err)
            drawWaveUnderline(gfx, chunks, (x + _x), y);
          if (chunks.bracket)
            drawHighlightedBracket(gfx, chunks, (x + _x), y );
        }
      }
      _x += chunks.width;
      chunks = (Chunk) chunks.next;
    }
    return _x;
  }

  private static void drawHighlightedBracket(Graphics2D gfx, Chunk chunks, 
      float x, float y)
  {
    gfx.setColor(Color.black);
    FontMetrics fm = gfx.getFontMetrics();
    int w = fm.charWidth(chunks.str.charAt(0));
    int h = fm.getHeight();
    
    gfx.drawRect((int)x, (int) y - h + fm.getDescent(), w, h);
  }
  
  private static void drawWaveUnderline(Graphics2D gfx, Chunk chunks, 
      float x, float y)
  {
    
    int waveLength = (int) chunks.width;
    //System.out.println("drawWaveUnderline: " + chunks.str + ":" + x
    //     + ":" + chunks.width + ":" +
    //     offsetToX(chunks, chunks.offset) + ":" + x);
    gfx.setColor(Color.RED);
    if (waveLength > 0)
    {
      int[] wf = { 0, +1, 0, -1 };
      int[] xArray = new int[waveLength + 1];
      int[] yArray = new int[waveLength + 1];
      int yBase = (int) (y + 1.5);
      for (int i = 0; i <= waveLength; i++)
      {
        xArray[i] = (int) x + i;
        yArray[i] = yBase + wf[xArray[i] % 4];
      }
      gfx.drawPolyline(xArray, yArray, waveLength);
    }
  }
  
  public static Chunk getChunkAtOffset(Chunk chunks, int offset)
  {
    while (chunks != null)
    {
      if (chunks.offset >= offset
          || chunks.offset + chunks.length > offset) return chunks;
      chunks = (Chunk) chunks.next;
    }
    return null;
  }

  
  /**
   * Converts an offset in a chunk list into an x co-ordinate.
   * @param chunks The chunk list
   * @param offset The offset
   * @since jEdit 4.1pre1
   */
  public static float offsetToX(Chunk chunks, int offset)
  {
    if (chunks != null && offset < chunks.offset)
    {
//      TODO: sometimes this error arises without
      //known reason and no side effects, so keep quiet for now...
      //throw new ArrayIndexOutOfBoundsException(offset + " < "
      //    + chunks.offset);
    }
    float x = 0.0f;
    while (chunks != null)
    {
      //System.out.println("Chunk - offsetToX: " + chunks.offset + 
      //    ":" + chunks.length);
      if (chunks.accessable && offset < chunks.offset + chunks.length)
        return x + chunks.offsetToX(offset - chunks.offset);
      x += chunks.width;
      chunks = (Chunk) chunks.next;
    }
    return x;
  }
  
  public static float offsetToX(Chunk chunks, int start, int offset)
  {
    if (chunks != null && offset < chunks.offset)
    {
      //TODO: sometimes this error arises without
      //known reason and no side effects, so keep quiet for now...
      //throw new ArrayIndexOutOfBoundsException(offset + " < "
      //    + chunks.offset);
      return 0;
    }
    float x = 0.0f;
    while (chunks != null)
    {
      //System.out.println("Chunk - offsetToX: " + chunks.offset + 
      //    ":" + chunks.length + ":" + chunks.str);
      if (chunks.accessable && offset < chunks.offset + chunks.length)
        return x + chunks.offsetToX(offset - chunks.offset);
      if(start <= chunks.offset)
         x += chunks.width;
      chunks = (Chunk) chunks.next;
    }
    return x;
  }

  /**
   * Converts an x co-ordinate in a chunk list into an offset.
   * @param chunks The chunk list
   * @param x The x co-ordinate
   * @param round Round up to next letter if past the middle of a letter?
   * @return The offset within the line, or -1 if the x co-ordinate is too far
   * to the right
   * @since jEdit 4.1pre1
   */
  public static int xToOffset(Chunk chunks, float x, boolean round)
  {
    float _x = 0.0f;
    while (chunks != null)
    {
      if (chunks.accessable && x < _x + chunks.width)
        return chunks.xToOffset(x - _x, round);
      _x += chunks.width;
      chunks = (Chunk) chunks.next;
    }
    return -1;
  }
  public boolean accessable;
  public boolean visible;
  public boolean initialized;
  // set up after init()
  public SyntaxStyle style;
  // this is either style.getBackgroundColor() or
  // styles[defaultID].getBackgroundColor()
  public Color background;
  public float width;
  public GlyphVector gv;
  public String str;

  public Chunk(float width, int offset, ParserRuleSet rules)
  {
    super(Token.NULL, offset, 0, rules);
    this.width = width;
  }

  public Chunk(byte id, int offset, int length, ParserRuleSet rules,
      SyntaxStyle[] styles, byte defaultID)
  {
    super(id, offset, length, rules);
    accessable = true;
    style = styles[id];
    if(style == null) style = styles[defaultID];
    background = style.getBackgroundColor();
    if (background == null)
      background = styles[defaultID].getBackgroundColor();
  }

  public final float[] getPositions()
  {
    if (gv == null) //return null;
    {
      gv = style.getFont().createGlyphVector(fontRenderContext, str);
      width = (float) gv.getLogicalBounds().getWidth();
    }
    if (positions == null)
      positions = gv.getGlyphPositions(0, length, null);
    return positions;
  }

  public final float offsetToX(int offset)
  {
    if (!visible || offset < 0)
      return 0.0f;
    else
      return (float) gv.getGlyphPosition(offset).getX();//getPositions()[offset * 2];
  }

  public final int xToOffset(float x, boolean round)
  {
    if (!visible)
    {
      if (round && width - x < x)
        return offset + length;
      else
        return offset;
    } else
    {
      float[] pos = getPositions();
      for (int i = 0; i < length; i++)
      {
        float glyphX = pos[i * 2];
        float nextX = (i == length - 1 ? width : pos[i * 2 + 2]);
        if (nextX > x)
        {
          if (!round || nextX - x > x - glyphX)
            return offset + i;
          else
            return offset + i + 1;
        }
      }
    }
    // wtf?
    return -1;
  }
  public static int tabSize = 8;

  public float nextTabStop(float x, int tabOffset)
  {
    int ntabs = (int) (x / tabSize);
    return (ntabs + 1) * tabSize;
  }

  public void init(Segment seg, TabExpander expander, float x,
      FontRenderContext fontRenderContext)
  {
    initialized = true;
    if (!accessable)
    {
      // do nothing
    } else if (length == 1 && seg.offset + offset < seg.array.length &&
            seg.array[seg.offset + offset] == '\t')
    {
      visible = false;
      if (expander == null) expander = this;
      float newX = expander.nextTabStop(x, offset + length);
      width = newX - x;
    } else
    {
      visible = true;
      str = new String(seg.array, seg.offset + offset, length);
      gv = style.getFont().createGlyphVector(getFontRenderContext(fontRenderContext), str);
      width = (float) gv.getLogicalBounds().getWidth();
      //System.out.println("Chunk - init: " + style.getFont());
    }
  }
  
  private static FontRenderContext fontRenderContext;
  static{
    Graphics2D g2 =
         new BufferedImage(1,1,BufferedImage.TYPE_INT_RGB).createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
    fontRenderContext = g2.getFontRenderContext();
  }
  
  private static FontRenderContext getFontRenderContext(FontRenderContext in)
  {
    if(in != null) 
      return in;
    return fontRenderContext;
  }
  
  private static Chunk splitChunk(Chunk chunks, int part)
  {
    String s = chunks.str;
    Chunk new_chunk = new Chunk(chunks, part);
    chunks.str = s.substring(0, part);
    chunks.length = part;
    chunks.gv = chunks.style.getFont().createGlyphVector(
        getFontRenderContext(fontRenderContext), chunks.str);
    chunks.width = (float) chunks.gv.getLogicalBounds().getWidth();
    Chunk next = (Chunk) chunks.next;
    chunks.next = new_chunk;
    new_chunk.next = next;
    new_chunk.prev = chunks;
    if(next != null)
       next.prev = new_chunk;
    return chunks;
  }
  
  private float[] positions;
  
  private Chunk(Chunk c, int part)
  {
    super(c.id, c.offset + part, c.length - part, c.rules);
    accessable = true;
    visible = true;
    style = c.style;
    background = c.background;
    str = c.str.substring(part);
    initialized = true;
    gv = style.getFont().createGlyphVector(getFontRenderContext(fontRenderContext), str);
    width = (float) gv.getLogicalBounds().getWidth();
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.