/*
Редактор шрифтов UFN
Эта программа создана специально для программы Малик Эмулятор.
Copyright © 2019, 2022 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
общественной лицензии GNU.
Вы должны были получить копию Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package ru.malik.elaborarer.ufneditor;
import com.nokia.mid.ui.*;
import java.util.*;
import javax.microedition.lcdui.*;
import malik.emulator.fileformats.font.*;
import malik.emulator.fileformats.font.UnicodeRasterFont.*;
public final class GlyphList extends FontCustomItem
{
private static final int COLOR_SELECTED = 0xaa008800;
private static final int COLOR_SHADOW = 0x55202020;
private static final int COLOR_GLYPH = 0xff003300;
private static final Image DIGITS;
static {
Image digits;
try
{
digits = Image.createImage("/digits.png");
}
catch(Exception e)
{
digits = Image.createImage(16, 1);
}
DIGITS = digits;
}
private boolean focused;
private int cols;
private int selected;
private int[] codes;
public GlyphList(UnicodeRasterFont font) {
super(font);
}
public void notifyFontChanged() {
invalidate();
}
public void fillGlyphCodes(Gauge progressBar) {
int i = 0;
int len;
int[] codes;
UnicodeRasterFont font = this.font;
progressBar.setValue(0);
progressBar.setMaxValue((len = font.getGlyphsCount()) <= 0 ? 1 : len);
this.codes = codes = len > 0 ? new int[len] : null;
if(len > 0)
{
for(Enumeration e = font.glyphs(); e.hasMoreElements(); progressBar.setValue(++i)) codes[i] = ((Glyph) e.nextElement()).getCharacterCode();
if(selected >= len) selected = len - 1;
} else
{
selected = 0;
}
invalidate();
}
public void setSelectedCode(int code) {
int[] codes;
if((codes = this.codes) != null) for(int result, limit1 = 0, limit2 = codes.length - 1; limit1 <= limit2; )
{
int guess;
if((guess = codes[result = (limit1 + limit2) >> 1] - code) < 0)
{
limit1 = result + 1;
}
else if(guess > 0)
{
limit2 = result - 1;
}
else
{
selected = result;
invalidate();
break;
}
}
}
public boolean isEmpty() {
return codes == null;
}
public int getSelectedCode() {
int selected;
int[] codes;
return (codes = this.codes) != null && (selected = this.selected) >= 0 && selected < codes.length ? codes[selected] : 0;
}
protected void paint(Graphics renderA, int contentW, int contentH) {
int len;
int cols;
int right;
int clipW;
int clipH;
int clipL = renderA.getClipX();
int clipT = renderA.getClipY();
int clipR = clipL + (clipW = renderA.getClipWidth()) - 1;
int clipB = clipT + (clipH = renderA.getClipHeight()) - 1;
int cellW = getMinContentWidth();
int cellH = getMinContentHeight();
int[] codes;
Image digits = DIGITS;
UnicodeRasterFont font = this.font;
DirectGraphics renderB = DirectUtils.getDirectGraphics(renderA);
if((cols = contentW / cellW) < 1) cols = 1;
if((clipR > (right = cols * cellW - 1))) clipW = (clipR = right) - clipL + 1;
if(clipL <= clipR && (codes = this.codes) != null && (len = codes.length) > 0)
{
int gh;
int index = focused ? selected : -1;
int index0 = (clipT / cellH) * cols + (clipL / cellW);
int index1 = (clipB / cellH) * cols + (clipR / cellW);
int digitW = digits.getWidth() >> 4;
int digitH = digits.getHeight();
int bp = (gh = font.getHeight()) - font.getBaselineHeight() + 1;
for(int i = index0, cellL = (i % cols) * cellW, cellT = (i / cols) * cellH; i < len && i <= index1; i++)
{
if(cellL <= clipR && cellT <= clipB && cellL + cellW > clipL && cellT + cellH > clipT)
{
int code;
Glyph glyph;
if(i == index)
{
renderB.setARGBColor(COLOR_SELECTED);
renderA.fillRoundRect(cellL, cellT, cellW, cellH, 2, 2);
}
if((code = codes[i]) >= 0x0000 && code <= 0xffff)
{
for(int x = (cellL + 4) + (digitW << 1), y = (cellT + cellH) - (digitH + 1), s = 12; s >= 0; x += digitW, s -= 4)
{
renderA.drawRegion(digits, ((code >> s) & 0x0f) * digitW, 0, digitW, digitH, 0, x, y, 0);
}
}
else if(code >= 0x010000 && code <= 0xffffff)
{
for(int x = (cellL + 4) + digitW, y = (cellT + cellH) - (digitH + 1), s = 20; s >= 0; x += digitW, s -= 4)
{
renderA.drawRegion(digits, ((code >> s) & 0x0f) * digitW, 0, digitW, digitH, 0, x, y, 0);
}
}
else
{
for(int x = (cellL + 4), y = (cellT + cellH) - (digitH + 1), s = 28; s >= 0; x += digitW, s -= 4)
{
renderA.drawRegion(digits, ((code >> s) & 0x0f) * digitW, 0, digitW, digitH, 0, x, y, 0);
}
}
if((glyph = font.getGlyph(code)) != null)
{
int gw = glyph.getWidth();
int rl = glyph.getRawLeft();
int rb = glyph.getRawBottom();
int rw = glyph.getRawWidth();
int rh = glyph.getRawHeight();
if(rw > 0 && rh > 0)
{
int x = cellL + rl + ((cellW - gw) >> 1);
int y = cellT + bp - rh - rb;
int[] pixels = new int[rw * rh];
renderA.clipRect(cellL + 4, cellT + 1, cellW - 8, gh + 1);
glyph.getPixels(pixels, 0, rw, COLOR_SHADOW);
renderA.drawRGB(pixels, 0, rw, x + 1, y + 1, rw, rh, true);
glyph.getPixels(pixels, 0, rw, COLOR_GLYPH);
renderA.drawRGB(pixels, 0, rw, x, y, rw, rh, true);
renderA.setClip(clipL, clipT, clipW, clipH);
}
}
}
if((cellL += cellW) > right)
{
cellL = 0;
cellT += cellH;
}
}
}
this.cols = cols;
}
protected void pointerPressed(int x, int y) {
pointerHandle(x, y);
}
protected void pointerDragged(int x, int y) {
pointerHandle(x, y);
}
protected void pointerReleased(int x, int y) {
pointerHandle(x, y);
}
protected void traverseOut() {
focused = false;
repaint();
}
protected boolean traverse(int direction, int viewportWidth, int viewportHeight, int[] visibleRectangle) {
boolean result;
int len;
int row;
int col;
int rows;
int cols = this.cols;
int index = selected;
int[] codes;
if(cols < 1) cols = 1;
len = (codes = this.codes) != null ? codes.length : 0;
rows = (len / cols) + (len % cols > 0 ? 1 : 0);
col = index % cols;
row = index / cols;
if(focused)
{
switch(direction)
{
case Canvas.UP:
if(result = row > 0) row--;
break;
case Canvas.DOWN:
if(result = row < rows - 1) row++;
break;
case Canvas.LEFT:
if(result = col > 0) col--;
break;
case Canvas.RIGHT:
if(result = col < cols - 1) col++;
break;
default:
result = true;
break;
}
} else
{
focused = result = true;
switch(direction)
{
case Canvas.DOWN:
case Canvas.RIGHT:
row = col = 0;
break;
case Canvas.UP:
case Canvas.LEFT:
row = rows - 1;
col = cols - 1;
break;
}
}
if(result)
{
int newindex;
int cellW = getMinContentWidth();
int cellH = getMinContentHeight();
if((newindex = col + row * cols) >= len) newindex = len - 1;
if(newindex < 0) newindex = 0;
if(index != (selected = newindex))
{
repaint();
super.notifyStateChanged();
}
visibleRectangle[0] = (newindex % cols) * cellW;
visibleRectangle[1] = (newindex / cols) * cellH;
visibleRectangle[2] = cellW;
visibleRectangle[3] = cellH;
return true;
}
return false;
}
protected int getMinContentWidth() {
return ((DIGITS.getWidth() >> 4) << 3) + 8;
}
protected int getMinContentHeight() {
return DIGITS.getHeight() + font.getHeight() + 4;
}
protected int getPrefContentWidth(int contentHeight) {
int len;
int cols;
int rows;
int[] codes;
len = (codes = this.codes) != null ? codes.length : 0;
if(contentHeight < 0) return 0;
if((rows = contentHeight / getMinContentHeight()) < 1) rows = 1;
for(cols = len / rows; cols * rows < len; cols++);
return cols * getMinContentWidth();
}
protected int getPrefContentHeight(int contentWidth) {
int len;
int cols;
int[] codes;
len = (codes = this.codes) != null ? codes.length : 0;
if(contentWidth < 0) return 0;
if((cols = contentWidth / getMinContentWidth()) < 1) cols = 1;
return ((len / cols) + (len % cols > 0 ? 1 : 0)) * getMinContentHeight();
}
private void pointerHandle(int x, int y) {
int len;
int row;
int col;
int cols = this.cols;
int newindex;
int[] codes;
if(cols < 1) cols = 1;
len = (codes = this.codes) != null ? codes.length : 0;
col = x / getMinContentWidth();
row = y / getMinContentHeight();
if((newindex = col + row * cols) >= len) newindex = len - 1;
if(newindex < 0) newindex = 0;
if(selected != (selected = newindex))
{
invalidate();
super.notifyStateChanged();
}
}
}