/*******************************************************************************
Jimm - Mobile Messaging - J2ME ICQ clone
Copyright (C) 2003-05 Jimm Project
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 (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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************
Author(s): Artyomov Denis, Vladimir Kryukov
*******************************************************************************/
package jimmui.view.text;
import jimmui.view.icons.Icon;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import jimm.modules.*;
import jimmui.view.base.CanvasEx;
/**
*
* @author Vladimir Kryukov
*/
public final class Parser {
private Par par;
private short maxWidth;
private int minHeight = 0;
private Vector<Object> objects = new Vector<Object>();
private Vector<short[]> items = new Vector<short[]>();
private int lineWidth = 0;
private Font[] fontSet;
public Parser(Par par, Font[] fontSet, int width) {
this.par = par;
maxWidth = (short) width;
this.fontSet = fontSet;
}
public Parser(Font[] fontSet, int width) {
this(new Par(), fontSet, width);
}
public void setSelectable(boolean selectable) {
par.selectable = selectable;
}
public void useMinHeight() {
minHeight = CanvasEx.minItemHeight;
}
public void destroy() {
objects.removeAllElements();
objects = null;
items.removeAllElements();
items = null;
}
public Par getPar() {
commit();
return par;
}
private int getWidth() {
return maxWidth;
}
private int getLastLineWidth() {
return lineWidth;
}
private short addObject(Object obj) {
objects.addElement(obj);
return (short) (objects.size() - 1);
}
private int getGlyphHeight(short[] item) {
if (Par.TEXT == item[0]) {
return fontSet[item[4]].getHeight();
}
if (Par.SMILE == item[0]) {
return Emotions.instance.getSmileIcon(item[1]).getHeight();
}
if (Par.IMAGE == item[0]) {
return ((Icon) objects.elementAt(item[1])).getHeight();
}
return fontSet[CanvasEx.FONT_STYLE_PLAIN].getHeight();
}
private short[] getLines(Vector glyphs) {
int size = 1;
boolean newLine = true;
final int glyphCount = glyphs.size();
for (int i = 0; i < glyphCount; ++i) {
if (newLine) {
newLine = false;
size += 1;
}
short[] glyph = (short[]) glyphs.elementAt(i);
size += glyph.length;
if ((Par.EOL == glyph[0]) || (Par.BR == glyph[0])) {
newLine = true;
}
}
int height = 0;
int lineHeight = 0;
int lineHeightOffset = 1;
newLine = false;
short[] data = new short[size];
data[0] = (short) height;
int ip = 2;
for (int i = 0; i < glyphCount; ++i) {
if (newLine) {
data[lineHeightOffset] = (short) lineHeight;
height += lineHeight;
lineHeight = 0;
newLine = false;
lineHeightOffset = ip++;
}
short[] glyph = (short[]) glyphs.elementAt(i);
lineHeight = Math.max(lineHeight, getGlyphHeight(glyph));
System.arraycopy(glyph, 0, data, ip, glyph.length);
ip += glyph.length;
if ((Par.EOL == glyph[0]) || (Par.BR == glyph[0])) {
newLine = true;
}
}
if (lineHeightOffset < data.length) {
data[lineHeightOffset] = (short) lineHeight;
height += lineHeight;
}
data[0] = (short) Math.max(height, minHeight);
return data;
}
public void commit() {
Object[] objs = new Object[objects.size()];
objects.copyInto(objs);
par.setLines(getLines(items), objs);
}
private static final short[] PAR_BR = new short[]{Par.BR};
private static final short[] PAR_EOL = new short[]{Par.EOL};
private void internNewLine(boolean br) {
items.addElement(br ? PAR_BR : PAR_EOL);
lineWidth = 0;
}
private short[] createText(short text, int from, int to, byte colorType, byte fontStyle, int width) {
short[] data = new short[7];
data[0] = Par.TEXT;
data[1] = text;
data[2] = (short) from;
data[3] = (short) (to - from);
data[4] = fontStyle;
data[5] = colorType;
data[6] = (short) width;
return data;
}
private short[] createSmile(short smileIndex) {
short[] data = new short[2];
data[0] = Par.SMILE;
data[1] = smileIndex;
return data;
}
private void addText(short textIndex, int from, int to, byte colorType, byte fontStyle, int width) {
internAdd(createText(textIndex, from, to, colorType, fontStyle, width), width);
}
private void internAdd(short[] item, int width) {
items.addElement(item);
lineWidth += width;
}
public void doCRLF() {
internNewLine(true);
}
public void addImage(Icon image) {
short[] img = new short[2];
img[0] = Par.IMAGE;
img[1] = addObject(image);
int width = image.getWidth() + 2;
if ((getLastLineWidth() + width) > getWidth()) {
internNewLine(false);
}
internAdd(img, width);
}
public void addProgress(byte colorType) {
internNewLine(false);
short[] progress = new short[4];
progress[0] = Par.PROGRESS;
progress[1] = colorType;
progress[2] = maxWidth;
progress[3] = 0;
internAdd(progress, maxWidth);
}
public void addText(String text, byte colorType, byte fontStyle) {
addBigText(text, colorType, fontStyle, false);
}
public void addTextWithSmiles(String text, byte colorType, byte fontStyle) {
addBigText(text, colorType, fontStyle, true);
}
/**
* Add big multiline textIndex.
*
* Text visial width can be larger then screen maxWidth.
* Method addText automatically divides textIndex to short lines
* and adds lines to textIndex list
*/
private void addBigText(String text, byte colorType,
byte fontStyle, boolean withEmotions) {
Font font = fontSet[fontStyle];
short textIndex = addObject(text);
// Width of free space in last line
final int fullWidth = getWidth();
int width = fullWidth - getLastLineWidth();
int curLineWidth = 0;
// #sijapp cond.if modules_SMILES is "true" #
int smileCount = 100;
// #sijapp cond.end #
int lineStart = 0;
int wordStart = 0;
int wordWidth = 0;
int textLen = text.length();
// #sijapp cond.if modules_SMILES is "true" #
Emotions smiles = Emotions.instance;
withEmotions &= smiles.isEnabled();
// #sijapp cond.end #
for (int i = 0; i < textLen; ++i) {
char ch = text.charAt(i);
if ('\n' == ch) {
addText(textIndex, lineStart, i, colorType, fontStyle, curLineWidth);
internNewLine(true);
lineStart = i + 1;
width = fullWidth;
wordStart = lineStart;
wordWidth = 0;
curLineWidth = 0;
continue;
}
// #sijapp cond.if modules_SMILES is "true" #
int smileIndex = withEmotions ? smiles.getSmile(text, i) : -1;
if (-1 != smileIndex) {
wordStart = i;
if (lineStart < wordStart) {
addText(textIndex, lineStart, wordStart, colorType, fontStyle, curLineWidth);
if (width <= 0) {
internNewLine(false);
width = fullWidth;
}
}
short[] smileItem = createSmile((short) smileIndex);
int smileWidth = smiles.getSmileIcon(smileIndex).getWidth();
width -= smileWidth;
if (width <= 0) {
internNewLine(false);
width = fullWidth - smileWidth;
}
internAdd(smileItem, smileWidth);
i += smiles.getSmileText(smileIndex).length() - 1;
lineStart = i + 1;
wordStart = lineStart;
wordWidth = 0;
smileCount--;
if (0 == smileCount) {
withEmotions = false;
}
curLineWidth = 0;
continue;
}
// #sijapp cond.end #
int charWidth = font.charWidth(ch);
wordWidth += charWidth;
curLineWidth += charWidth;
width -= charWidth;
if (' ' == ch) {
wordStart = i + 1;
wordWidth = 0;
continue;
}
if (width <= 0) {
if (lineStart < wordStart) {
curLineWidth -= wordWidth;
addText(textIndex, lineStart, wordStart, colorType, fontStyle, curLineWidth);
internNewLine(false);
lineStart = wordStart;
width = fullWidth - wordWidth;
curLineWidth = wordWidth;
} else if (wordWidth < fullWidth) {
if (0 < getLastLineWidth()) {
internNewLine(false);
}
width = fullWidth - wordWidth;
curLineWidth = wordWidth;
} else {
addText(textIndex, lineStart, i, colorType, fontStyle, curLineWidth);
internNewLine(false);
lineStart = i;
width = fullWidth - charWidth;
wordStart = i;
wordWidth = charWidth;
curLineWidth = charWidth;
}
}
}
if (lineStart < text.length()) {
addText(textIndex, lineStart, text.length(), colorType, fontStyle, curLineWidth);
}
}
}