package de.unisiegen.tpml.graphics.renderer ;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.text.CharacterIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import de.unisiegen.tpml.core.prettyprinter.PrettyAnnotation;
import de.unisiegen.tpml.core.prettyprinter.PrettyCharIterator;
import de.unisiegen.tpml.core.prettyprinter.PrettyPrintable;
import de.unisiegen.tpml.core.prettyprinter.PrettyString;
import de.unisiegen.tpml.core.util.Theme ;
import de.unisiegen.tpml.graphics.components.Bonds;
import de.unisiegen.tpml.graphics.components.ShowBonds;
/**
* Subclass of the {@link AbstractRenderer} providing the rendering of a
* {@link de.unisiegen.tpml.core.prettyprinter.PrettyString}
*
* @author marcell
* @author michael
*/
public class PrettyStringRenderer extends AbstractRenderer
{
/**
* Inner private class the store information for the line wrapping
*
* @author marcell
* @author michael
*/
private class CheckerResult
{
/**
* list of break Offsets
*/
// / The annotation used for the linewrapping
public ArrayList < Integer > breakOffsets ;
/**
* row-counts
*/
public int rows ;
/**
* the needed size
*/
public Dimension sizeOfResult ;
/**
* constructor
*/
public CheckerResult ( )
{
this.sizeOfResult = new Dimension ( ) ;
this.breakOffsets = new ArrayList < Integer > ( ) ;
}
}
/**
* Results containg the information of all possible linewrappings.
*/
private LinkedList < CheckerResult > results ;
/**
* Results containg the information of all needed Linewarps to fit on paper.
*/
private LinkedList < CheckerResult > resultsForPrinting ;
/**
* The pretty string that should be rendered.
*/
private PrettyString prettyString ;
/**
* The result of all results that actually will be used during the rendering.
* If <i>result</i> is <i>null</i> no line wrapping is done.
*/
private CheckerResult result ;
/**
* The pretty printable that will be underline during the rendering.
*/
private PrettyPrintable underlinePrettyPrintable ;
/**
* saves the positions where the mouse will react
*/
private ToListenForMouseContainer toListenForMouse ;
/**
* The annotation containing the information where the underline appears
* within the string.
*/
private PrettyAnnotation underlineAnnotation ;
/**
* constructor
*/
public PrettyStringRenderer ( )
{
this.results = new LinkedList < CheckerResult > ( ) ;
this.resultsForPrinting = new LinkedList < CheckerResult > ( ) ;
this.result = null ;
this.underlinePrettyPrintable = null ;
this.underlineAnnotation = null ;
}
/**
* Sets the PrettyString
*
* @param pPrettyString
*/
public void setPrettyString ( PrettyString pPrettyString )
{
this.prettyString = pPrettyString ;
if ( (this.prettyString != null) && (this.underlinePrettyPrintable != null) )
{
this.underlineAnnotation = this.prettyString
.getAnnotationForPrintable ( this.underlinePrettyPrintable ) ;
}
else
{
this.underlineAnnotation = null ;
}
checkLinewraps ( ) ;
}
/**
* Sets the prettyPrintable that should be underlined.
*
* @param prettyPrintable
*/
public void setUndelinePrettyPrintable ( PrettyPrintable prettyPrintable )
{
this.underlinePrettyPrintable = prettyPrintable ;
if ( (this.prettyString != null) && (this.underlinePrettyPrintable != null) )
{
this.underlineAnnotation = this.prettyString
.getAnnotationForPrintable ( this.underlinePrettyPrintable ) ;
}
else
{
this.underlineAnnotation = null ;
}
}
/**
* Checks which of the previously calculated result (by checklinewrap) is the
* best match for the given maxWidth.<br>
* <br>
* The size of the widest result that still fits in the maxWidth will be
* returned. If no result is stored that fits in the smallest size is
* returned; in this case the returned size is wider than the given maxWidth.
*
* @param maxWidth The maximum available size for this expression.
* @return The dimensions that will be needed to perform the rendering later.
*/
public Dimension getNeededSize ( int maxWidth )
{
// Find the biggest result, that is smaller than the given
// maxWidth. If none could be found the smllest result available
// will be used.
CheckerResult smallestResult = null ;
CheckerResult biggestResult = null ;
for ( CheckerResult r : this.results )
{
//as long as the actual result is smaller than the smallesResul update it
if ( (smallestResult == null) || (smallestResult.sizeOfResult.width > r.sizeOfResult.width) )
{
smallestResult = r ;
}
//let only the results in that are smaller than the maxwidht or equal
if ( r.sizeOfResult.width <= maxWidth )
{
//the best one will be the one with the smallest heigh. everyone with the same height the best one will be the smallest
//if ( biggestResult == null || biggestResult.sizeOfResult.width < r.sizeOfResult.width || biggestResult.sizeOfResult.height > r.sizeOfResult.height)
if ( (biggestResult == null) || (biggestResult.sizeOfResult.height > r.sizeOfResult.height) || ((biggestResult.sizeOfResult.height == r.sizeOfResult.height) && (biggestResult.sizeOfResult.width > r.sizeOfResult.width)) )
{
biggestResult = r ;
}
}
}
if ( biggestResult != null )
{
this.result = biggestResult ;
}
else if ( smallestResult != null )
{
this.result = smallestResult ;
}
else
{
this.result = null;
}
return this.result.sizeOfResult ;
}
/**
* Checks which of the previously calculated results will be used to match the
* given maxWidth.<br>
* If no result fits it a new one will be calculated. First every needed brakpoint will
* be added. If it still does not fit breakpoits will be fored. If the maxWidth is to small
* to render a singel Char the Dimension (0, 0) will be returned.
* <br>
*
* @param maxWidth The maximum available size for this expression.
* @return The dimensions that will be needed to perform the rendering later.
*/
public Dimension getNeededSizeAll_ ( int maxWidth )
{
// JAVA makes teh Window very samll at first (-56)
// catch this
//TODO was macht er, wenn er zu wenig Platz hat?
//if ( maxWidth <= -56 )
//{
// return new Dimension ( 0 , 0 ) ;
//}
// first, find out if the normal algorithem (getNeededSizeAll)
// is ok to render it
// if ther is no space available for the expression, e.g. the rules are to wide
// calculate the normal one to
Dimension dimOfOne = getNeededSize ( maxWidth ) ;
if ( dimOfOne.width <= maxWidth || maxWidth < 20 )
{
// so use it
return dimOfOne ;
}
// else find out everey needed breakpoint
// get all Breakpoints
ArrayList < Integer > allBreakPoints = new ArrayList < Integer > ( ) ;
for ( int i = 0 ; i < this.results.size ( ) ; i ++ )
{
for ( int j = 0 ; j < this.results.get ( i ).breakOffsets.size ( ) ; j ++ )
{
allBreakPoints.add ( this.results.get ( i ).breakOffsets.get ( j ) ) ;
}
}
// sort all Breakpoints them
Collections.sort ( allBreakPoints ) ;
// remember the used ones
ArrayList < Integer > useBreakPoints = new ArrayList < Integer > ( ) ;
// these results will be used for printing
this.resultsForPrinting.clear ( ) ;
int actualMaxNeededWidth = 0 ;
int actualWidth = 0 ;
// int actualPosiotion=0;
PrettyCharIterator it = this.prettyString.toCharacterIterator ( ) ;
int indexOfActualC = 0 ;
int widthOfActualC = 0 ;
for ( char c = it.first ( ) ; c != CharacterIterator.DONE ; c = it.next ( ) , indexOfActualC ++ )
{
// Find out the width of the actual char
widthOfActualC = 0 ;
switch ( it.getStyle ( ) )
{
case IDENTIFIER :
widthOfActualC += AbstractRenderer.identifierFontMetrics.charWidth ( c ) ;
break ;
case NONE :
widthOfActualC += AbstractRenderer.expFontMetrics.charWidth ( c ) ;
break ;
case KEYWORD :
widthOfActualC += AbstractRenderer.keywordFontMetrics.charWidth ( c ) ;
break ;
case CONSTANT :
widthOfActualC += AbstractRenderer.constantFontMetrics.charWidth ( c ) ;
break ;
case COMMENT :
break ;
case TYPE :
widthOfActualC += AbstractRenderer.typeFontMetrics.charWidth ( c ) ;
break ;
}
// width of the expression is
actualWidth = actualWidth + widthOfActualC ;
actualMaxNeededWidth = Math.max ( actualWidth , actualMaxNeededWidth ) ;
// The expression has grwon to big
// the programm will find the very next brakepoint to the actual position
// being smaller then it. If there is no possible breakpoint, it will
// force to break at this position
if ( actualWidth > maxWidth )
{
if (indexOfActualC <= 1)
{
// the maxWidth is smaller than the first char so it will never fit in
return new Dimension (0,0);
}
// use the next breakpoint bevor the actual position. If there is no breakpoint
// a breakpoint will be inserted
if ( allBreakPoints.size ( ) == 0 ) //no brakpoint available
{
//use the actual position -1
//useBreakPoints.add (Math.max(0, j - 1) ) ;
useBreakPoints.add (new Integer(indexOfActualC - 1));
indexOfActualC -- ;
it.setIndex(indexOfActualC - 1);
}
else if (allBreakPoints.get(0).intValue() > indexOfActualC - 1) // The first breakpoint is after the actual position
{
useBreakPoints.add(new Integer(indexOfActualC - 1));
indexOfActualC--;
it.setIndex((indexOfActualC - 1));
}
else
{
for ( int i = 1 ; i < allBreakPoints.size ( ) ; i ++ )
{
if ( allBreakPoints.get ( i ).intValue ( ) > indexOfActualC )
{
//i is after the actual position, check, if i-1 is already used
if ( ! useBreakPoints.contains ( allBreakPoints.get ( i - 1 ) ) )
{
//so use it
useBreakPoints.add ( allBreakPoints.get ( i - 1 ) ) ;
//go back to the breakpoint
it.setIndex ( allBreakPoints.get ( i - 1 ).intValue() ) ;
indexOfActualC = allBreakPoints.get ( i - 1 ).intValue() ;
}
// i is allready in use, so ther will be a breakpoint
else
{
useBreakPoints.add ( new Integer(indexOfActualC - 1 )) ;
indexOfActualC -- ;
it.setIndex ( ( indexOfActualC - 1 ) ) ;
}
break ;
}
}
}
// next line will start with width 0
actualWidth = 0 ;
}
}
this.result = new CheckerResult ( ) ;
this.result.rows = 0 ;
this.result.sizeOfResult.height = 0 ;
this.result.sizeOfResult.width = actualMaxNeededWidth ;
for ( Integer i : useBreakPoints )
{
this.result.breakOffsets.add ( i ) ;
}
this.result.sizeOfResult.height = AbstractRenderer.getAbsoluteHeight() * ( this.result.breakOffsets.size ( ) + 2 ) ;
this.result.rows = this.result.breakOffsets.size ( ) + 1 ;
return ( this.result.sizeOfResult ) ;
}
/**
* Checks the results for all possible annotations.
*/
public void checkLinewraps ( )
{
this.results.clear ( ) ;
this.results.add ( checkLinewrap ( null ) ) ;
for ( PrettyAnnotation annotation : this.prettyString.getAnnotations ( ) )
{
this.results.add ( checkLinewrap ( annotation ) ) ;
}
}
/**
* Checks the size that would be needed when the expression would be rendered
* using the given Annotation.
*
* @param annotation
* @return CheckerResult
*/
private CheckerResult checkLinewrap ( PrettyAnnotation annotation )
{
CheckerResult resultToReturn = new CheckerResult ( ) ;
resultToReturn.rows = 1 ;
if ( annotation != null )
{
//for ( int i = 0 ; i < annotation.getBreakOffsets ( ).length ; i ++ )
for (int anno : annotation.getBreakOffsets() )
{
//result.breakOffsets.add ( annotation.getBreakOffsets ( ) [ i ] ) ;
resultToReturn.breakOffsets.add(new Integer(anno));
}
}
resultToReturn.sizeOfResult.height = AbstractRenderer.getAbsoluteHeight();
//System.out.println("Größe: "+AbstractRenderer.fontHeight + ", Leading"+AbstractRenderer.fontLeading);
PrettyCharIterator it = this.prettyString.toCharacterIterator ( ) ;
int i = 0 ;
int w = 0 ;
for ( char c = it.first ( ) ; c != CharacterIterator.DONE ; c = it.next ( ) , i ++ )
{
for ( int j = 0 ; j < resultToReturn.breakOffsets.size ( ) ; j ++ )
{
if ( resultToReturn.breakOffsets.get ( j ).intValue ( ) == i )
{
// System.out.println("Treffer, die Zeilen werden um eins erhöht!");
resultToReturn.sizeOfResult.height += AbstractRenderer.getAbsoluteHeight();
resultToReturn.sizeOfResult.width = Math.max ( w , resultToReturn.sizeOfResult.width ) ;
resultToReturn.rows ++ ;
// let the next line be to big fonts indentated
w = AbstractRenderer.expFontMetrics.stringWidth ( "GG" ) ;
break ;
}
}
switch ( it.getStyle ( ) )
{
case IDENTIFIER :
w += AbstractRenderer.identifierFontMetrics.charWidth ( c ) ;
break ;
case NONE :
w += AbstractRenderer.expFontMetrics.charWidth ( c ) ;
break ;
case KEYWORD :
w += AbstractRenderer.keywordFontMetrics.charWidth ( c ) ;
break ;
case CONSTANT :
w += AbstractRenderer.constantFontMetrics.charWidth ( c ) ;
break ;
case COMMENT :
break ;
case TYPE :
w += AbstractRenderer.typeFontMetrics.charWidth ( c ) ;
break ;
}
// System.out.println("Rows: "+result.rows);
}
resultToReturn.sizeOfResult.width = Math.max ( w , resultToReturn.sizeOfResult.width ) ;
// the result contains the size of the whole expression when renderd
// with the given Annotation
return resultToReturn ;
}
/**
* checks, if the int test is in the list of Bonds and returns the position.
*
* @param test int to finde
* @param list list to serach in
* @return int - the position, -1 if the int is not in the List
*/
public static int getPositionInList ( int test , ArrayList < Bonds > list )
{
int result = - 1 ;
for ( int i = 0 ; i < list.size ( ) ; i ++ )
{
int min = list.get ( i ).getStartOffset ( ) ;
int max = list.get ( i ).getEndOffset ( ) ;
// LinkedList<PrettyAnnotation> other = list.get(i).marks;
// System.out.println("alles zwischen "+min+" und "+max+ " wird
// makiert.");
// list.get(i).
// int tmp = 1;
// System.out.println(""+tmp);
if ( ( test <= max ) && ( test >= min ) )
{
return i ;
}
// else
{
ArrayList < PrettyAnnotation > rest = list.get ( i ).getPrettyAnnotation ( );
for ( int j = 0 ; j < rest.size ( ) ; j ++ )
{
PrettyAnnotation tmp = rest.get ( j ) ;
int min1 = tmp.getStartOffset ( ) ;
int max1 = tmp.getEndOffset ( ) ;
if ( ( test <= max1 ) && ( test >= min1 ) )
{
return i ;
}
}
}
}
return result ;
}
/**
* checks if the given int test is the one of the first elements
*
* @param test the int to find
* @param list the list to search
* @return true/false
*/
public static boolean isFirstInListe ( int test , ArrayList < Bonds > list )
{
// it is not first in List
boolean result = false ;
for ( int i = 0 ; i < list.size ( ) ; i ++ )
{
int min = list.get ( i ).getStartOffset ( ) ;
int max = list.get ( i ).getEndOffset ( ) ;
// if it is first in List
if ( ( test <= max ) && ( test >= min ) )
{
// return true
return true ;
}
}
// return false if for was alway wrong
return result ;
}
/**
* Dose the same as the render mathode, but corrects the y value to render the prettystring
* to the the baseline
*
* @param x
* @param y
* @param width
* @param height
* @param gc
* @param bound
* @param toListenForM
*/
public void renderBase ( int x , int y ,int width , int height, Graphics gc , ShowBonds bound , ToListenForMouseContainer toListenForM )
{
render (x, y-(height / 2) - fontAscent / 2, width, height, gc, bound, toListenForM);
}
/**
* Renders the Prettystring. <br>
* <br>
* The Prettystring will be rendered verticaly centered between the position y
* and (y + height).
*
* @param x Left position where the rendering should take place
* @param y Top position where the rendering should take place
* @param height The height that is available for the rendering.
* @param width The width that is available for the rendering.
* @param gc The Graphics context that will be used to render
* @param bound
* @param toListenForM
*/
public void render ( int x , int y ,int width , int height, Graphics gc , ShowBonds bound , ToListenForMouseContainer toListenForM )
{
this.toListenForMouse = toListenForM ;
// get The MousePosition
Point mousePosition = this.toListenForMouse.getHereIam ( ) ;
boolean mouseOver = true ;
if ( (mousePosition.x == 0) && (mousePosition.y == 0) )
{
mouseOver = false ;
}
// get the Char-Position to the MousePosition
// count the chars by using the charwidth adding till the mouseposition is
// found for functioning in more than 1 line the lines are count
// the breakoffsets to find the line
//check the linewraping
int arraySize = 0 ;
if ( this.result != null )
{
arraySize = this.result.breakOffsets.size ( ) ;
}
else
{
//The expression has no breakpoint!
this.result = new CheckerResult ();
}
int [ ] breakOffsets = new int [ arraySize ] ;
for ( int i = 0 ; i < this.result.breakOffsets.size ( ) ; i ++ )
{
breakOffsets [ i ] = this.result.breakOffsets.get ( i ).intValue ( ) ;
}
// an Iterator through all chars
PrettyCharIterator it = this.prettyString.toCharacterIterator ( ) ;
// find out wher the mousepointer is, at wich char
int charPosition = x ;
int charIndex = 0 ;
FontMetrics fm = null ;
fm = AbstractRenderer.expFontMetrics ;
// if the cahr is not in the first line the startvalue must be different
int lineCount = 0 ;
int posY_ = y + height / 2 ;
posY_ += AbstractRenderer.fontAscent / 2 ;
float addY_ = ( this.result.rows - 1 ) / 2.0f ;
addY_ *= AbstractRenderer.getAbsoluteHeight();
posY_ -= addY_ ;
// mousePosition[1] is the x-coordinate, start to count at 1
lineCount = ( ( mousePosition.y - ( posY_ - AbstractRenderer.getAbsoluteHeight() ) ) / AbstractRenderer.getAbsoluteHeight ( ) ) + 1 ;
// test if the mouse is realy over a char
boolean highlight = false;
if ( (lineCount >= 1) && (lineCount <= height / AbstractRenderer.getAbsoluteHeight()) ) //schon mal die richtige Zeile
{
if ( (mousePosition.x > x) && (mousePosition.x <= x + width) && (mousePosition.y > y) && (mousePosition.y <= y + height) )
{
highlight = true;
}
}
if ( (lineCount > 1) && (lineCount <= height / AbstractRenderer.getAbsoluteHeight()) )
{
// may be the mousepointer is under the expression
try
{
charIndex = breakOffsets [ lineCount - 2 ] ;
}
catch ( IndexOutOfBoundsException e )
{
// nothing to do
}
}
// add the width of the chars till the mousepointer is reached an dcount the
// chars. charIdenx will be the index where the mousePointer is over
for ( char c = it.setIndex ( Math.max ( charIndex , 0 ) ) ; c != CharacterIterator.DONE ; c = it.next ( ) , charIndex++ )
{
switch ( it.getStyle ( ) )
{
case KEYWORD :
gc.setFont ( AbstractRenderer.keywordFont ) ;
gc.setColor ( AbstractRenderer.keywordColor ) ;
fm = AbstractRenderer.keywordFontMetrics;
break ;
case IDENTIFIER :
gc.setFont ( AbstractRenderer.identifierFont ) ;
gc.setColor ( AbstractRenderer.identifierColor ) ;
fm = AbstractRenderer.identifierFontMetrics;
break ;
case NONE :
gc.setFont ( AbstractRenderer.expFont ) ;
gc.setColor ( AbstractRenderer.expColor ) ;
fm = AbstractRenderer.expFontMetrics;
break ;
case CONSTANT :
gc.setFont ( AbstractRenderer.constantFont ) ;
gc.setColor ( AbstractRenderer.constantColor ) ;
fm = AbstractRenderer.constantFontMetrics;
break ;
case COMMENT :
continue ;
case TYPE :
gc.setFont ( AbstractRenderer.typeFont ) ;
gc.setColor ( AbstractRenderer.typeColor ) ;
fm = AbstractRenderer.typeFontMetrics;
break ;
}
int charWidth = fm.stringWidth ( "" + c ) ;
charPosition = charPosition + charWidth ;
if ( charPosition > mousePosition.x )
{
break ;
}
}
// get the annotations
ArrayList < Bonds > annotationsList = new ArrayList < Bonds > ( ) ;
if ( mouseOver )
{
annotationsList = bound.getAnnotations ( ) ;
}
//check if the mouse stands on a char which is to highlight
//rightAnnotationList will be the annotation to underline, if -1 there is no underlining
int rightAnnotationList = getPositionInList ( charIndex , annotationsList ) ;
// get the starting offsets x is just the left border
// y will be the center of the space available minus the
// propper amount of rows
int i = 0 ;
int posX = x ;
int posY = y + AbstractRenderer.fontAscent;
// start and end position for the underlining (if the pointer is over the button or the meta rule)
int underlineStart = - 1 ;
int underlineEnd = - 1 ;
if ( this.underlineAnnotation != null )
{
underlineStart = this.underlineAnnotation.getStartOffset ( ) ;
underlineEnd = this.underlineAnnotation.getEndOffset ( ) ;
}
// now we can start to render the expression
// everey char
for ( char c = it.first ( ) ; c != CharacterIterator.DONE ; c = it.next ( ) , i ++ )
{
for ( int j = 0 ; j < breakOffsets.length ; j ++ )
{
if ( breakOffsets [ j ] == i )
{
posY += AbstractRenderer.getAbsoluteHeight() ;
posX = x ;
}
}
fm = AbstractRenderer.expFontMetrics ;
int charWidth = fm.stringWidth ( "" + c ) ;
// just corrects the posY to start at baseline instead of the middel
int posYC = posY - ( AbstractRenderer.getAbsoluteHeight ( ) - fm.getDescent ( ) ) ;
// look for aktual char is in this list (-1 stands for false)
if ( ! ( this.toListenForMouse.isMark ( ) ) && (( getPositionInList ( i , annotationsList ) ) > - 1) )
{
// tell mouselistener in CompoundExpression to react at these positions
// posY dose not stand for the baseline but for the center, so we have
// to use corrected values for posY
// he will just react from baseline to upper corner of char, not to
// lower corner of char
this.toListenForMouse.add ( posX - 1,posYC, posX + charWidth + 1,posYC + fm.getAscent ( ) ) ;
}
if ( this.toListenForMouse.isMark ( )
&& (getPositionInList ( i , annotationsList ) != - 1) )
{
// if the char will be highlited first teh font and color will be set to
// normal
// and later it will be overwritten
gc.setFont ( AbstractRenderer.identifierFont ) ;
gc.setColor ( AbstractRenderer.identifierColor ) ;
// if actual char is in the same List as the list in wich the char where
// MousePointer is
// the char should be highlightet
if ( (getPositionInList ( i , annotationsList ) == rightAnnotationList) && highlight)
{
// type highlighted in bold
fm.getFont ( ).getName ( ) ;
Font test = new Font ( fm.getFont ( ).getName ( ) , Font.BOLD , fm
.getFont ( ).getSize ( ) ) ;
gc.setFont ( test ) ;
// let font be in right color
// the first in the list is the Id an should be highlighted in an
// other color than the Bounded ones
if ( isFirstInListe ( i , annotationsList ) )
{
gc.setColor ( Theme.currentTheme ( ).getBindingIdColor ( ) ) ;
}
else
{
gc.setColor ( Theme.currentTheme ( ).getBoundIdColor ( ) ) ;
}
// underline the actual char
gc.drawLine ( posX , posY + 1 , posX + charWidth , posY + 1 ) ;
}
}
// manipulating font witch is not marked as bounded
else
{
// select the proppert font and color for the character
switch ( it.getStyle ( ) )
{
case KEYWORD :
gc.setFont ( AbstractRenderer.keywordFont ) ;
gc.setColor ( AbstractRenderer.keywordColor ) ;
fm = AbstractRenderer.keywordFontMetrics;
break ;
case IDENTIFIER :
gc.setFont ( AbstractRenderer.identifierFont ) ;
gc.setColor ( AbstractRenderer.identifierColor ) ;
fm = AbstractRenderer.identifierFontMetrics;
break ;
case NONE :
gc.setFont ( AbstractRenderer.expFont ) ;
gc.setColor ( AbstractRenderer.expColor ) ;
fm = AbstractRenderer.expFontMetrics;
break ;
case CONSTANT :
gc.setFont ( AbstractRenderer.constantFont ) ;
gc.setColor ( AbstractRenderer.constantColor ) ;
fm = AbstractRenderer.constantFontMetrics;
break ;
case COMMENT :
continue ;
case TYPE :
gc.setFont ( AbstractRenderer.typeFont ) ;
gc.setColor ( AbstractRenderer.typeColor ) ;
fm = AbstractRenderer.typeFontMetrics;
break ;
}
}
if ( (i >= underlineStart) && (i <= underlineEnd) )
{
// the current character is in the range, where underlining
// should happen
// save the current color, it will become resetted later
Color color = gc.getColor ( ) ;
gc.setColor ( AbstractRenderer.underlineColor ) ;
// draw the line below the character
// int charWidth = fm.stringWidth("" + c);
gc.drawLine ( posX , posY + 1 , posX + charWidth , posY + 1 ) ;
// reset the color for the characters
gc.setColor ( color ) ;
}
if ( this.alternativeColor != null )
{
gc.setColor ( this.alternativeColor ) ;
}
// draw the character and move the position
gc.drawString ( "" + c , posX , posY ) ;
posX += fm.stringWidth ( "" + c ) ;
// go on to the next character
}
}
}