/**
* Copyright (C) 2013 Tokanagrammar Team
*
* This is a jigsaw-like puzzle game,
* except each piece is token from a source file,
* and the 'complete picture' is the program.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package edu.umb.cs.gui;
import edu.umb.cs.source.SourceToken;
import edu.umb.cs.source.SourceTokenKind;
import edu.umb.cs.source.std.EmptyToken;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.WritableImage;
import javax.imageio.ImageIO;
import static edu.umb.cs.source.SourceTokenKind.*;
/**
* Handles the tokens that go on the LHS
* @author Matt
*
*/
public class LHSTokenIconizer {
final static int TOKEN_HEIGHT = 20; //token height is always the same
final static int MIN_WIDTH = 32; //the minimum token width
final static Font TOKEN_FONT = new Font("Courier New", Image.SCALE_DEFAULT, 12);
private static int index;
static final Map<SourceTokenKind, Color> tokenFontColors = computeColorsMap();
private static Map<SourceTokenKind, Color> computeColorsMap()
{
EnumMap<SourceTokenKind, Color> ret = new EnumMap<>(SourceTokenKind.class);
Color orange = new Color (206, 123, 0);
ret.put(KEYWORD, Color.blue);
ret.put(NUM_LITERAL, Color.black);
ret.put(CHAR_LITERAL, orange);
ret.put(IDENTIFIER, Color.black);
ret.put(STRING_LITERAL, orange);
ret.put(SEPARATOR, Color.black);
ret.put(OPERATOR, Color.black);
return Collections.unmodifiableMap(ret);
}
/**
* Take a token and makes a graphic representation of it.
* @param token
* @return an IconizedToken
*/
public static LHSIconizedToken iconizeToken(SourceToken token){
SourceTokenKind tokenKind = token.kind();
BufferedImage image = null;
BufferedImage finalImage = null;
Graphics graphics = null;
try{
if(tokenKind == SourceTokenKind.EMPTY)
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/removed_.fw.png"));
else
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/noop_.fw.png"));
} catch(IOException e){
e.printStackTrace();
}
graphics = image.getGraphics();
FontMetrics fm = graphics.getFontMetrics(TOKEN_FONT);
switch(tokenKind)
{
case EMPTY:
finalImage = finalizeImage(image, EmptyToken.INSTANCE,TOKEN_HEIGHT, MIN_WIDTH, Image.SCALE_DEFAULT);
break;
case TAB:
finalImage = finalizeImage(image, token,TOKEN_HEIGHT, 20 , Image.SCALE_DEFAULT);
break;
default:
finalImage = finalizeImage(image, token,TOKEN_HEIGHT, fm.stringWidth(token.image()), Image.SCALE_DEFAULT);
}
WritableImage writableImage = SwingFXUtils.toFXImage(finalImage, null);
return new LHSIconizedToken(writableImage, token, index++);
}
/**
* Creates a "pretty" clear token so the user can tell it's a placed token.
* @param token
* @return an IconizedToken
*/
public static LHSIconizedToken createSingleIconizedToken(SourceToken token, int newIndex){
SourceTokenKind kind = token.kind();
BufferedImage image = null;
BufferedImage finalImage = null;
Graphics graphics = null;
int imagePadding = 20;
try
{
switch(kind)
{
case KEYWORD:
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/lucentPink.fw.png"));
break;
case IDENTIFIER:
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/lucentGreen.fw.png"));
break;
case STRING_LITERAL:
case CHAR_LITERAL:
case NUM_LITERAL:
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/lucentGreen.fw.png"));
break;
case SEPARATOR:
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/lucentYellow.fw.png"));
break;
case OPERATOR:
image = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("images/ui/tokens/lucentPurple.fw.png"));
break;
default:
System.out.println("Unexpected token kind: " + kind);
// TODO: handle error
}
}
catch (IOException e)
{
e.printStackTrace();
}
graphics = image.getGraphics();
FontMetrics fm = graphics.getFontMetrics(TOKEN_FONT);
if(fm.stringWidth(token.image()) < 25 || token.kind() == SourceTokenKind.EMPTY)
finalImage = finalizeImage(image, token,TOKEN_HEIGHT, MIN_WIDTH, Image.SCALE_DEFAULT);
else
finalImage = finalizeImage(image, token,TOKEN_HEIGHT, fm.stringWidth(token.image()) + imagePadding, Image.SCALE_DEFAULT);
WritableImage writableImage = SwingFXUtils.toFXImage(finalImage, null);
return new LHSIconizedToken(writableImage, token, newIndex);
}
/**
* Resizes an image, adds the string text, and adds occurrence subscript.
* @param originalImage
* @param height
* @param width
* @param type
* @return an image worth putting in the LDZ
*/
private static BufferedImage finalizeImage( Image originalImage,
SourceToken token,
int height,
int width,
int type){
String tokenImage = token.image();
SourceTokenKind kind = token.kind();
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
FontMetrics fm = g.getFontMetrics(TOKEN_FONT);
int strWidth = (fm.stringWidth(tokenImage));
int imageWidth = resizedImage.getWidth();
int textBegin = (imageWidth - strWidth) / 2;
int textHeight = (fm.getAscent() + (TOKEN_HEIGHT - (fm.getAscent() + fm.getDescent())) / 2 );
g.drawImage(originalImage, 0, 0, width, height, null);
g.setColor(tokenFontColors.get(kind));
g.setFont(TOKEN_FONT);
if (kind != SourceTokenKind.EMPTY)
g.drawString(tokenImage, textBegin, textHeight);
g.dispose();
return resizedImage;
}
// temp
public static void resetIndex()
{
index = 0;
}
}