/*
    Редактор шрифтов UFN
    Эта программа создана специально для программы Малик Эмулятор.

    Copyright © 2019, 2022 Малик Разработчик

    Это свободная программа: вы можете перераспространять ее и/или изменять
    ее на условиях Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она будет полезной,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <https://www.gnu.org/licenses/>.
*/

package ru.malik.elaborarer.ufneditor;

import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.rms.*;
import malik.emulator.fileformats.font.*;
import malik.emulator.fileformats.font.UnicodeRasterFont.*;

public final class UFNEditorMIDlet extends MIDlet implements CommandListener, ItemCommandListener, ItemStateListener
{
    static class FillGlyphListThread extends Thread
    {
        final UFNEditorMIDlet owner;
        final Gauge progressBar;

        public FillGlyphListThread(UFNEditorMIDlet owner, Gauge progressBar) {
            this.owner = owner;
            this.progressBar = progressBar;
        }

        public void run() {
            owner.getItemList().fillGlyphCodes(progressBar);
        }
    }

    static class CodeFillGlyphListThread extends FillGlyphListThread
    {
        int code;

        public CodeFillGlyphListThread(UFNEditorMIDlet owner, Gauge progressBar) {
            super(owner, progressBar);
        }

        public CodeFillGlyphListThread(UFNEditorMIDlet owner, Gauge progressBar, int code) {
            super(owner, progressBar);
            this.code = code;
        }

        public void run() {
            GlyphList list;
            UFNEditorMIDlet app;
            (list = (app = owner).getItemList()).fillGlyphCodes(progressBar);
            list.setSelectedCode(code);
            Display.getDisplay(app).setCurrent(app.getScreenGlyphList());
        }
    }

    static void glyphBold(Glyph glyph) {
        int width;
        if((width = glyph.getWidth() + 1) <= UnicodeRasterFont.MAX_COORDINATE) glyph.setWidth(width);
        for(int y = glyph.getRawBottom(), t = glyph.getRawHeight() + y; y < t; y++)
        {
            boolean currPixel;
            boolean prevPixel = false;
            for(int l = glyph.getRawLeft(), x = glyph.getRawWidth() + l; --x >= l; prevPixel = currPixel) if((currPixel = glyph.getPixel(x, y)) && !prevPixel) glyph.putPixel(x + 1, y, true);
        }
    }

    static void glyphTiltR(Glyph glyph) {
        for(int y = glyph.getRawBottom(), t = glyph.getRawHeight() + y, s = (y >= 0 ? y : y - 3) / 4; y < t; s += (++y % 4 == 0 ? 1 : 0))
        {
            if(s < 0)
            {
                for(int x = glyph.getRawLeft(), r = glyph.getRawWidth() + x - s, xd = x + s; x < r; x++, xd++) glyph.putPixel(xd, y, glyph.getPixel(x, y));
                continue;
            }
            if(s > 0) for(int l = glyph.getRawLeft() - s, x = glyph.getRawWidth() + l + s, xd = --x + s; x >= l; x--, xd--) glyph.putPixel(xd, y, glyph.getPixel(x, y));
        }
    }

    static void glyphTiltL(Glyph glyph) {
        for(int y = glyph.getRawBottom(), t = glyph.getRawHeight() + y, s = (y >= 0 ? y : y - 3) / -4; y < t; s += (++y % 4 == 0 ? -1 : 0))
        {
            if(s < 0)
            {
                for(int x = glyph.getRawLeft(), r = glyph.getRawWidth() + x - s, xd = x + s; x < r; x++, xd++) glyph.putPixel(xd, y, glyph.getPixel(x, y));
                continue;
            }
            if(s > 0) for(int l = glyph.getRawLeft() - s, x = glyph.getRawWidth() + l + s, xd = --x + s; x >= l; x--, xd--) glyph.putPixel(xd, y, glyph.getPixel(x, y));
        }
    }

    static void glyphMirrorH(Glyph glyph) {
        int max;
        int min = -(max = getMaxCoordinate(glyph));
        Glyph copy = glyph.copy();
        for(int yd = min, ys = min; ys <= max; yd++, ys++) for(int xd = max, xs = min; xs <= max; xd--, xs++) glyph.putPixel(xd, yd, copy.getPixel(xs, ys));
    }

    static void glyphMirrorV(Glyph glyph) {
        int max;
        int min = -(max = getMaxCoordinate(glyph));
        Glyph copy = glyph.copy();
        for(int yd = max, ys = min; ys <= max; yd--, ys++) for(int xd = min, xs = min; xs <= max; xd++, xs++) glyph.putPixel(xd, yd, copy.getPixel(xs, ys));
    }

    static void glyphRotateL(Glyph glyph) {
        int max;
        int min = -(max = getMaxCoordinate(glyph));
        Glyph copy = glyph.copy();
        for(int xd = max, ys = min; ys <= max; xd--, ys++) for(int yd = min, xs = min; xs <= max; yd++, xs++) glyph.putPixel(xd, yd, copy.getPixel(xs, ys));
    }

    static void glyphRotateR(Glyph glyph) {
        int max;
        int min = -(max = getMaxCoordinate(glyph));
        Glyph copy = glyph.copy();
        for(int xd = min, ys = min; ys <= max; xd++, ys++) for(int yd = max, xs = min; xs <= max; yd--, xs++) glyph.putPixel(xd, yd, copy.getPixel(xs, ys));
    }

    static void glyphRotateU(Glyph glyph) {
        int max;
        int min = -(max = getMaxCoordinate(glyph));
        Glyph copy = glyph.copy();
        for(int yd = max, ys = min; ys <= max; yd--, ys++) for(int xd = max, xs = min; xs <= max; xd--, xs++) glyph.putPixel(xd, yd, copy.getPixel(xs, ys));
    }

    static void saveFileName(String fileName) {
        try
        {
            RecordStore store = RecordStore.openRecordStore("filename", true);
            try
            {
                byte[] record = fileName.getBytes();
                if(store.getNumRecords() == 0)
                {
                    store.addRecord(record, 0, record.length);
                } else
                {
                    store.setRecord(1, record, 0, record.length);
                }
            }
            finally
            {
                store.closeRecordStore();
            }
        }
        catch(RecordStoreException e)
        {
            e.printStackTrace();
        }
    }

    static char digitToChar(int digit) {
        return digit >= 0x00 && digit <= 0x09 ? (char) (digit + '0') : (char) (digit + ('A' - 0x0a));
    }

    static int getMaxCoordinate(Glyph glyph) {
        int l = glyph.getRawLeft();
        int b = glyph.getRawBottom();
        int r = glyph.getRawWidth() + l - 1;
        int t = glyph.getRawHeight() + b - 1;
        if(l < 0) l = -l;
        if(b < 0) b = -b;
        if(r < 0) r = -r;
        if(t < 0) t = -t;
        return Math.max(Math.max(l, b), Math.max(r, t));
    }

