/*
* Copyright (C) 2011 Alasdair C. Hamilton
*
* 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
* (at your option) 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 ket.display.box;
import geom.Offset;
import geom.Position;
import java.awt.*;
import java.awt.geom.*;
import java.util.Vector;
import ket.math.*;
import ket.display.ColourScheme;
/*
* handle display of a letter, symbol or word
*/
public class BoxWord extends Box {
Font font;
final String text;
double ascent;
//- Position topLeft = null;
//- Offset actualSize = null;
Vector<Integer> cumulativeWidths = null;
//double decent; // not yet used
/*
* If HORIZONTAL_STRETCH or VERTICAL_STRETCH is set then this factor
* describes how the font should be scaled horizontally.
*/
double xFactor;
/*
* If HORIZONTAL_STRETCH or VERTICAL_STRETCH is set then this factor
* describes how the font should be scaled vertically.
*/
double yFactor;
@Override
public Box cloneBox() {
return new BoxWord(getArgument(), text, getSettings());
}
public BoxWord(Argument argument, String text, long settings) {
super(argument, settings);
this.text = text;
this.font = null;
this.ascent = 0.0;
this.xFactor = 1.0;
this.yFactor = 1.0;
}
/*
* Change the font to a given size and style.
*/
@Override
protected void fontSetup(int parentFontSize) {
super.fontSetup(parentFontSize);
int style = 0;
if (hasProperty(BOLD_FONT)) {
style |= Font.BOLD;
} else if (hasProperty(PLAIN_FONT)) {
style |= Font.PLAIN;
} else {
style |= Font.ITALIC;
}
font = new Font(//! Font.DIALOG,
"Times New Roman",
style,
fontSize);
boolean invalid = false;
for (int i=0; i<text.length(); i++) {
int codePoint = text.codePointAt(i);
if ( ! font.canDisplay(codePoint) ) {
invalid = true;
}
}
if (invalid) {
font = new Font(
Font.DIALOG,
style,
(int) (TEXT_SCALE_FACTOR*fontSize));
}
}
/*
* Use the font to determine the size of the text.
*/
@Override
protected void calcMinimumSize() {
FontMetrics fontMetrics = getFontMetrics(font);
int width = fontMetrics.stringWidth(text);
int decent = fontMetrics.getDescent();
ascent = fontMetrics.getAscent();
innerRectangle = new Offset(width, decent+ascent);
}
private int getFontProperty() {
if (hasProperty(BOLD_FONT)) {
return Font.BOLD;
} else if (hasProperty(PLAIN_FONT)) {
return Font.PLAIN;
} else {
return Font.ITALIC;
}
}
@Override
public void setupOuterRectangle(Offset actualSize) {
FontMetrics fontMetrics = getFontMetrics(font);
cumulativeWidths = new Vector<Integer>();
if (hasProperty(HORIZONTAL_STRETCH) || hasProperty(VERTICAL_STRETCH)) {
int oldAscent = fontMetrics.getAscent();
int oldDescent = fontMetrics.getDescent();
int oldWidth = fontMetrics.stringWidth(text);
for (int fontSize=SMALLEST_FONT_SIZE-1; fontSize<=LARGEST_FONT_SIZE; fontSize++) {
font = new Font("Times New Roman", getFontProperty(), fontSize);
fontMetrics = getFontMetrics(font);
ascent = fontMetrics.getAscent();
int newDescent = fontMetrics.getDescent();
int newWidth = fontMetrics.stringWidth(text);
if (hasProperty(HORIZONTAL_STRETCH) && hasProperty(VERTICAL_STRETCH)) {
xFactor = newWidth / (double) oldWidth;
yFactor = (ascent+newDescent) / (double) (oldAscent+oldDescent);
} else if (hasProperty(HORIZONTAL_STRETCH)) {
xFactor = actualSize.width / (double) oldWidth;
yFactor = (ascent+newDescent) / (double) (oldAscent+oldDescent);
} else if (hasProperty(VERTICAL_STRETCH)) {
xFactor = newWidth / (double) oldWidth;
yFactor = actualSize.height / (double) (oldAscent+oldDescent);
}
// TODO: In the case of horizontal and vertical
// stretches, this box should expand in size, but this
// is currently beyond the scope of this inheritance
// hierarchy.
if (hasProperty(PRESERVE_ASPECT_RATIO)) {
xFactor = Math.min(xFactor, yFactor);
yFactor = xFactor;
}
if (xFactor>=1 && yFactor>=1) {
break;
}
}
}
// Record cumulative widths of the string.
for (int q=1; q<text.length(); q++) {
int width = fontMetrics.stringWidth(text.substring(0, q));
cumulativeWidths.add(width);
}
// Use the actual size to appropriately lay out the component.
super.setupOuterRectangle(actualSize);
}
public int getIndex(Position p) {
if (topLeft==null || cumulativeWidths==null) {
return -1;
}
int boxIndent = (int) getXPosition(topLeft);
for (int q=1; q<cumulativeWidths.size(); q++) {
boolean bounded = 0<=p.x && p.x<boxIndent+cumulativeWidths.get(q); //? boxIndent
if (bounded) {
return q;
}
}
return -1;
}
@Override
public void draw(Graphics2D g2D, Position topLeft, ColourScheme colourScheme) {
this.topLeft = topLeft;
double x = getXPosition(topLeft);
double y = getYPosition(topLeft);
if (hasProperty(HORIZONTAL_STRETCH) || hasProperty(VERTICAL_STRETCH)) {
g2D.setFont(font);
AffineTransform originalTransformation = g2D.getTransform();
g2D.translate(x, y);
g2D.scale(xFactor, yFactor);
g2D.drawString(text, 0, (float) ascent);
g2D.setTransform(originalTransformation);
} else {
g2D.setFont(font);
g2D.drawString(text, (float) x, (float) (y + ascent));
}
}
public String getText() {
return text;
}
public String toString() {
return "BoxWord('" + text + "'; font:" + font + ", ascent:" + ascent +
" | " + super.toString();
}
@Override
public boolean withinInnerRectangle(Position p) {
if (getText().trim().equals("")) return false;
return super.withinInnerRectangle(p);
}
}