package de.unisiegen.tpml.graphics.components ;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;
import de.unisiegen.tpml.core.expressions.Expression;
import de.unisiegen.tpml.core.interpreters.Store;
import de.unisiegen.tpml.core.typechecker.TypeEnvironment;
import de.unisiegen.tpml.core.util.Environment;
import de.unisiegen.tpml.graphics.renderer.AbstractRenderer;
import de.unisiegen.tpml.graphics.renderer.EnvironmentRenderer;
import de.unisiegen.tpml.graphics.renderer.PrettyStringRenderer;
import de.unisiegen.tpml.graphics.renderer.ToListenForMouseContainer;
/**
* this class renders the coumpoundexpression
*
*
* @param <S>
* @param <E>
*/
public class CompoundExpression < S , E > extends JComponent
{
/**
*
*/
private static final long serialVersionUID = - 7653329118052555176L ;
/**
* Renderer that is used to render the expression
*/
private PrettyStringRenderer expressionRenderer ;
/**
* The current expression.
*/
private Expression expression ;
/**
* The expression that will be underlined during the rendering.
*/
private Expression underlineExpression ;
/**
* The size of the expression.
*/
private Dimension expressionSize ;
/**
* The current environment. If the environment is <i>null</i> no environment
* is rendered.
*/
private Environment < S , E > environment ;
/**
* Renderer that is used to render the environment.
*/
private EnvironmentRenderer < S , E > environmentRenderer ;
/**
* The size of the environment.
*/
private Dimension environmentSize ;
/**
* The width of the braces around the expression and the environment.
*/
private int braceSize ;
/**
* Whether the expression should be wrapped if there is not enough space to
* render it in on line.<br>
* Actualy this is only used with the result of the BigStepGUI.
*/
private boolean noLineWrapping ;
/**
* If this color is given all colors of the {@link AbstractRenderer} are
* ignored and only this color is used.
*/
private Color alternativeColor ;
/**
* The arrow symbol that is used between the environment and the expression
* when used within the
* {@link de.unisiegen.tpml.graphics.typechecker.TypeCheckerNodeComponent}
*/
private static String arrowStr = " \u22b3 " ;
/**
* Initialises the CompoundExpression with the default values.<br>
* <br>
* The braces have a size of 10 pixes, no underlining and the color of the
* {@link AbstractRenderer} are ignored.
*/
private ShowBonds bonds ;
/**
* the list of points where the mouseovereffect will be react
*/
private ToListenForMouseContainer toListenForMouse ;
/**
* the constructor
*
*/
public CompoundExpression ( )
{
super ( ) ;
this.bonds = new ShowBonds ( ) ;
this.toListenForMouse = new ToListenForMouseContainer ( ) ;
this.alternativeColor = null ;
this.braceSize = 10 ;
this.underlineExpression = null ;
this.addMouseMotionListener ( new MouseMotionAdapter ( )
{
@ Override
public void mouseMoved ( MouseEvent event )
{
handleMouseMoved ( event ) ;
}
} ) ;
this.addMouseListener ( new MouseAdapter ( )
{
@ Override
public void mouseExited ( MouseEvent e )
{
CompoundExpression.this.getToListenForMouse().reset();
CompoundExpression.this.getToListenForMouse().setMark ( false ) ;
CompoundExpression.this.repaint ( ) ;
}
} ) ;
}
/**
* Sets an alternative color.<br>
* <br>
* Both renderers, the {@link PrettyStringRenderer} and the
* {@link EnvironmentRenderer}, are updated with this color.
*
* @param color The alternative color.
*/
public void setAlternativeColor ( Color color )
{
this.alternativeColor = color ;
if ( this.expressionRenderer != null )
{
this.expressionRenderer.setAlternativeColor ( color ) ;
}
if ( this.environmentRenderer != null )
{
this.environmentRenderer.setAlternativeColor ( color ) ;
}
}
/**
* Sets the expression, that should be underlined.
*
* @param pUnderlineExpression
*/
public void setUnderlineExpression ( Expression pUnderlineExpression )
{
boolean needsRepaint = this.underlineExpression != pUnderlineExpression ;
this.underlineExpression = pUnderlineExpression ;
if ( this.expressionRenderer != null )
{
this.expressionRenderer
.setUndelinePrettyPrintable ( this.underlineExpression ) ;
}
if ( needsRepaint )
{
repaint ( ) ;
}
}
/**
* Causes the PrettyStringRenderer to recheck the linewraps
*/
public void reset ( )
{
if ( this.expressionRenderer != null )
{
this.expressionRenderer.checkLinewraps ( ) ;
}
}
/**
* Handles whether a ToolTip should be displayed for the environment, if some
* parts of it are collapsed.
*
* @param event
*/
void handleMouseMoved ( MouseEvent event )
{
//tell the PrettyStringRenderer where the mouse pointer is
this.toListenForMouse.setHereIam ( event.getX ( ) , event.getY ( ) ) ;
//first, we do not want to mark anything, we are waiting for mouse pointer is over one bounded id
this.toListenForMouse.setMark ( false ) ;
CompoundExpression.this.repaint ( ) ;
//note if to mark or not to mark
boolean mark = false;
//walk throu the postions where to mark
for ( int t = 0 ; t < this.toListenForMouse.size ( ) ; t++)
{
//get position of pointer, these are rectangles. These positions are made by the PrettyStringRenderer
Rectangle r = this.toListenForMouse.get ( t ) ;
int pX = r.x;
int pX1 = r.x+r.width ;
int pY = r.y;
int pY1 = r.y+r.height;
// fnde out if pointer is on one of the chars to mark
if ((event.getX() >= pX) && (event.getX() <= pX1) && (event.getY() >= pY) && (event.getY() <= pY1))
//if ( ( event.getX ( ) >= pX ) && ( event.getX ( ) <= pX1 ) )
{
//just note it
mark = true;
}
}
//if the pointer is on one of the bounded chars
if (mark)
{
//we want to habe marked
this.toListenForMouse.setMark ( true ) ;
CompoundExpression.this.repaint ( ) ;
}
else
{
//we do not want to see anything marked
this.toListenForMouse.setMark ( false ) ;
this.toListenForMouse.reset();
CompoundExpression.this.repaint ( ) ;
}
if ( this.environmentRenderer != null
&& this.environmentRenderer.isCollapsed ( ) )
{
Rectangle r = this.environmentRenderer.getCollapsedArea ( ) ;
if ( event.getX ( ) >= r.x && event.getX ( ) <= r.x + r.width )
{
setToolTipText ( this.environmentRenderer.getCollapsedString ( ) ) ;
}
else
{
setToolTipText ( null ) ;
}
}
else
{
setToolTipText ( null ) ;
}
}
/**
* Sets whether the expression should be wrapped or not.
*
* @param pNoLineWrapping
*/
public void setNoLineWrapping ( boolean pNoLineWrapping )
{
this.noLineWrapping = pNoLineWrapping ;
}
/**
* Sets the expression that should rendered.
*
* @param pExpression
*/
public void setExpression ( Expression pExpression )
{
// check if we have a new expression
if (this.expression != pExpression)
{
// update to the new expression
this.expression = pExpression;
// because of the bounds are cached we need a new one. The expression
// might change by translating in coresyntax
this.bonds = new ShowBonds();
this.bonds.load(this.expression);
// check what to do with the renderer
if (this.expression == null)
{
this.expressionRenderer = null;
}
else
{
if (this.expressionRenderer == null)
{
this.expressionRenderer = new PrettyStringRenderer();
this.expressionRenderer.setAlternativeColor(this.alternativeColor);
}
this.expressionRenderer.setPrettyString(this.expression.toPrettyString());
// reset the underlineExpression
setUnderlineExpression(this.underlineExpression);
}
// be sure to schedule a repaint
repaint();
}
}
/**
* Sets the environment taht should be rendered.
*
* @param pEnvironment
*/
public void setEnvironment ( Environment < S , E > pEnvironment )
{
// check if we have a new environment
if ( this.environment != pEnvironment )
{
// update to the new environment
this.environment = pEnvironment ;
// check what to do with the renderer
if ( this.environment == null )
{
this.environmentRenderer = null ;
}
else
{
if ( this.environmentRenderer == null )
{
this.environmentRenderer = new EnvironmentRenderer < S , E > ( ) ;
this.environmentRenderer.setAlternativeColor ( this.alternativeColor ) ;
}
this.environmentRenderer.setEnvironment ( this.environment ) ;
}
// be sure to schedule a repaint
repaint ( ) ;
}
}
/**
* Calculates the size needed to propperly render the compoundExpression
*
* @param pMaxWidth
* @return
*/
public Dimension getNeededSize ( int pMaxWidth )
{
int maxWidth = pMaxWidth;
// TODO was ist, wenn der Platz zu klein wird...
// if (maxWidth < 0)
// {
// return new Dimension(0,0);
// }
Dimension result = new Dimension ( 0 , 0 ) ;
if ( this.noLineWrapping )
{
// to guaranty that no line wrapping should be performed
// set the maxWidth = MAX_INT
maxWidth = Integer.MAX_VALUE ;
}
// check whether there is an environment.
if ( this.environment != null )
{
this.environmentSize = this.environmentRenderer.getNeededSize ( ) ;
result.height = this.environmentSize.height ;
if ( this.environment instanceof Store )
{
/*
* the store will be used within the smallstep and bigstep interpreter
* the rendering will appear like this: (expression [store])
*/
result.width += 2 * this.braceSize ;
// there will be a bit of free space between the environment
// and the expression
result.width += this.braceSize ;
// and the environment size will be missing asswell
result.width += this.environmentSize.width ;
/*
* XXX: Decision how the expression should be wrapped
*/
maxWidth -= result.width ; // ???
}
else if ( this.environment instanceof TypeEnvironment )
{
/*
* the typeenvironment will be used within the typechecker the rendering
* will appear like this: [type] |> expression
*/
// the resulting size contains the environment
result.width += this.environmentSize.width ;
// and the |> character
result.width += AbstractRenderer.getTextFontMetrics ( ).stringWidth (
CompoundExpression.arrowStr ) ;
maxWidth -= result.width ;
}
}
if ( this.expression != null && this.expressionRenderer != null )
{
// now check the size still available for the expression
this.expressionSize = this.expressionRenderer.getNeededSizeAll_ ( maxWidth ) ;
result.width += this.expressionSize.width ;
result.height = Math.max ( result.height , this.expressionSize.height ) ;
}
return result ;
}
/**
* The actualy rendering method.
*
* @param gc The Graphics object that will be used to render the stuff.
*/
@ Override
protected void paintComponent ( Graphics gc )
{
//TODO Only for test to make yompoundexpression visible
//it also displays how often the exptresso is rednerd while srolling...
//gc.setColor (Color.yellow);
//--------------------------------
//Color [] test = new Color [10];
//test[0] = Color.yellow;
//test[1] = Color.red;
//test[2] = Color.green;
//test[3] = Color.cyan;
//test[4] = Color.green;
//test[5] = Color.lightGray;
//test[6] = Color.blue;
//test[7] = Color.gray;
//test[8] = Color.magenta;
//test[9] = Color.orange;
//double get = Math.random();
//int getR = (int) (get*10);
//gc.setColor (test[getR]);
//gc.fillRect(0, 0, getWidth () - 1, getHeight () - 1);
//--------------------------------
// make sure that we have an expression renderer
if ( this.expressionRenderer == null )
{
return ;
}
// assuming the size of the component will suffice, no testing
// of any sizes will happen.
/*
* just to get reminded: no environment: expression storeenvironment:
* (expression [env]) typeenvironment: [env] |> expression
*/
int posX = 0 ;
int posY = 0 ;
// if there is an environment and it is of type Store
// draw the braces around the entire expression with environment
if ( this.environment instanceof Store )
{
posX += this.braceSize ;
gc.drawArc ( 0 , 0 , this.braceSize , getHeight ( ) , 90 , 180 ) ;
gc.drawArc ( getWidth ( ) - 1 - this.braceSize , 0 , this.braceSize ,
getHeight ( ) , 270 , 180 ) ;
}
// if there is no environment or the environment is of type
// Store, the entire expression (with environment) will begin
// with the expression
if ( this.environment == null || this.environment instanceof Store )
{
//this.expressionRenderer.render ( posX , posY , getHeight ( ) , gc ,
// bonds , toListenForMouse ) ;
this.expressionRenderer.render ( posX , posY , getWidth(), getHeight () , gc , this.bonds , this.toListenForMouse ) ;
posX += this.expressionSize.width ;
// if there is an environment render it now
if ( this.environment != null )
{
posX += this.braceSize ;
this.environmentRenderer.renderer ( posX , posY ,
this.environmentSize.width , getHeight ( ) , gc ) ;
}
}
else if ( this.environment instanceof TypeEnvironment )
{
// draw the environment first
this.environmentRenderer.renderer ( posX , posY , this.environmentSize.width , getHeight ( ) , gc ) ;
//this.environmentRenderer.renderer ( posX , posY , this.environmentSize.width , environmentSize.height , gc ) ;
posX += this.environmentSize.width ;
gc.setFont ( AbstractRenderer.getTextFont ( ) ) ;
gc.setColor ( AbstractRenderer.getTextColor ( ) ) ;
gc.drawString ( CompoundExpression.arrowStr , posX , posY+AbstractRenderer.getFontAscent() ) ;
posX += AbstractRenderer.getTextFontMetrics ( ).stringWidth (
CompoundExpression.arrowStr ) ;
// draw the expression at the last position.
this.expressionRenderer.render ( posX , posY , getWidth() ,getHeight ( ) , gc , this.bonds , this.toListenForMouse ) ;
//this.expressionRenderer.render ( posX , posY , getWidth() ,AbstractRenderer.getAbsoluteHeight (), gc , bonds , toListenForMouse ) ;
}
//TODO DEbugging
// gc.setColor (Color.YELLOW);
// gc.drawRect(0, 0, getWidth () - 1, getHeight () - 1);
}
/**
* returns the current expression.
*
* @return the expression
*/
public Expression getExpression ( )
{
return this.expression ;
}
/**
* @return the toListenForMouse
*/
public ToListenForMouseContainer getToListenForMouse()
{
return this.toListenForMouse;
}
}