    static int stringToCode(String string) throws NumberFormatException {
        int len;
        int result;
        if((len = string.length()) < 1 || len > 8)
        {
            throw new NumberFormatException();
        }
        result = 0;
        for(int i = 0; i < len; i++)
        {
            char c;
            if((c = string.charAt(i)) >= '0' && c <= '9')
            {
                result = (result << 4) + (c - '0');
            }
            else if(c >= 'A' && c <= 'F')
            {
                result = (result << 4) + (c - ('A' - 0x0a));
            }
            else if(c >= 'a' && c <= 'f')
            {
                result = (result << 4) + (c - ('a' - 0x0a));
            }
            else
            {
                throw new NumberFormatException();
            }
        }
        return result;
    }

    static String codeToString(int code) {
        if(code >= 0x0000 && code <= 0xffff)
        {
            return new String(new char[] {
                digitToChar((code >> 12) & 0x0f), digitToChar((code >> 8) & 0x0f),
                digitToChar((code >> 4) & 0x0f), digitToChar(code & 0x0f)
            });
        }
        if(code >= 0x010000 && code <= 0xffffff)
        {
            return new String(new char[] {
                digitToChar((code >> 20) & 0x0f), digitToChar((code >> 16) & 0x0f), digitToChar((code >> 12) & 0x0f),
                digitToChar((code >> 8) & 0x0f), digitToChar((code >> 4) & 0x0f), digitToChar(code & 0x0f)
            });
        }
        return new String(new char[] {
            digitToChar((code >> 28) & 0x0f), digitToChar((code >> 24) & 0x0f), digitToChar((code >> 20) & 0x0f), digitToChar((code >> 16) & 0x0f),
            digitToChar((code >> 12) & 0x0f), digitToChar((code >> 8) & 0x0f), digitToChar((code >> 4) & 0x0f), digitToChar(code & 0x0f)
        });
    }

    static String loadFileName() {
        String result;
        try
        {
            RecordStore store = RecordStore.openRecordStore("filename", false);
            try
            {
                byte[] record = store.getRecord(1);
                result = (new String(record)).intern();
            }
            finally
            {
                store.closeRecordStore();
            }
        }
        catch(RecordStoreException e)
        {
            e.printStackTrace();
            result = "/editor/! SYSTEM FONT !.ufn";
        }
        return result;
    }

    Command commandNo;
    Command commandYes;
    Command commandCut;
    Command commandCopy;
    Command commandPaste;
    Command commandClear;
    Command commandModeDraw;
    Command commandModeMove;
    Command commandModeSize;
    Command commandChangeCode;
    Command commandZoomOut;
    Command commandZoomIn;
    Command commandSelect;
    Command commandParams;
    Command commandCreate;
    Command commandDelete;
    Command commandTiltL;
    Command commandTiltR;
    Command commandBold;
    Command commandMirH;
    Command commandMirV;
    Command commandRotL;
    Command commandRotR;
    Command commandRotU;
    Command commandCode;
    Command commandEdit;
    Command commandLoad;
    Command commandSave;
    Command commandBack;
    Command commandExit;
    TextField itemCode;
    TextField itemLoadName;
    TextField itemSaveName;
    GlyphList itemList;
    GlyphEditor itemEditor;
    ChoiceGroup itemFace;
    StringItem itemUnderlineDown;
    StringItem itemUnderlineVal;
    StringItem itemUnderlineUp;
    StringItem itemStrikeoutDown;
    StringItem itemStrikeoutVal;
    StringItem itemStrikeoutUp;
    StringItem itemLineWidthDown;
    StringItem itemLineWidthVal;
    StringItem itemLineWidthUp;
    TextField itemPreviewText;
    ChoiceGroup itemPreviewStyle;
    FontPreview itemPreviewFont;
    List screenMainMenu;
    Form screenGlyphList;
    Form screenGlyphCode;
    Form screenGlyphEditor;
    Form screenFontParams;
    Form screenFontLoad;
    Form screenFontSave;
    Alert screenOperation;
    Alert screenFontBoldWarn;
    Alert screenFontTiltLWarn;
    Alert screenFontTiltRWarn;
    Alert screenFontLoadError;
    Alert screenFontSaveError;
    Alert screenGlyphCodeError;
    Alert screenGlyphListEmpty;
    Alert screenGlyphListFilling;
    Alert screenGlyphListChanging;
    Alert screenAbout;
    Glyph clipboard;
    final UnicodeRasterFont font;

    public UFNEditorMIDlet() {
        this.font = new UnicodeRasterFont();
        createFont();
    }

    public void commandAction(Command command, final Displayable screen) {
        List screenList;
        final Display display = Display.getDisplay(this);
        if(screen == screenFontTiltRWarn)
        {
            if(command == commandYes)
            {
                Gauge bar;
                Alert info;
                (bar = (info = getScreenGlyphListChanging()).getIndicator()).setValue(0);
                display.setCurrent(info);
                ((Thread) (new FillGlyphListThread(this, bar) {
                    public void run() {
                        int i = 1;
                        int len;
                        Gauge bar = progressBar;
                        UFNEditorMIDlet app;
                        UnicodeRasterFont font = (app = UFNEditorMIDlet.this).font;
                        bar.setMaxValue((len = font.getGlyphsCount()) <= 0 ? 1 : len);
                        for(Enumeration e = font.glyphs(); e.hasMoreElements(); bar.setValue(i++)) UFNEditorMIDlet.glyphTiltR((Glyph) e.nextElement());
                        ((FontCustomItem) app.getItemList()).notifyFontChanged();
                        display.setCurrent(app.getScreenGlyphList());
                    }
                })).start();
                return;
            }
            if(command == commandNo) display.setCurrent(getScreenGlyphList());
            return;
        }
        if(screen == screenFontTiltLWarn)
        {
            if(command == commandYes)
            {
                Gauge bar;
                Alert info;
                (bar = (info = getScreenGlyphListChanging()).getIndicator()).setValue(0);
                display.setCurrent(info);
                ((Thread) (new FillGlyphListThread(this, bar) {
                    public void run() {
                        int i = 1;
                        int len;
                        Gauge bar = progressBar;
                        UFNEditorMIDlet app;
                        UnicodeRasterFont font = (app = UFNEditorMIDlet.this).font;
                        bar.setMaxValue((len = font.getGlyphsCount()) <= 0 ? 1 : len);
                        for(Enumeration e = font.glyphs(); e.hasMoreElements(); bar.setValue(i++)) UFNEditorMIDlet.glyphTiltL((Glyph) e.nextElement());
                        ((FontCustomItem) app.getItemList()).notifyFontChanged();
                        display.setCurrent(app.getScreenGlyphList());
                    }
                })).start();
                return;
            }
            if(command == commandNo) display.setCurrent(getScreenGlyphList());
            return;
        }
        if(screen == screenFontBoldWarn)
        {
            if(command == commandYes)
            {
                Gauge bar;
                Alert info;
                (bar = (info = getScreenGlyphListChanging()).getIndicator()).setValue(0);
                display.setCurrent(info);
                ((Thread) (new FillGlyphListThread(this, bar) {
                    public void run() {
                        int i = 1;
                        int len;
                        Gauge bar = progressBar;
                        UFNEditorMIDlet app;
                        UnicodeRasterFont font = (app = UFNEditorMIDlet.this).font;
                        bar.setMaxValue((len = font.getGlyphsCount()) <= 0 ? 1 : len);
                        for(Enumeration e = font.glyphs(); e.hasMoreElements(); bar.setValue(i++)) UFNEditorMIDlet.glyphBold((Glyph) e.nextElement());
                        ((FontCustomItem) app.getItemList()).notifyFontChanged();
                        display.setCurrent(app.getScreenGlyphList());
                    }
                })).start();
                return;
            }
            if(command == commandNo) display.setCurrent(getScreenGlyphList());
            return;
        }
        if(screen == screenFontSave)
        {
            if(command == commandSave)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        String fileName;
                        UFNEditorMIDlet app;
                        UnicodeRasterFont font = (app = UFNEditorMIDlet.this).font;
                        UFNEditorMIDlet.saveFileName(fileName = app.getItemSaveName().getString());
                        try
                        {
                            OutputStream stream = null;
                            try
                            {
                                FileConnection file = (FileConnection) Connector.open("file://".concat(fileName));
                                try
                                {
                                    if(file.exists()) file.delete();
                                    file.create();
                                    stream = file.openOutputStream();
                                }
                                finally
                                {
                                    file.close();
                                }
                                font.saveToOutputStream(stream);
                            }
                            finally
                            {
                                if(stream != null) stream.close();
                            }
                        }
                        catch(Exception e)
                        {
                            e.printStackTrace();
                            display.setCurrent(app.getScreenFontSaveError());
                            return;
                        }
                        display.setCurrent(app.getScreenFontSave());
                    }
                })).start();
                return;
            }
            if(command == commandBack) display.setCurrent(getScreenMainMenu());
            return;
        }
        if(screen == screenFontLoad)
        {
            if(command == commandLoad)
            {
                Gauge bar;
                (bar = getScreenGlyphListFilling().getIndicator()).setValue(0);
                display.setCurrent(getScreenOperation());
                ((Thread) (new FillGlyphListThread(this, bar) {
                    public void run() {
                        String fileName;
                        Displayable next;
                        UFNEditorMIDlet app;
                        UnicodeRasterFont font = (app = UFNEditorMIDlet.this).font;
                        UFNEditorMIDlet.saveFileName(fileName = app.getItemLoadName().getString());
                        try
                        {
                            InputStream stream = Connector.openInputStream("file://".concat(fileName));
                            try
                            {
                                DataInputStream dataStream = new DataInputStream(stream);
                                if((int) UnicodeRasterFont.SIGNATURE != dataStream.readInt())
                                {
                                    throw new IOException("Неправильный формат файла UFN-шрифта.");
                                }
                                font.loadFromInputStream(stream);
                            }
                            finally
                            {
                                stream.close();
                            }
                        }
                        catch(Exception e)
                        {
                            e.printStackTrace();
                            display.setCurrent(app.getScreenFontLoadError());
                            return;
                        }
                        display.setCurrent(app.getScreenGlyphListFilling(), next = app.getScreenGlyphList());
                        super.run();
                        display.setCurrent(next);
                    }
                })).start();
                return;
            }
            if(command == commandBack) display.setCurrent(getScreenMainMenu());
            return;
        }
        if(screen == screenFontParams)
        {
            if(command == commandBack) display.setCurrent(getScreenGlyphList());
            return;
        }
        if(screen == screenGlyphEditor)
        {
            final GlyphEditor itemEditor = this.itemEditor;
            if(command == commandModeDraw)
            {
                itemEditor.setMode(GlyphEditor.MODE_DRAW);
                return;
            }
            if(command == commandModeMove)
            {
                itemEditor.setMode(GlyphEditor.MODE_MOVE);
                return;
            }
            if(command == commandModeSize)
            {
                itemEditor.setMode(GlyphEditor.MODE_SIZE);
                return;
            }
            if(command == commandClear)
            {
                int width;
                Glyph glyph;
                width = (glyph = itemEditor.getGlyph()).getWidth();
                glyph.clear();
                glyph.setWidth(width);
                itemEditor.notifyGlyphChanged();
                return;
            }
            if(command == commandCut)
            {
                Glyph glyph;
                clipboard = (glyph = itemEditor.getGlyph()).copy();
                glyph.clear();
                itemEditor.notifyGlyphChanged();
                return;
            }
            if(command == commandCopy)
            {
                clipboard = itemEditor.getGlyph().copy();
                return;
            }
            if(command == commandPaste)
            {
                Glyph glyph;
                if((glyph = clipboard) != null)
                {
                    itemEditor.getGlyph().paste(glyph);
                    itemEditor.notifyGlyphChanged();
                }
                return;
            }
            if(command == commandMirH)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphMirrorH(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandMirV)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphMirrorV(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandRotL)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphRotateL(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandRotR)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphRotateR(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandRotU)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphRotateU(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandBold)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphBold(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandTiltL)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphTiltL(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandTiltR)
            {
                display.setCurrent(getScreenOperation());
                ((Thread) (new Thread() {
                    public void run() {
                        GlyphEditor editor = itemEditor;
                        UFNEditorMIDlet.glyphTiltR(editor.getGlyph());
                        display.setCurrent(screen);
                        editor.notifyGlyphChanged();
                    }
                })).start();
                return;
            }
            if(command == commandZoomIn)
            {
                itemEditor.zoomIn();
                return;
            }
            if(command == commandZoomOut)
            {
                itemEditor.zoomOut();
                return;
            }
            if(command == commandBack)
            {
                getItemList().notifyFontChanged();
                display.setCurrent(getScreenGlyphList());
            }
            return;
        }
        if(screen == screenGlyphCode)
        {
            if(command == commandChangeCode)
            {
                int code = stringToCode(itemCode.getString());
                Displayable list = getScreenGlyphList();
                Displayable next;
                try
                {
                    font.getGlyph(getItemList().getSelectedCode()).setCharacterCode(code);
                    next = list;
                }
                catch(CharacterCodeExistsException e)
                {
                    next = getScreenGlyphCodeError();
                }
                display.setCurrent(next);
                if(next == list)
                {
                    Gauge bar;
                    Alert info;
                    (bar = (info = getScreenGlyphListFilling()).getIndicator()).setValue(0);
                    display.setCurrent(info);
                    ((Thread) (new CodeFillGlyphListThread(this, bar, code))).start();
                }
                return;
            }
            if(command == commandBack) display.setCurrent(getScreenGlyphList());
            return;
        }
        if(screen == screenGlyphList)
        {
            if(command == commandEdit)
            {
                GlyphList list;
                if((list = itemList).isEmpty())
                {
                    display.setCurrent(getScreenGlyphListEmpty());
                } else
                {
                    int code;
                    Displayable next = getScreenGlyphEditor();
                    getItemEditor().setGlyph(font.getGlyph(code = list.getSelectedCode()));
                    next.setTitle("Редактирование глифа: " + codeToString(code) + (code >= 0x0000 && code <= 0xd7ff || code >= 0xe000 && code <= 0xffff ? " (" + (char) code + ")" : ""));
                    display.setCurrent(next);
                }
                return;
            }
            if(command == commandCreate)
            {
                Gauge bar;
                Alert info;
                (bar = (info = getScreenGlyphListFilling()).getIndicator()).setValue(0);
                display.setCurrent(info);
                ((Thread) (new CodeFillGlyphListThread(this, bar) {
                    public void run() {
                        int code;
                        UFNEditorMIDlet app;
                        UnicodeRasterFont font = (app = UFNEditorMIDlet.this).font;
                        GlyphList list;
                        if((list = app.itemList).isEmpty())
                        {
                            code = 0x20;
                        } else
                        {
                            for(code = list.getSelectedCode() + 1; font.getGlyph(code) != null; code++);
                        }
                        font.addGlyph(this.code = code).setWidth(8);
                        super.run();
                    }
                })).start();
                return;
            }
            if(command == commandCopy)
            {
                final GlyphList list;
                if((list = itemList).isEmpty())
                {
                    display.setCurrent(getScreenGlyphListEmpty());
                } else
                {
                    Gauge bar;
                    Alert info;
                    (bar = (info = getScreenGlyphListFilling()).getIndicator()).setValue(0);
                    display.setCurrent(info);
                    ((Thread) (new CodeFillGlyphListThread(this, bar) {
                        public void run() {
                            int code;
                            Glyph glyph;
                            UnicodeRasterFont font;
                            glyph = (font = (UFNEditorMIDlet.this).font).getGlyph(code = list.getSelectedCode());
                            for(code++; font.getGlyph(code) != null; code++);
                            font.addGlyph(this.code = code).paste(glyph);
                            super.run();
                        }
                    })).start();
                }
                return;
            }
            if(command == commandCode)
            {
                GlyphList list;
                if((list = itemList).isEmpty())
                {
                    display.setCurrent(getScreenGlyphListEmpty());
                } else
                {
                    getItemCode().setString(codeToString(list.getSelectedCode()));
                    display.setCurrent(getScreenGlyphCode());
                }
                return;
            }
            if(command == commandDelete)
            {
                final GlyphList list;
                if((list = itemList).isEmpty())
                {
                    display.setCurrent(getScreenGlyphListEmpty());
                } else
                {
                    Gauge bar;
                    Alert info;
                    (bar = (info = getScreenGlyphListFilling()).getIndicator()).setValue(0);
                    display.setCurrent(info);
                    ((Thread) (new FillGlyphListThread(this, bar) {
                        public void run() {
                            (UFNEditorMIDlet.this).font.removeGlyph(list.getSelectedCode());
                            super.run();
                            display.setCurrent(screen);
                        }
                    })).start();
                }
                return;
            }
            if(command == commandBold)
            {
                display.setCurrent(getScreenFontBoldWarn());
                return;
            }
            if(command == commandTiltL)
            {
                display.setCurrent(getScreenFontTiltLWarn());
                return;
            }
            if(command == commandTiltR)
            {
                display.setCurrent(getScreenFontTiltRWarn());
                return;
            }
            if(command == commandParams)
            {
                setFontParamsActual();
                display.setCurrent(getScreenFontParams());
                return;
            }
            if(command == commandBack) display.setCurrent(getScreenMainMenu());
            return;
        }
        if(screen == (screenList = screenMainMenu))
        {
            if(command == List.SELECT_COMMAND)
            {
                switch(screenList.getSelectedIndex())
                {
                case 0:
                    display.setCurrent(getScreenGlyphList());
                    break;
                case 1:
                    createFont();
                    getItemList().fillGlyphCodes(getScreenGlyphListFilling().getIndicator());
                    display.setCurrent(getScreenGlyphList());
                    break;
                case 2:
                    display.setCurrent(getScreenFontSave());
                    break;
                case 3:
                    display.setCurrent(getScreenFontLoad());
                    break;
                case 4:
                    display.setCurrent(getScreenAbout());
                    break;
                case 5:
                    notifyDestroyed();
                    break;
                }
                return;
            }
            if(command == commandExit) notifyDestroyed();
        }
    }

    public void commandAction(Command command, Item item) {
        TextField itemTextField;
        UnicodeRasterFont font = this.font;
        if(item == itemUnderlineUp)
        {
            font.setUnderscoreLinePosition(font.getUnderscoreLinePosition() + 1);
            setFontParamsActual();
            return;
        }
        if(item == itemUnderlineDown)
        {
            font.setUnderscoreLinePosition(font.getUnderscoreLinePosition() - 1);
            setFontParamsActual();
            return;
        }
        if(item == itemStrikeoutUp)
        {
            font.setStrikeoutLinePosition(font.getStrikeoutLinePosition() + 1);
            setFontParamsActual();
            return;
        }
        if(item == itemStrikeoutDown)
        {
            font.setStrikeoutLinePosition(font.getStrikeoutLinePosition() - 1);
            setFontParamsActual();
            return;
        }
        if(item == itemLineWidthUp)
        {
            font.setLinesWidth(font.getLinesWidth() + 1);
            setFontParamsActual();
            return;
        }
        if(item == itemLineWidthDown)
        {
            int val;
            if((val = font.getLinesWidth()) > 1)
            {
                font.setLinesWidth(val - 1);
                setFontParamsActual();
            }
            return;
        }
        if(item == (itemTextField = itemPreviewText))
        {
            itemTextField.setString(command.getLabel());
            itemTextField.notifyStateChanged();
        }
    }

    public void itemStateChanged(Item item) {
        ChoiceGroup itemChoice;
        TextField itemTextField;
        if(item == (itemChoice = itemFace))
        {
            boolean[] face = new boolean[2];
            UnicodeRasterFont font = this.font;
            itemChoice.getSelectedFlags(face);
            font.setBold(face[0]);
            font.setItalic(face[1]);
            return;
        }
        if(item == (itemTextField = itemPreviewText))
        {
            itemPreviewFont.setString(itemTextField.getString());
            return;
        }
        if(item == (itemChoice = itemPreviewStyle))
        {
            boolean[] style = new boolean[2];
            FontPreview preview = itemPreviewFont;
            itemChoice.getSelectedFlags(style);
            preview.setUnderline(style[0]);
            preview.setStrikeout(style[1]);
            return;
        }
        if(item == (itemTextField = itemCode))
        {
            boolean modified = false;
            int len = itemTextField.size();
            char[] chars = new char[8];
            itemTextField.getChars(chars);
            for(int i = len; i-- > 0; )
            {
                char c = chars[i];
                if((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f'))
                {
                    modified = true;
                    System.arraycopy(chars, i + 1, chars, i, --len - i);
                }
            }
            if(modified) itemTextField.setChars(chars, 0, len);
        }
    }

    protected void startApp() throws MIDletStateChangeException {
        Display.getDisplay(this).setCurrent(getScreenMainMenu());
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
    }

    void createFont() {
        UnicodeRasterFont font;
        (font = this.font).clear();
        font.setHeight(20);
        font.setBaselineHeight(4);
        font.setSmallLettersHeight(8);
        font.setCapitalLettersHeight(12);
        font.setStrikeoutLinePosition(3);
        font.setUnderscoreLinePosition(-3);
        font.setLinesWidth(1);
        font.setBold(false);
        font.setItalic(false);
    }

    void setFontParamsActual() {
        boolean[] style = new boolean[2];
        ChoiceGroup face = getItemFace();
        UnicodeRasterFont font = this.font;
        FontPreview preview = getItemPreviewFont();
        face.setSelectedFlags(new boolean[] { font.isBold(), font.isItalic() });
        getItemUnderlineVal().setText(" " + font.getUnderscoreLinePosition());
        getItemStrikeoutVal().setText(" " + font.getStrikeoutLinePosition());
        getItemLineWidthVal().setText(" " + font.getLinesWidth());
        getItemPreviewStyle().getSelectedFlags(style);
        preview.setUnderline(style[0]);
        preview.setStrikeout(style[1]);
        preview.setString(getItemPreviewText().getString());
        ((FontCustomItem) preview).notifyFontChanged();
    }

    Command getCommandNo() {
        Command result;
        if((result = commandNo) == null) result = commandNo = new Command("Нет", Command.BACK, 0);
        return result;
    }

    Command getCommandYes() {
        Command result;
        if((result = commandYes) == null) result = commandYes = new Command("Да", Command.OK, 0);
        return result;
    }

    Command getCommandCut() {
        Command result;
        if((result = commandCut) == null) result = commandCut = new Command("Вырезать", Command.SCREEN, 0);
        return result;
    }

    Command getCommandCopy() {
        Command result;
        if((result = commandCopy) == null) result = commandCopy = new Command("Копировать", Command.SCREEN, 0);
        return result;
    }

    Command getCommandPaste() {
        Command result;
        if((result = commandPaste) == null) result = commandPaste = new Command("Вклеить", Command.SCREEN, 0);
        return result;
    }

    Command getCommandClear() {
        Command result;
        if((result = commandClear) == null) result = commandClear = new Command("Очистить", Command.SCREEN, 0);
        return result;
    }

    Command getCommandModeDraw() {
        Command result;
        if((result = commandModeDraw) == null) result = commandModeDraw = new Command("Режим рисования (1)", Command.SCREEN, 0);
        return result;
    }

    Command getCommandModeMove() {
        Command result;
        if((result = commandModeMove) == null) result = commandModeMove = new Command("Режим перемещения (2)", Command.SCREEN, 0);
        return result;
    }

    Command getCommandModeSize() {
        Command result;
        if((result = commandModeSize) == null) result = commandModeSize = new Command("Режим изменения размеров (3)", Command.SCREEN, 0);
        return result;
    }

    Command getCommandChangeCode() {
        Command result;
        if((result = commandChangeCode) == null) result = commandChangeCode = new Command("Изменить код", Command.OK, 0);
        return result;
    }

    Command getCommandZoomOut() {
        Command result;
        if((result = commandZoomOut) == null) result = commandZoomOut = new Command("Уменьшить (*)", Command.SCREEN, 0);
        return result;
    }

    Command getCommandZoomIn() {
        Command result;
        if((result = commandZoomIn) == null) result = commandZoomIn = new Command("Увеличить (#)", Command.SCREEN, 0);
        return result;
    }

    Command getCommandSelect() {
        Command result;
        if((result = commandSelect) == null) result = commandSelect = new Command("Выбрать", Command.OK, 0);
        return result;
    }

    Command getCommandParams() {
        Command result;
        if((result = commandParams) == null) result = commandParams = new Command("Параметры шрифта…", Command.SCREEN, 0);
        return result;
    }

    Command getCommandCreate() {
        Command result;
        if((result = commandCreate) == null) result = commandCreate = new Command("Создать глиф", Command.SCREEN, 0);
        return result;
    }

    Command getCommandDelete() {
        Command result;
        if((result = commandDelete) == null) result = commandDelete = new Command("Удалить глиф", Command.SCREEN, 0);
        return result;
    }

    Command getCommandTiltL() {
        Command result;
        if((result = commandTiltL) == null) result = commandTiltL = new Command("Наклонить влево", Command.SCREEN, 0);
        return result;
    }

    Command getCommandTiltR() {
        Command result;
        if((result = commandTiltR) == null) result = commandTiltR = new Command("Наклонить вправо", Command.SCREEN, 0);
        return result;
    }

    Command getCommandBold() {
        Command result;
        if((result = commandBold) == null) result = commandBold = new Command("Сделать жирнее", Command.SCREEN, 0);
        return result;
    }

    Command getCommandMirH() {
        Command result;
        if((result = commandMirH) == null) result = commandMirH = new Command("Отразить слева направо", Command.SCREEN, 0);
        return result;
    }

    Command getCommandMirV() {
        Command result;
        if((result = commandMirV) == null) result = commandMirV = new Command("Отразить сверху вниз", Command.SCREEN, 0);
        return result;
    }

    Command getCommandRotL() {
        Command result;
        if((result = commandRotL) == null) result = commandRotL = new Command("Повернуть на 90° влево", Command.SCREEN, 0);
        return result;
    }

    Command getCommandRotR() {
        Command result;
        if((result = commandRotR) == null) result = commandRotR = new Command("Повернуть на 90° вправо", Command.SCREEN, 0);
        return result;
    }

    Command getCommandRotU() {
        Command result;
        if((result = commandRotU) == null) result = commandRotU = new Command("Повернуть на 180°", Command.SCREEN, 0);
        return result;
    }

    Command getCommandCode() {
        Command result;
        if((result = commandCode) == null) result = commandCode = new Command("Код глифа…", Command.SCREEN, 0);
        return result;
    }

    Command getCommandEdit() {
        Command result;
        if((result = commandEdit) == null) result = commandEdit = new Command("Редактировать", Command.OK, 0);
        return result;
    }

    Command getCommandLoad() {
        Command result;
        if((result = commandLoad) == null) result = commandLoad = new Command("Загрузить", Command.OK, 0);
        return result;
    }

    Command getCommandSave() {
        Command result;
        if((result = commandSave) == null) result = commandSave = new Command("Сохранить", Command.OK, 0);
        return result;
    }

    Command getCommandBack() {
        Command result;
        if((result = commandBack) == null) result = commandBack = new Command("Назад", Command.BACK, 0);
        return result;
    }

    Command getCommandExit() {
        Command result;
        if((result = commandExit) == null) result = commandExit = new Command("Выход", Command.EXIT, 0);
        return result;
    }

    TextField getItemCode() {
        TextField result;
        if((result = itemCode) == null) (result = itemCode = new TextField("Код глифа", "", 8, Input.ANY)).setLayout(Item.LAYOUT_2 | Item.LAYOUT_EXPAND);
        return result;
    }

    TextField getItemLoadName() {
        TextField result;
        if((result = itemLoadName) == null)
        {
            result = itemLoadName = new TextField("Введите путь к файлу UFN-шрифта (должен существовать в папке приложения)", loadFileName(), Integer.MAX_VALUE, Input.ANY);
        }
        return result;
    }

    TextField getItemSaveName() {
        TextField result;
        if((result = itemSaveName) == null)
        {
            result = itemSaveName = new TextField("Введите путь к файлу UFN-шрифта (в папке приложения; будет перезаписан, если существует)", loadFileName(), Integer.MAX_VALUE, Input.ANY);
        }
        return result;
    }

    GlyphList getItemList() {
        GlyphList result;
        if((result = itemList) == null) result = itemList = new GlyphList(font);
        return result;
    }

    GlyphEditor getItemEditor() {
        GlyphEditor result;
        if((result = itemEditor) == null) result = itemEditor = new GlyphEditor(font);
        return result;
    }

    ChoiceGroup getItemFace() {
        ChoiceGroup result;
        if((result = itemFace) == null)
        {
            (result = itemFace = new ChoiceGroup("Начертание шрифта", Choice.MULTIPLE, new String[] {
                "Жирный", "Курсив"
            }, null)).setFont(0, Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL));
            result.setFont(1, Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_SMALL));
        }
        return result;
    }

    StringItem getItemUnderlineDown() {
        StringItem result;
        if((result = itemUnderlineDown) == null)
        {
            (result = itemUnderlineDown = new StringItem(null, "\u2193", Item.BUTTON)).setItemCommandListener(this);
            result.setLayout(Item.LAYOUT_VCENTER);
            result.setDefaultCommand(getCommandSelect());
        }
        return result;
    }

    StringItem getItemUnderlineVal() {
        StringItem result;
        if((result = itemUnderlineVal) == null)
        {
            (result = itemUnderlineVal = new StringItem(null, "")).setPreferredSize(100, -1);
            result.setLayout(Item.LAYOUT_VCENTER);
        }
        return result;
    }

    StringItem getItemUnderlineUp() {
        StringItem result;
        if((result = itemUnderlineUp) == null)
        {
            (result = itemUnderlineUp = new StringItem(null, "\u2191", Item.BUTTON)).setItemCommandListener(this);
            result.setLayout(Item.LAYOUT_VCENTER);
            result.setDefaultCommand(getCommandSelect());
        }
        return result;
    }

    StringItem getItemStrikeoutDown() {
        StringItem result;
        if((result = itemStrikeoutDown) == null)
        {
            (result = itemStrikeoutDown = new StringItem(null, "\u2193", Item.BUTTON)).setItemCommandListener(this);
            result.setLayout(Item.LAYOUT_VCENTER);
            result.setDefaultCommand(getCommandSelect());
        }
        return result;
    }

    StringItem getItemStrikeoutVal() {
        StringItem result;
        if((result = itemStrikeoutVal) == null)
        {
            (result = itemStrikeoutVal = new StringItem(null, "")).setPreferredSize(100, -1);
            result.setLayout(Item.LAYOUT_VCENTER);
        }
        return result;
    }

    StringItem getItemStrikeoutUp() {
        StringItem result;
        if((result = itemStrikeoutUp) == null)
        {
            (result = itemStrikeoutUp = new StringItem(null, "\u2191", Item.BUTTON)).setItemCommandListener(this);
            result.setLayout(Item.LAYOUT_VCENTER);
            result.setDefaultCommand(getCommandSelect());
        }
        return result;
    }

    StringItem getItemLineWidthDown() {
        StringItem result;
        if((result = itemLineWidthDown) == null)
        {
            (result = itemLineWidthDown = new StringItem(null, "–", Item.BUTTON)).setItemCommandListener(this);
            result.setLayout(Item.LAYOUT_VCENTER);
            result.setDefaultCommand(getCommandSelect());
        }
        return result;
    }

    StringItem getItemLineWidthVal() {
        StringItem result;
        if((result = itemLineWidthVal) == null)
        {
            (result = itemLineWidthVal = new StringItem(null, "")).setPreferredSize(100, -1);
            result.setLayout(Item.LAYOUT_VCENTER);
        }
        return result;
    }

    StringItem getItemLineWidthUp() {
        StringItem result;
        if((result = itemLineWidthUp) == null)
        {
            (result = itemLineWidthUp = new StringItem(null, "+", Item.BUTTON)).setItemCommandListener(this);
            result.setLayout(Item.LAYOUT_VCENTER);
            result.setDefaultCommand(getCommandSelect());
        }
        return result;
    }

    TextField getItemPreviewText() {
        TextField result;
        if((result = itemPreviewText) == null)
        {
            (result = itemPreviewText = new TextField(
                "Предварительный просмотр шрифта", "[Русский] В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!", Integer.MAX_VALUE, Input.ANY
            )).setItemCommandListener(this);
            result.addCommand(new Command("[Азербайджанский] Zəfər, jaketini də, papağını da götür, bu axşam hava çox soyuq olacaq.", Command.ITEM, 0));
            result.addCommand(new Command("[Английский] Jackdaws love my big sphinx of quartz.", Command.ITEM, 0));
            result.addCommand(new Command("[Английский] The five boxing wizards jump quickly.", Command.ITEM, 0));
            result.addCommand(new Command("[Английский] The quick brown fox jumps over the lazy dog.", Command.ITEM, 0));
            result.addCommand(new Command("[Армянский] Բել դղյակի ձախ ժամն օֆ ազգությանը ցպահանջ չճշտած վնաս էր եւ փառք։", Command.ITEM, 0));
            result.addCommand(new Command("[Болгарский] Ах, чудна българска земьо, полюшвай цъфтящи жита.", Command.ITEM, 0));
            result.addCommand(new Command("[Белорусский] У Іўі худы жвавы чорт у зялёнай камізэльцы пабег пад’есці фаршу з юшкай.", Command.ITEM, 0));
            result.addCommand(new Command("[Белорусский] У рудога вераб’я ў сховішчы пад фатэлем ляжаць нейкія гаючыя зёлкі.", Command.ITEM, 0));
            result.addCommand(new Command("[Белорусский] Я жорстка заб’ю проста ў сэрца гэты расквечаны профіль, што ходзіць ля маёй хаты.", Command.ITEM, 0));
            result.addCommand(new Command("[Греческий] Γαζίες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο.", Command.ITEM, 0));
            result.addCommand(new Command("[Греческий] Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία.", Command.ITEM, 0));
            result.addCommand(new Command("[Греческий] Τάχιστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός.", Command.ITEM, 0));
            result.addCommand(new Command("[Ирландский] Chuaigh bé mhórshách le dlúthspád fíorfhinn trí hata mo dhea-phorcáin bhig.", Command.ITEM, 0));
            result.addCommand(new Command("[Испанский] El jefe buscó el éxtasis en un imprevisto baño de whisky y gozó como un duque.", Command.ITEM, 0));
            result.addCommand(new Command("[Испанский] Exhíbanse politiquillos zafios, con orejas kilométricas y uñas de gavilán.", Command.ITEM, 0));
            result.addCommand(new Command("[Испанский] La cigüeña tocaba cada vez mejor el saxofón y el búho pedía kiwi y queso.", Command.ITEM, 0));
            result.addCommand(new Command("[Итальянский] Pranzo d’acqua fa volti sghembi.", Command.ITEM, 0));
            result.addCommand(new Command("[Итальянский] Qualche vago ione tipo zolfo, bromo, sodio", Command.ITEM, 0));
            result.addCommand(new Command("[Итальянский] Quel vituperabile xenofobo zelante assaggia il whisky ed esclama: alleluja!", Command.ITEM, 0));
            result.addCommand(new Command("[Корейский] 키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다.", Command.ITEM, 0));
            result.addCommand(new Command("[Латинский] Gaza frequens Lybicum duxit Karthago triumphum.", Command.ITEM, 0));
            result.addCommand(new Command("[Латышский] Glāžšķūņa rūķīši dzērumā čiepj Baha koncertflīģeļu vākus.", Command.ITEM, 0));
            result.addCommand(new Command("[Латышский] Ķieģeļu cepējs Edgars Buls fraku un hūti žāvē uz čīkstošām eņģēm.", Command.ITEM, 0));
            result.addCommand(new Command("[Латышский] Četri psihi faķīri vēlu vakarā zāģēja guļbūvei durvis, fonā šņācot mežam.", Command.ITEM, 0));
            result.addCommand(new Command("[Немецкий] Zwölf große Boxkämpfer jagen Viktor quer über den Sylter Deich.", Command.ITEM, 0));
            result.addCommand(new Command("[Немецкий] Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.", Command.ITEM, 0));
            result.addCommand(new Command("[Немецкий] «Fix, Schwyz!» quäkt Jürgen blöd vom Paß.", Command.ITEM, 0));
            result.addCommand(new Command("[Польский] Pchnąć w tę łódź jeża lub ośm skrzyń fig.", Command.ITEM, 0));
            result.addCommand(new Command("[Польский] Pójdźże, kiń tę chmurność w głąb flaszy!", Command.ITEM, 0));
            result.addCommand(new Command("[Польский] Mężny bądź, chroń pułk twój i sześć flag.", Command.ITEM, 0));
            result.addCommand(new Command("[Русский] В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!", Command.ITEM, 0));
            result.addCommand(new Command("[Русский] Съешь же ещё этих мягких французских булок да выпей чаю.", Command.ITEM, 0));
            result.addCommand(new Command("[Русский] Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.", Command.ITEM, 0));
            result.addCommand(new Command("[Украинский] Гей, хлопці, не вспію — на ґанку ваша файна їжа знищується бурундучком.", Command.ITEM, 0));
            result.addCommand(new Command("[Украинский] Глянь (!): що ж є шрифт, цей «спазм» — ґід букв? Юч їх.", Command.ITEM, 0));
            result.addCommand(new Command("[Украинский] Єхидна, ґава, їжак ще й шиплячі плазуни бігцем форсують Янцзи.", Command.ITEM, 0));
            result.addCommand(new Command("[Французский] Dès Noël où un zéphyr haï me vêt de glaçons würmiens je dîne d’exquis rôtis de bœuf au kir à l’aÿ d’âge mûr & cætera!", Command.ITEM, 0));
            result.addCommand(new Command("[Французский] Portez ce vieux whisky au juge blond qui fume.", Command.ITEM, 0));
            result.addCommand(new Command("[Чешский] Příliš žluťoučký kůň úpěl ďábelské ódy.", Command.ITEM, 0));
            result.addCommand(new Command("[Эсперанто] Gajecvoĉa fuŝmuĝaĵo de knabĥoro haltpaŭzis.", Command.ITEM, 0));
            result.addCommand(new Command("[Эсперанто] Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj.", Command.ITEM, 0));
            result.addCommand(new Command("[Японский] いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさきゆめみし ゑひもせす.", Command.ITEM, 0));
        }
        return result;
    }

    ChoiceGroup getItemPreviewStyle() {
        ChoiceGroup result;
        if((result = itemPreviewStyle) == null)
        {
            result = itemPreviewStyle = new ChoiceGroup(null, Choice.MULTIPLE, new String[] { "Подчёркнутый", "Зачёркнутый" }, null);
            for(int i = result.size(); i-- > 0; result.setSelectedIndex(i, true));
        }
        return result;
    }

    FontPreview getItemPreviewFont() {
        FontPreview result;
        if((result = itemPreviewFont) == null) result = itemPreviewFont = new FontPreview(font);
        return result;
    }

    List getScreenMainMenu() {
        List result;
        if((result = screenMainMenu) == null)
        {
            (result = screenMainMenu = new List("Редактор шрифтов UFN", Choice.IMPLICIT, new String[] {
                "Перейти в редактор", "Создать шрифт", "Сохранить шрифт", "Загрузить шрифт", "О программе…", "Выход"
            }, null)).setCommandListener(this);
            result.addCommand(getCommandExit());
        }
        return result;
    }

    Form getScreenGlyphList() {
        Form result;
        if((result = screenGlyphList) == null)
        {
            (result = screenGlyphList = new Form("Список глифов шрифта", new Item[] { getItemList() })).setCommandListener(this);
            result.addCommand(getCommandEdit());
            result.addCommand(getCommandBack());
            result.addCommand(getCommandCreate());
            result.addCommand(getCommandCopy());
            result.addCommand(getCommandCode());
            result.addCommand(getCommandDelete());
            result.addCommand(getCommandBold());
            result.addCommand(getCommandTiltL());
            result.addCommand(getCommandTiltR());
            result.addCommand(getCommandParams());
        }
        return result;
    }

    Form getScreenGlyphCode() {
        Form result;
        if((result = screenGlyphCode) == null)
        {
            StringItem help;
            Spacer margin = new Spacer(4, 0);
            (help = new StringItem(
                null,
                "Введите в это поле шестнадцатеричный код глифа (до 8 символов). Разрешено вводить цифры 0123456789 и латинские буквы ABCDEF, abcdef. " +
                "Нули в начале кода игнорируются. К примеру, коды «20», «0020», «0000020» – это один и тот же код."
            )).setLayout(Item.LAYOUT_EXPAND);
            help.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL));
            (result = screenGlyphCode = new Form("Код глифа", new Item[] { getItemCode(), margin, help })).setCommandListener(this);
            result.addCommand(getCommandChangeCode());
            result.addCommand(getCommandBack());
            result.setItemStateListener(this);
        }
        return result;
    }

    Form getScreenGlyphEditor() {
        Form result;
        if((result = screenGlyphEditor) == null)
        {
            (result = screenGlyphEditor = new Form(null, new Item[] { getItemEditor() })).setCommandListener(this);
            result.addCommand(getCommandBack());
            result.addCommand(getCommandModeDraw());
            result.addCommand(getCommandModeMove());
            result.addCommand(getCommandModeSize());
            result.addCommand(getCommandClear());
            result.addCommand(getCommandCut());
            result.addCommand(getCommandCopy());
            result.addCommand(getCommandPaste());
            result.addCommand(getCommandMirH());
            result.addCommand(getCommandMirV());
            result.addCommand(getCommandRotL());
            result.addCommand(getCommandRotR());
            result.addCommand(getCommandRotU());
            result.addCommand(getCommandBold());
            result.addCommand(getCommandTiltL());
            result.addCommand(getCommandTiltR());
            result.addCommand(getCommandZoomIn());
            result.addCommand(getCommandZoomOut());
        }
        return result;
    }

    Form getScreenFontParams() {
        Form result;
        if((result = screenFontParams) == null)
        {
            Item itemUnderlineLabel = new StringItem(null, "Позиция линии подчёркивания:");
            Item itemStrikeoutLabel = new StringItem(null, "Позиция линии зачёркивания:");
            Item itemLineWidthLabel = new StringItem(null, "Толщина линий:");
            itemUnderlineLabel.setLayout(Item.LAYOUT_VCENTER | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_EXPAND);
            itemStrikeoutLabel.setLayout(Item.LAYOUT_VCENTER | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_EXPAND);
            itemLineWidthLabel.setLayout(Item.LAYOUT_VCENTER | Item.LAYOUT_NEWLINE_BEFORE | Item.LAYOUT_EXPAND);
            (result = screenFontParams = new Form("Параметры шрифта", new Item[] {
                getItemFace(), itemUnderlineLabel, getItemUnderlineDown(), getItemUnderlineVal(), getItemUnderlineUp(), itemStrikeoutLabel, getItemStrikeoutDown(), getItemStrikeoutVal(),
                getItemStrikeoutUp(), itemLineWidthLabel, getItemLineWidthDown(), getItemLineWidthVal(), getItemLineWidthUp(), getItemPreviewText(), getItemPreviewStyle(), getItemPreviewFont()
            })).setCommandListener(this);
            result.setItemStateListener(this);
            result.addCommand(getCommandBack());
        }
        return result;
    }

    Form getScreenFontLoad() {
        Form result;
        if((result = screenFontLoad) == null)
        {
            (result = screenFontLoad = new Form("Загрузить шрифт", new Item[] { getItemLoadName() })).setCommandListener(this);
            result.addCommand(getCommandBack());
            result.addCommand(getCommandLoad());
        }
        return result;
    }

    Form getScreenFontSave() {
        Form result;
        if((result = screenFontSave) == null)
        {
            (result = screenFontSave = new Form("Сохранить шрифт", new Item[] { getItemSaveName() })).setCommandListener(this);
            result.addCommand(getCommandBack());
            result.addCommand(getCommandSave());
        }
        return result;
    }

    Alert getScreenOperation() {
        Alert result;
        if((result = screenOperation) == null)
        {
            (result = screenOperation = new Alert(null, "Операция выполняется…", null, AlertType.INFO)).setCommandListener(this);
            result.setIndicator(new Gauge(null, false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING));
        }
        return result;
    }

    Alert getScreenFontBoldWarn() {
        Alert result;
        if((result = screenFontBoldWarn) == null)
        {
            (result = screenFontBoldWarn = new Alert(
                null,
                "Осторожно: эта операция будет применена ко всем глифам шрифта и её нельзя будет отменить. Используйте эту операцию только для ускорения создания жирных шрифтов. " +
                "Сделать все глифы жирнее?", null, AlertType.WARNING
            )).setCommandListener(this);
            result.addCommand(getCommandYes());
            result.addCommand(getCommandNo());
        }
        return result;
    }

    Alert getScreenFontTiltLWarn() {
        Alert result;
        if((result = screenFontTiltLWarn) == null)
        {
            (result = screenFontTiltLWarn = new Alert(
                null,
                "Осторожно: эта операция будет применена ко всем глифам шрифта. Используйте её только для отмены курсивного начертания шрифта. Наклонить все глифы влево?", null, AlertType.WARNING
            )).setCommandListener(this);
            result.addCommand(getCommandYes());
            result.addCommand(getCommandNo());
        }
        return result;
    }

    Alert getScreenFontTiltRWarn() {
        Alert result;
        if((result = screenFontTiltRWarn) == null)
        {
            (result = screenFontTiltRWarn = new Alert(
                null,
                "Осторожно: эта операция будет применена ко всем глифам шрифта. Используйте её только для ускорения создания курсивных шрифтов. Наклонить все глифы вправо?", null, AlertType.WARNING
            )).setCommandListener(this);
            result.addCommand(getCommandYes());
            result.addCommand(getCommandNo());
        }
        return result;
    }

    Alert getScreenFontLoadError() {
        Alert result;
        if((result = screenFontLoadError) == null)
        {
            result = screenFontLoadError = new Alert(
                null,
                "Ошибка возникла при загрузке файла шрифта. Возможно, файл не существует, или недоступен, или имеет неправильный формат.", null, AlertType.ERROR
            );
        }
        return result;
    }

    Alert getScreenFontSaveError() {
        Alert result;
        if((result = screenFontSaveError) == null)
        {
            result = screenFontSaveError = new Alert(
                null,
                "Ошибка возникла при сохранении файла шрифта. Возможно, файл недоступен для создания/перезаписи, или путь введён с ошибками.", null, AlertType.ERROR
            );
        }
        return result;
    }

    Alert getScreenGlyphCodeError() {
        Alert result;
        if((result = screenGlyphCodeError) == null) result = screenGlyphCodeError = new Alert(null, "Глиф с таким кодом уже существует.", null, AlertType.ERROR);
        return result;
    }

    Alert getScreenGlyphListEmpty() {
        Alert result;
        if((result = screenGlyphListEmpty) == null) result = screenGlyphListEmpty = new Alert(null, "Список глифов пуст. Создайте хотя бы один глиф.", null, AlertType.ERROR);
        return result;
    }

    Alert getScreenGlyphListFilling() {
        Alert result;
        if((result = screenGlyphListFilling) == null)
        {
            Gauge indicator = new Gauge(null, false, 1, 0);
            (result = screenGlyphListFilling = new Alert(null, "Чтение глифов шрифта…", null, AlertType.INFO)).setCommandListener(this);
            result.setIndicator(indicator);
        }
        return result;
    }

    Alert getScreenGlyphListChanging() {
        Alert result;
        if((result = screenGlyphListChanging) == null)
        {
            Gauge indicator = new Gauge(null, false, 1, 0);
            (result = screenGlyphListChanging = new Alert(null, "Модификация глифов шрифта…", null, AlertType.INFO)).setCommandListener(this);
            result.setIndicator(indicator);
        }
        return result;
    }

    Alert getScreenAbout() {
        Alert result;
        if((result = screenAbout) == null)
        {
            Image icon;
            try
            {
                icon = Image.createImage("/icon.png");
            }
            catch(Exception e)
            {
                icon = null;
            }
            result = screenAbout = new Alert(null, getAppProperty("MIDlet-Name") + "\nЭта программа создана специально для программы Малик Эмулятор.\n\nВерсия: " + getAppProperty("MIDlet-Version") + (
                "\n\nCopyright © 2019, 2022 Малик Разработчик\n\nЭто свободная программа: вы можете перераспространять её и/или изменять её на условиях Стандартной общественной лицензии GNU в том " +
                "виде, в каком она была опубликована Фондом свободного программного обеспечения; либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.\n\nЭта программа " +
                "распространяется в надежде, что она будет полезной, но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в " +
                "Стандартной общественной лицензии GNU.\n\nВы должны были получить копию Стандартной общественной лицензии GNU вместе с этой программой. Если это не так, см. " +
                "<https://www.gnu.org/licenses/>."
            ), icon, AlertType.INFO);
        }
        return result;
    }
}
