/*
	Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
	и других спецификаций для функционирования компактных приложений на языке
	Java (мидлетов) в среде программного обеспечения Малик Эмулятор.

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

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

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

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


package malik.emulator.midp;

import java.io.*;
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;
import malik.emulator.application.*;
import malik.emulator.fileformats.text.mapped.*;
import malik.emulator.io.cloud.*;
import malik.emulator.io.j2me.*;
import malik.emulator.media.text.*;
import malik.emulator.util.*;

public class SystemGUI extends Object
		implements CommandListener, ItemCommandListener,
		ItemStateListener, VirtualSystemGUI, ThreadTerminationListener
{
	public static final Command APPLY_SETTINGS_COMMAND;
	public static final Command BACK_TO_SETTINGS_COMMAND;
	private static final int NAME = 0;
	private static final int ICON = 1;
	private static final int CLASS = 2;
	private static final int SETTABLE_KEY_CODES = 7;
	private static final int NATIVE_SYSTEM_SETTINGS = 3;
	private static final int[] KEYS;
	private static final int[] DEVICE_KEYS;
	private static final KeyCodeSet[] KEY_CODE_SETS;
	private static final String YES = "Да";
	private static final String NO = "Нет";

	static
	{
		APPLY_SETTINGS_COMMAND = new Command("Применить", Command.SCREEN, Integer.MAX_VALUE);
		BACK_TO_SETTINGS_COMMAND = new Command("Назад", Command.BACK, Integer.MIN_VALUE);
		KEYS = new int[] {
				0, AppProxy.KEY_ESCAPE, AppProxy.KEY_F1, AppProxy.KEY_F2, AppProxy.KEY_F3,
				AppProxy.KEY_F4, AppProxy.KEY_F5, AppProxy.KEY_F6, AppProxy.KEY_F7,
				AppProxy.KEY_F8, AppProxy.KEY_F9, AppProxy.KEY_F10, AppProxy.KEY_F11,
				AppProxy.KEY_F12, AppProxy.KEY_F13, AppProxy.KEY_F14, AppProxy.KEY_F15,
				AppProxy.KEY_F16, AppProxy.KEY_F17, AppProxy.KEY_F18, AppProxy.KEY_F19,
				AppProxy.KEY_F20, AppProxy.KEY_F21, AppProxy.KEY_F22, AppProxy.KEY_F23,
				AppProxy.KEY_F24, AppProxy.KEY_ENTER, AppProxy.KEY_SPACE, AppProxy.KEY_INSERT,
				AppProxy.KEY_DELETE, AppProxy.KEY_HOME, AppProxy.KEY_END, AppProxy.KEY_PAGE_UP,
				AppProxy.KEY_PAGE_DOWN, AppProxy.KEY_UP, AppProxy.KEY_DOWN, AppProxy.KEY_LEFT,
				AppProxy.KEY_RIGHT, AppProxy.KEY_NUM_PLUS, AppProxy.KEY_NUM_MINUS,
				AppProxy.KEY_NUM_STAR, AppProxy.KEY_NUM_DIVIDE, AppProxy.KEY_NUM_DECIMAL,
				AppProxy.KEY_NUM_0, AppProxy.KEY_NUM_1, AppProxy.KEY_NUM_2, AppProxy.KEY_NUM_3,
				AppProxy.KEY_NUM_4, AppProxy.KEY_NUM_5, AppProxy.KEY_NUM_6, AppProxy.KEY_NUM_7,
				AppProxy.KEY_NUM_8, AppProxy.KEY_NUM_9
		};
		DEVICE_KEYS = new int[] {
				MIDletProxy.DEVICE_KEY_UP, MIDletProxy.DEVICE_KEY_DOWN,
				MIDletProxy.DEVICE_KEY_LEFT, MIDletProxy.DEVICE_KEY_RIGHT,
				MIDletProxy.DEVICE_KEY_SELECT, MIDletProxy.DEVICE_KEY_SOFT1,
				MIDletProxy.DEVICE_KEY_SOFT2, MIDletProxy.DEVICE_KEY_CONSOLE,
				MIDletProxy.DEVICE_KEY_EXITAPP,
				MIDletProxy.DEVICE_KEY_1, MIDletProxy.DEVICE_KEY_2, MIDletProxy.DEVICE_KEY_3,
				MIDletProxy.DEVICE_KEY_4, MIDletProxy.DEVICE_KEY_5, MIDletProxy.DEVICE_KEY_6,
				MIDletProxy.DEVICE_KEY_7, MIDletProxy.DEVICE_KEY_8, MIDletProxy.DEVICE_KEY_9,
				MIDletProxy.DEVICE_KEY_STAR, MIDletProxy.DEVICE_KEY_0, MIDletProxy.DEVICE_KEY_POUND
		};
		KEY_CODE_SETS = new KeyCodeSet[] {
				new KeyCodeSet("Свои настройки", 0, 0, 0, 0, 0, 0, 0),
				new KeyCodeSet("Nokia (по умолчанию)", -1, -2, -3, -4, -5, -6, -7),
				new KeyCodeSet("Motorola", -1, -6, -2, -5, -20, -21, -22),
				new KeyCodeSet("Siemens", -59, -60, -61, -62, -26, -1, -4)
		};
	}

	public static Image loadImage(String fileName)
	{
		Image result;
		FileInputStream stream;
		try
		{
			(stream = new FileInputStream(fileName)).checkOpenError();
			try
			{
				result = Image.createImage(stream);
			}
			finally
			{
				stream.close();
			}
		}
		catch(IOException e)
		{
			e.printRealStackTrace();
			result = null;
		}
		return result;
	}

	public static Image loadMIDletIcon(AttributableMappedTextDecoder descriptor)
	{
		String iconRes;
		if((iconRes = descriptor.get("MIDlet-Icon")) == null)
		{
			return null;
		}
		if(iconRes.length() > 0 && iconRes.charAt(0) == '/')
		{
			iconRes = iconRes.substring(1);
		}
		return loadImage("/res/".concat(iconRes));
	}

	private static int toFontSize(int systemGUISize)
	{
		return systemGUISize == 2 ? 2 : systemGUISize ^ 0x01;
	}

	private static int indexOfKey(int key)
	{
		int i;
		int[] keys;
		for(i = (keys = KEYS).length; i-- > 1; )
		{
			if(keys[i] == key)
			{
				return i;
			}
		}
		return 0;
	}

	private static int indexOfFont(RasterFont font)
	{
		int i;
		for(i = RasterFont.getRasterFontsCount(); i-- > 0; )
		{
			if(RasterFont.getRasterFontAtIndex(i) == font)
			{
				return i;
			}
		}
		return -1;
	}

	private static String[] fillMIDletList(Choice dst, AttributableMappedTextDecoder descriptor)
	{
		int i;
		int len;
		String[] attr;
		String[] result;
		String iconRes;
		String className;
		String midletName;
		len = 0;
		result = new String[1];
		dst.deleteAll();
		for(i = 0; (attr = descriptor.getAttributes("MIDlet-" + (i + 1))) != null &&
				attr.length >= 3; i++)
		{
			if((iconRes = attr[ICON]) == null)
			{
				continue;
			}
			if(iconRes.length() > 0 && iconRes.charAt(0) == '/')
			{
				iconRes = iconRes.substring(1);
			}
			if((className = attr[CLASS]) == null || (midletName = attr[NAME]) == null)
			{
				continue;
			}
			if(len == result.length)
			{
				Array.copy(result, 0, result = new String[(len << 1) + 1], 0, len);
			}
			result[len++] = className;
			dst.append(midletName, loadImage("/res/".concat(iconRes)));
		}
		if(len != result.length)
		{
			Array.copy(result, 0, result = new String[len], 0, len);
		}
		return result;
	}


	private boolean midletStarted;
	private String[] midletClassNames;
	private Displayable[] screensOfSettings;
	private String midletName;
	private Command commandLaunch;
	private Command commandExit;
	private Command commandSettings;
	private Command commandAboutApplication;
	private Command commandAboutImplementation;
	private Command commandProperties;
	private Command commandDeleteAll;
	private Command commandDelete;
	private Command commandUpdate;
	private Command commandBack;
	private Command commandYes;
	private Command commandNo;
	private Ticker tickerLaunch;
	private Ticker tickerSettings;
	private Gauge itemFrequency;
	private ChoiceGroup itemModelSelect;
	private ChoiceGroup itemSystemSettings;
	private StringItem itemRecordName;
	private StringItem itemRecordSize;
	private StringItem itemRecordNumRecords;
	private StringItem itemRecordLastModified;
	private StringItem itemRecordVersion;
	private ButtonSet itemKeypad;
	private StringItem itemKeyDescription;
	private StringItem itemKeyEquivalent;
	private StringItem itemLibraryList;
	private List screenMIDlets;
	private List screenSettings;
	private Form screenSettingControl;
	private Form screenSettingKeyCodes;
	private Form screenSettingFonts;
	private Form screenSettingFrequency;
	private Form screenSettingSystem;
	private List screenSettingRecords;
	private Form screenSettingKeypad;
	private Form screenRecordProperties;
	private Alert screenRecordDelete;
	private Alert screenRecordDeleteAll;
	private Alert screenRecordDeletionError;
	private Form screenAboutApplication;
	private Form screenAboutImplementation;
	private Form screenError;
	private Alert screenExit;
	private ConsoleScreen screenConsole;

	public SystemGUI()
	{
		int i;
		List scr;
		String midletName;
		String parameterKey;
		String parameterValue;
		ConsoleScreen console;
		AttributableMappedTextDecoder descriptor = MIDletProxy.getInstance().getMIDletDescriptor();
		Run.getInstance().setThreadProxy(this);
		this.midletClassNames = fillMIDletList(scr = getScreenMIDlets(), descriptor);
		this.midletName = midletName = descriptor.get("MIDlet-Name");
		this.screenConsole = console = new SystemConsole("Консоль Java 2 ME", this);
		scr.setTitle(midletName);
		getScreenExit();
		console.addConsoleCommand(new ImageCommand());
		console.addConsoleCommand(new SoundCommand());
		for(i = 1; true; i++)
		{
			parameterKey = "malik.emulator.microedition.console.command.".concat(
					Integer.toString(i));
			if((parameterValue = System.getSystemProperty(parameterKey)) == null)
			{
				break;
			}
			try
			{
				console.addConsoleCommand((ConsoleCommand) Class.forName(parameterValue).
						newInstance());
			}
			catch(Exception e)
			{
				e.printRealStackTrace();
			}
		}
	}

	public void commandAction(Command command, Displayable screen)
	{
		int sel;
		List list;
		Display display;
		MIDletProxy proxy;
		display = (proxy = MIDletProxy.getInstance()).getEmulatorScreen();
		if(command == APPLY_SETTINGS_COMMAND)
		{
			applySettingsFromScreen((list = getScreenSettings()).getSelectedIndex());
			display.setCurrent(list);
			return;
		}
		if(command == BACK_TO_SETTINGS_COMMAND)
		{
			display.setCurrent(getScreenSettings());
			return;
		}
		if(screen == (list = screenMIDlets))
		{
			if(command == commandLaunch)
			{
				launchSelectedMIDlet(list);
				return;
			}
			if(command == commandSettings)
			{
				display.setCurrent(getScreenSettings());
				return;
			}
			if(command == commandAboutApplication)
			{
				display.setCurrent(getScreenAboutApplication());
				return;
			}
			if(command == commandAboutImplementation)
			{
				display.setCurrent(getScreenAboutImplementation());
				return;
			}
			if(command == commandExit)
			{
				Run.getInstance().terminate();
			}
			return;
		}
		if(screen == (list = screenSettings))
		{
			if(command == List.SELECT_COMMAND)
			{
				if((sel = list.getSelectedIndex()) == list.size() - 1)
				{
					Run.getInstance().showMessage("Сохранение настроек…");
					proxy.saveMIDletProperties();
					display.setCurrent(screenMIDlets);
					return;
				}
				updateSettingsScreen(sel);
				display.setCurrent(screensOfSettings[sel]);
				return;
			}
			if(command == commandBack)
			{
				display.setCurrent(screenMIDlets);
			}
			return;
		}
		if(screen == (list = screenSettingRecords))
		{
			if(command == commandProperties)
			{
				showSelectedRecordProperties(list, display);
				return;
			}
			if(command == commandDelete)
			{
				showSelectedRecordDeleteDialog(list, display);
				return;
			}
			if(command == commandDeleteAll)
			{
				display.setCurrent(getScreenRecordDeleteAll());
				return;
			}
			if(command == commandUpdate)
			{
				updateRecordList();
			}
			return;
		}
		if(screen == screenRecordProperties && command == commandBack)
		{
			display.setCurrent(screenSettingRecords);
			return;
		}
		if(screen == screenRecordDelete)
		{
			if(command == commandYes)
			{
				deleteSelectedRecord(display);
				return;
			}
			if(command == commandNo)
			{
				display.setCurrent(screenSettingRecords);
			}
			return;
		}
		if(screen == screenRecordDeleteAll)
		{
			if(command == commandYes)
			{
				deleteAllRecords(display);
				return;
			}
			if(command == commandNo)
			{
				display.setCurrent(screenSettingRecords);
			}
			return;
		}
		if(screen == screenRecordDeletionError && command == Alert.DISMISS_COMMAND)
		{
			updateRecordList();
			display.setCurrent(screenSettingRecords);
			return;
		}
		if((screen == screenAboutApplication || screen == screenAboutImplementation) &&
				command == commandBack)
		{
			display.setCurrent(screenMIDlets);
			return;
		}
		if(screen == screenConsole && command == ConsoleScreen.BACK_COMMAND)
		{
			display.hideSystemScreen();
			return;
		}
		if(screen == screenExit)
		{
			if(command != null && YES.equals(command.getLabel()))
			{
				stopMIDlet();
				return;
			}
			display.hideSystemScreen();
			return;
		}
		if(screen == screenError && command == commandExit)
		{
			Run.getInstance().terminate();
		}
	}

	public void commandAction(Command command, Item item)
	{
		int i;
		Displayable[] screens;
		Displayable scr;
		FormSettings form;
		if(command != List.SELECT_COMMAND)
		{
			return;
		}
		for(i = (screens = screensOfSettings).length; i-- > 0; )
		{
			if(!((scr = screens[i]) instanceof FormSettings))
			{
				continue;
			}
			form = (FormSettings) scr;
			if(item == form.getApplyButton())
			{
				commandAction(APPLY_SETTINGS_COMMAND, form);
				break;
			}
			if(item == form.getBackButton())
			{
				commandAction(BACK_TO_SETTINGS_COMMAND, form);
				break;
			}
		}
	}

	public void itemStateChanged(Item item)
	{
		int i;
		int code;
		int[] codes;
		MIDletProxy proxy;
		ButtonSet keypad;
		Choice list;
		Form form;
		if(item == (list = itemModelSelect))
		{
			form = screenSettingKeyCodes;
			i = (codes = KEY_CODE_SETS[list.getSelectedIndex()].getKeyCodesAsArray()).length;
			while(i-- > 0)
			{
				if((code = codes[i]) != 0)
				{
					((Input) form.get(i + 1)).setString(Integer.toString(code));
				}
			}
			return;
		}
		if(item == (keypad = itemKeypad))
		{
			proxy = MIDletProxy.getInstance();
			switch(code = keypad.getPressedButtonID())
			{
			case MIDletProxy.DEVICE_KEY_UP:
			case MIDletProxy.DEVICE_KEY_DOWN:
			case MIDletProxy.DEVICE_KEY_LEFT:
			case MIDletProxy.DEVICE_KEY_RIGHT:
			case MIDletProxy.DEVICE_KEY_SELECT:
			case MIDletProxy.DEVICE_KEY_SOFT1:
			case MIDletProxy.DEVICE_KEY_SOFT2:
			case MIDletProxy.DEVICE_KEY_1:
			case MIDletProxy.DEVICE_KEY_2:
			case MIDletProxy.DEVICE_KEY_3:
			case MIDletProxy.DEVICE_KEY_4:
			case MIDletProxy.DEVICE_KEY_5:
			case MIDletProxy.DEVICE_KEY_6:
			case MIDletProxy.DEVICE_KEY_7:
			case MIDletProxy.DEVICE_KEY_8:
			case MIDletProxy.DEVICE_KEY_9:
			case MIDletProxy.DEVICE_KEY_STAR:
			case MIDletProxy.DEVICE_KEY_0:
				itemKeyDescription.setText(proxy.getKeyName(proxy.getKeyCodeFor(code)) +
						"\nКод клавиши: " + Integer.toString(proxy.getKeyCodeFor(code)) +
						"\nНазначение: посылает в приложение соответствующий код клавиши.");
				break;
			case MIDletProxy.DEVICE_KEY_POUND:
				itemKeyDescription.setText(proxy.getKeyName(proxy.getKeyCodeFor(code)) +
						"\nКод клавиши: " + Integer.toString(proxy.getKeyCodeFor(code)) +
						"\nНазначение: посылает в приложение соответствующий код клавиши." +
						"\nПримечание: эта клавиша позволяет полностью просмотреть выделенный " +
						"элемент списка. Если на экране отображается стандартный список " +
						"каких-нибудь элементов и среди них есть такие, которые заканчиваются " +
						"на многоточие (…), выделите такой элемент и нажмите эту клавишу. Пока " +
						"вы её удерживаете, на экране отображается полный текст выделенного " +
						"элемента, без многоточия.");
				break;
			case MIDletProxy.DEVICE_KEY_CONSOLE:
				itemKeyDescription.setText("Вызов консоли" +
						"\nНазначение: вызов системной консоли " +
						"(доступно только во время работы приложения).");
				break;
			case MIDletProxy.DEVICE_KEY_EXITAPP:
				itemKeyDescription.setText("Выход из приложения" +
						"\nНазначение: вызов диалога выхода из приложения " +
						"(доступно только во время работы приложения).");
				break;
			default:
				return;
			}
			itemKeyEquivalent.setText(MIDletProxy.getSystemKeyName(proxy.getKeyUsedAs(code)));
		}
	}

	public void showStartScreen(Display display)
	{
		display.setCurrent(screenMIDlets);
	}

	public void showConsole(Display display)
	{
		if(display.isClientInForeground())
		{
			display.setCurrent(screenConsole);
		}
	}

	public void showExitScreen(Display display)
	{
		if(display.isClientInForeground())
		{
			display.setCurrent(screenExit);
		}
	}

	public void onMIDletStarted()
	{
	}

	public void onMIDletDestroyed()
	{
		Run.getInstance().terminate();
	}

	public void onAppTerminate()
	{
	}

	public void threadTerminated(Thread terminatedThread, Throwable exitThrowable)
	{
		if(exitThrowable == null)
		{
			return;
		}
		exitThrowable.printRealStackTrace();
		MIDletProxy.getInstance().getEmulatorScreen().setCurrent(getScreenError(exitThrowable));
		RecordStore.closeAllRecordStores();
	}

	public void onAboutImplementationScreenShow()
	{
	}

	public void onSettingsScreenShow()
	{
	}

	public void onSettingsUpdate(Displayable screen)
	{
	}

	public void onSettingsApply(Displayable screen)
	{
	}

	public void onSystemSettingsScreenShow()
	{
	}

	public void onSystemSettingsUpdate()
	{
	}

	public void onSystemSettingsApply()
	{
	}

	public void startMIDlet(Class midletClass)
	{
		MIDletProxy.getInstance().getEmulatorScreen().setCurrent(
				new LaunchScreen(this, midletClass));
	}

	public void stopMIDlet()
	{
		MIDletProxy.getInstance().stopMIDlet();
	}

	public void addConsoleCommand(ConsoleCommand command)
	{
		screenConsole.addConsoleCommand(command);
	}

	public void addThirdPartyLibraryInformation(String libraryNameAndJSR)
	{
		StringItem list;
		if((list = itemLibraryList) == null)
		{
			throw new IllegalStateException("SystemGUI.addThirdPartyLibraryInformation: " +
					"экран сведений о реализации ещё не создан.");
		}
		if(libraryNameAndJSR == null)
		{
			throw new NullPointerException("SystemGUI.addThirdPartyLibraryInformation: " +
					"параметр libraryNameAndJSR равен нулевой ссылке.");
		}
		list.setText(list.getText() + "\n" + libraryNameAndJSR);
	}

	public void addSettingScreen(String label, Image icon, Displayable screen)
	{
		int len;
		Displayable[] screens;
		Choice settings;
		if((screens = screensOfSettings) == null || (settings = screenSettings) == null)
		{
			throw new IllegalStateException("SystemGUI.addSettingScreen: " +
					"экран настроек ещё не создан.");
		}
		if(label == null)
		{
			throw new NullPointerException("SystemGUI.addSettingScreen: " +
					"параметр label равен нулевой ссылке.");
		}
		if(screen == null)
		{
			throw new NullPointerException("SystemGUI.addSettingScreen: " +
					"параметр screen равен нулевой ссылке.");
		}
		if((len = settings.size() - 1) == screens.length)
		{
			Array.copyReferences(screens, 0,
					screens = screensOfSettings = new Displayable[(len << 1) + 1], 0, len);
		}
		screens[len] = screen;
		settings.insert(len, label, icon);
	}

	public int addSystemSetting(String label, String description, Image icon)
	{
		Choice list;
		String text;
		if((list = itemSystemSettings) == null)
		{
			throw new IllegalStateException("SystemGUI.addSystemSetting: " +
					"экран настроек системы ещё не создан.");
		}
		if(label == null)
		{
			throw new NullPointerException("SystemGUI.addSystemSetting: " +
					"параметр label равен нулевой ссылке.");
		}
		text = description == null ? label : label + "\n\n" + description;
		return list.append(text, icon) - NATIVE_SYSTEM_SETTINGS;
	}

	public final void setSystemSetting(int index, boolean value)
	{
		Choice list;
		if((list = itemSystemSettings) == null)
		{
			throw new IllegalStateException("SystemGUI.setSystemSetting: " +
					"экран настроек системы ещё не создан.");
		}
		if((index += NATIVE_SYSTEM_SETTINGS) < NATIVE_SYSTEM_SETTINGS || index >= list.size())
		{
			throw new IndexOutOfBoundsException("SystemGUI.setSystemSetting: " +
					"параметр index выходит из диапазона.");
		}
		list.setSelectedIndex(index, value);
	}

	public final boolean getSystemSetting(int index)
	{
		Choice list;
		if((list = itemSystemSettings) == null)
		{
			throw new IllegalStateException("SystemGUI.getSystemSetting: " +
					"экран настроек системы ещё не создан.");
		}
		if((index += NATIVE_SYSTEM_SETTINGS) < NATIVE_SYSTEM_SETTINGS || index >= list.size())
		{
			throw new IndexOutOfBoundsException("SystemGUI.getSystemSetting: " +
					"параметр index выходит из диапазона.");
		}
		return list.isSelected(index);
	}

	public final String getMIDletName()
	{
		return midletName;
	}

	protected void prepareForStartMIDlet()
	{
		midletClassNames = null;
		screensOfSettings = null;
		midletName = null;
		commandLaunch = null;
		commandExit = null;
		commandSettings = null;
		commandAboutApplication = null;
		commandAboutImplementation = null;
		commandProperties = null;
		commandDeleteAll = null;
		commandDelete = null;
		commandUpdate = null;
		commandBack = null;
		commandYes = null;
		commandNo = null;
		tickerLaunch = null;
		tickerSettings = null;
		itemFrequency = null;
		itemModelSelect = null;
		itemSystemSettings = null;
		itemRecordName = null;
		itemRecordSize = null;
		itemRecordNumRecords = null;
		itemRecordLastModified = null;
		itemRecordVersion = null;
		itemKeypad = null;
		itemKeyDescription = null;
		itemKeyEquivalent = null;
		itemLibraryList = null;
		screenMIDlets = null;
		screenSettings = null;
		screenSettingControl = null;
		screenSettingKeyCodes = null;
		screenSettingFonts = null;
		screenSettingFrequency = null;
		screenSettingSystem = null;
		screenSettingRecords = null;
		screenSettingKeypad = null;
		screenRecordProperties = null;
		screenRecordDelete = null;
		screenRecordDeleteAll = null;
		screenRecordDeletionError = null;
		screenAboutApplication = null;
		screenAboutImplementation = null;
	}

	private void launchSelectedMIDlet(Choice midletList)
	{
		if(midletStarted)
		{
			return;
		}
		midletStarted = true;
		try
		{
			startMIDlet(Class.forName(midletClassNames[midletList.getSelectedIndex()]));
		}
		catch(Throwable cause)
		{
			cause.printRealStackTrace();
			MIDletProxy.getInstance().getEmulatorScreen().setCurrent(getScreenError(cause));
		}
	}

	private void showSelectedRecordProperties(Choice recordList, Display display)
	{
		Calendar lastModified = Calendar.getInstance();
		Displayable screen = getScreenRecordProperties();
		RecordStore record;
		try
		{
			record = RecordStore.openRecordStore(
					recordList.getString(recordList.getSelectedIndex()), false);
			try
			{
				lastModified.setTime(new Date(record.getLastModified()));
				itemRecordName.setText(record.getName());
				itemRecordSize.setText(Integer.toString(record.getSize()).concat(" байт"));
				itemRecordNumRecords.setText(Integer.toString(record.getNumRecords()));
				itemRecordLastModified.setText(lastModified.toString());
				itemRecordVersion.setText(Integer.toString(record.getVersion()));
			}
			finally
			{
				record.closeRecordStore();
			}
		}
		catch(RecordStoreException e)
		{
			e.printRealStackTrace();
			itemRecordName.setText("(Ошибка возникла при считывании свойств записи.)");
			itemRecordSize.setText(null);
			itemRecordNumRecords.setText(null);
			itemRecordLastModified.setText(null);
			itemRecordVersion.setText(null);
		}
		display.setCurrent(screen);
	}

	private void showSelectedRecordDeleteDialog(Choice recordList, Display display)
	{
		Displayable screen = getScreenRecordDelete();
		screenRecordDelete.setString(
				"Осторожно: удаление одной записи может привести к ошибкам в работе приложения. " +
				"Удаляйте записи только если уверены, что всё делаете правильно. Удалить запись " +
				recordList.getString(recordList.getSelectedIndex()) + "?");
		display.setCurrent(screen);
	}

	private void deleteSelectedRecord(Display display)
	{
		List list;
		try
		{
			RecordStore.deleteRecordStore(
					(list = screenSettingRecords).getString(list.getSelectedIndex()));
			updateRecordList();
			display.setCurrent(list);
		}
		catch(RecordStoreException e)
		{
			e.printRealStackTrace();
			display.setCurrent(getScreenRecordDeletionError());
		}
	}

	private void deleteAllRecords(Display display)
	{
		int i;
		String[] list;
		try
		{
			for(i = (list = RecordStore.listRecordStores()) == null ? 0 : list.length; i-- > 0; )
			{
				RecordStore.deleteRecordStore(list[i]);
			}
			updateRecordList();
			display.setCurrent(screenSettingRecords);
		}
		catch(RecordStoreException e)
		{
			e.printRealStackTrace();
			display.setCurrent(getScreenRecordDeletionError());
		}
	}

	private void createSettingsScreens()
	{
		this.screensOfSettings = new Displayable[] {
				getScreenSettingControl(),
				getScreenSettingKeyCodes(),
				getScreenSettingFonts(),
				getScreenSettingFrequency(),
				getScreenSettingSystem(),
				getScreenSettingRecords(),
				getScreenSettingKeypad()
		};
	}

	private void applySettingsFromScreen(int screenIndex)
	{
		MIDletProxy proxy = MIDletProxy.getInstance();
		switch(screenIndex)
		{
		default:
			onSettingsApply(screensOfSettings[screenIndex]);
			/* fall through */
		case 5:
		case 6:
			return;
		case 0:
			{
				int i;
				int key;
				int[] keys = KEYS;
				int[] deviceKeys = DEVICE_KEYS;
				Form form = screenSettingControl;
				for(i = deviceKeys.length; i-- > 0; )
				{
					if((key = keys[((Choice) form.get(i)).getSelectedIndex()]) == 0)
					{
						continue;
					}
					proxy.setKeyUsedAs(deviceKeys[i], key);
				}
			}
			return;
		case 1:
			{
				int i;
				int keyCode;
				int[] deviceKeys = DEVICE_KEYS;
				Form form = screenSettingKeyCodes;
				String representation;
				for(i = SETTABLE_KEY_CODES; i-- > 0; )
				{
					if((representation = ((Input) form.get(i + 1)).getString()) == null ||
							representation.length() <= 0 ||
							(keyCode = Integer.parseInt(representation)) == 0)
					{
						continue;
					}
					proxy.setKeyCodeFor(deviceKeys[i], keyCode);
				}
			}
			return;
		case 2:
			{
				int i;
				Form form;
				for(i = (form = screenSettingFonts).size(); i-- > 0; )
				{
					proxy.setFont(toFontSize(i >> 2), i & 0x03, RasterFont.getRasterFontAtIndex(
							((Choice) form.get(i)).getSelectedIndex()));
				}
			}
			return;
		case 3:
			proxy.setMaximumFrequency(Math.max(itemFrequency.getValue(), 1));
			return;
		case 4:
			{
				Choice list = itemSystemSettings;
				proxy.setEnableStackTrace(list.isSelected(0));
				proxy.setKeyRepeatedEvent(list.isSelected(1));
				proxy.setSerializeRepaint(list.isSelected(2));
				onSystemSettingsApply();
			}
			return;
		}
	}

	private void updateSettingsScreen(int screenIndex)
	{
		MIDletProxy proxy = MIDletProxy.getInstance();
		switch(screenIndex)
		{
		default:
			onSettingsUpdate(screensOfSettings[screenIndex]);
			/* fall through */
		case 6:
			return;
		case 0:
			{
				int i;
				int[] deviceKeys = DEVICE_KEYS;
				Form form = screenSettingControl;
				for(i = deviceKeys.length; i-- > 0; )
				{
					((Choice) form.get(i)).setSelectedIndex(
							indexOfKey(proxy.getKeyUsedAs(deviceKeys[i])), true);
				}
			}
			return;
		case 1:
			{
				int i;
				int[] deviceKeys = DEVICE_KEYS;
				Form form = screenSettingKeyCodes;
				itemModelSelect.setSelectedIndex(0, true);
				for(i = SETTABLE_KEY_CODES; i-- > 0; )
				{
					((Input) form.get(i + 1)).setString(
							Integer.toString(proxy.getKeyCodeFor(deviceKeys[i])));
				}
			}
			return;
		case 2:
			{
				int i;
				Form form;
				for(i = (form = screenSettingFonts).size(); i-- > 0; )
				{
					((Choice) form.get(i)).setSelectedIndex(
							indexOfFont(proxy.getFont(toFontSize(i >> 2), i & 0x03)), true);
				}
			}
			return;
		case 3:
			itemFrequency.setValue(proxy.getMaximumFrequency());
		case 4:
			{
				Choice list = itemSystemSettings;
				list.setSelectedIndex(0, proxy.isEnableStackTrace());
				list.setSelectedIndex(1, proxy.isKeyRepeatedEvent());
				list.setSelectedIndex(2, proxy.isSerializeRepaint());
				onSystemSettingsUpdate();
			}
			return;
		case 5:
			updateRecordList();
			return;
		}
	}

	private void updateRecordList()
	{
		int i;
		int len;
		int sel;
		String[] records;
		String rec;
		Command cmd1;
		Command cmd2;
		List list = screenSettingRecords;
		len = (records = RecordStore.listRecordStores()) == null ? 0 : records.length;
		sel = list.getSelectedIndex();
		list.deleteAll();
		list.removeCommand(cmd1 = getCommandDelete());
		list.removeCommand(cmd2 = getCommandDeleteAll());
		for(i = 0; i < len; i++)
		{
			list.append((rec = records[i]) == null ? "" : rec, null);
			if(i == 0)
			{
				list.addCommand(cmd1);
				list.addCommand(cmd2);
			}
		}
		if(sel >= 0 && --len >= 0)
		{
			list.setSelectedIndex(Math.min(sel, len), true);
		}
	}

	private Command getCommandLaunch()
	{
		Command result;
		if((result = commandLaunch) == null)
		{
			result = commandLaunch = new Command("Запустить",
					Command.OK, 0);
		}
		return result;
	}

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

	private Command getCommandSettings()
	{
		Command result;
		if((result = commandSettings) == null)
		{
			result = commandSettings = new Command("Настройки",
					Command.SCREEN, 0);
		}
		return result;
	}

	private Command getCommandAboutApplication()
	{
		Command result;
		if((result = commandAboutApplication) == null)
		{
			result = commandAboutApplication = new Command("О приложении",
					Command.SCREEN, 0);
		}
		return result;
	}

	private Command getCommandAboutImplementation()
	{
		Command result;
		if((result = commandAboutImplementation) == null)
		{
			result = commandAboutImplementation = new Command("Об этой реализации J2ME",
					Command.SCREEN, 0);
		}
		return result;
	}

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

	private Command getCommandYes()
	{
		Command result;
		if((result = commandYes) == null)
		{
			result = commandYes = new Command(YES,
					Command.OK, 0);
		}
		return result;
	}

	private Command getCommandNo()
	{
		Command result;
		if((result = commandNo) == null)
		{
			result = commandNo = new Command(NO,
					Command.BACK, 0);
		}
		return result;
	}

	private Command getCommandProperties()
	{
		Command result;
		if((result = commandProperties) == null)
		{
			result = commandProperties = new Command("Свойства",
					Command.OK, 0);
		}
		return result;
	}

	private Command getCommandDeleteAll()
	{
		Command result;
		if((result = commandDeleteAll) == null)
		{
			result = commandDeleteAll = new Command("Удалить все",
					Command.SCREEN, 0);
		}
		return result;
	}

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

	private Command getCommandUpdate()
	{
		Command result;
		if((result = commandUpdate) == null)
		{
			result = commandUpdate = new Command("Обновить",
					Command.SCREEN, 0);
		}
		return result;
	}

	private Ticker getTickerLaunch()
	{
		Ticker result;
		if((result = tickerLaunch) == null)
		{
			result = tickerLaunch = new Ticker(
					"Выберите приложение и нажмите кнопку «Запустить». Если приложение запущено " +
					"впервые (после установки), нажмите кнопку «Меню» и выберите пункт " +
					"«Настройки».");
		}
		return result;
	}

	private Ticker getTickerSettings()
	{
		Ticker result;
		if((result = tickerSettings) == null)
		{
			result = tickerSettings = new Ticker(
					"Это настройки приложения. ВАЖНО: не забудьте сохранить настройки. " +
					"Настройки шрифтов применяются только после перезапуска программы.");
		}
		return result;
	}

	private List getScreenMIDlets()
	{
		Command[] commands;
		List result;
		if((result = screenMIDlets) == null)
		{
			commands = new Command[] {
					getCommandExit(), getCommandSettings(),
					getCommandAboutApplication(), getCommandAboutImplementation()
			};
			result = screenMIDlets = new List(false,
					null, getTickerLaunch(), commands, getCommandLaunch(), this,
					Choice.IMPLICIT, new String[0], null, "(приложений нет)");
		}
		return result;
	}

	private List getScreenSettings()
	{
		String[] elements;
		Command[] commands;
		List result;
		if((result = screenSettings) == null)
		{
			Run.getInstance().showMessage("Загрузка настроек…");
			elements = new String[] {
					"Управление", "Коды клавиш", "Шрифты", "Частота обновления экрана",
					"Система", "Записи", "Показать клавиатуру", "Сохранить настройки"
			};
			commands = new Command[] {
					getCommandBack()
			};
			result = screenSettings = new List(false,
					"Настройки", getTickerSettings(), commands, List.SELECT_COMMAND, this,
					Choice.IMPLICIT, elements, null);
			createSettingsScreens();
			onSettingsScreenShow();
		}
		return result;
	}

	private Displayable getScreenSettingControl()
	{
		int i;
		int w;
		int len;
		int key;
		int[] k;
		Item[] items;
		String[] elements;
		Command[] commands;
		MIDletProxy proxy;
		String keyName;
		Form result;
		if((result = screenSettingControl) == null)
		{
			(elements = new String[i = (k = KEYS).length])[0] = "Свои настройки";
			for(; i-- > 1; elements[i] = MIDletProxy.getSystemKeyName(k[i]));
			items = new Item[len = (k = DEVICE_KEYS).length];
			proxy = MIDletProxy.getInstance();
			for(i = len; i-- > 0; )
			{
				switch(key = k[i])
				{
				case MIDletProxy.DEVICE_KEY_CONSOLE:
					keyName = "Вызов консоли";
					break;
				case MIDletProxy.DEVICE_KEY_EXITAPP:
					keyName = "Выход из приложения";
					break;
				default:
					keyName = proxy.getKeyName(proxy.getKeyCodeFor(key));
					break;
				}
				items[i] = new ChoiceGroup(Item.LAYOUT_NEWLINE_BEFORE, keyName, null, null,
						Choice.POPUP, elements, null);
			}
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND, APPLY_SETTINGS_COMMAND
			};
			result = screenSettingControl = new FormSettings("Управление", null, commands, this,
					"Здесь можно задать соответствие между клавишами виртуального устройства и " +
					"клавишами компьютера.\nВАЖНО: следите, чтобы в разных полях были разные " +
					"названия клавиш компьютера, так как программа этого не проверяет.\n" +
					"Расшифровка непонятных названий клавиш компьютера:\n  SPACE – пробел\n" +
					"  UP – \u2191\n  DOWN – \u2193\n  LEFT – \u2190\n  RIGHT – \u2192\n" +
					"  NUM_PLUS – Num +\n  NUM_MINUS – Num -\n  NUM_STAR – Num *\n" +
					"  NUM_DIVIDE – Num /\n  NUM_DECIMAL – Num .\nОпция «Свои настройки» не " +
					"изменяет соответствующую клавишу при нажатии кнопки «Применить», оставляя " +
					"ту клавишу, которая была задана раннее. Эта опция была специально создана " +
					"на случай, когда файл настроек /midlet.properties редактировался вручную.",
					items, this, null);
			w = result.getWidth();
			for(i = len; i-- > 0; items[i].setPreferredSize(w, -1));
		}
		return result;
	}

	private Displayable getScreenSettingKeyCodes()
	{
		int i;
		int w;
		int len;
		int[] deviceKeys;
		Item[] items;
		String[] elements;
		Command[] commands;
		KeyCodeSet[] sets;
		MIDletProxy proxy;
		Form result;
		if((result = screenSettingKeyCodes) == null)
		{
			deviceKeys = DEVICE_KEYS;
			elements = new String[len = (sets = KEY_CODE_SETS).length];
			for(i = len; i-- > 0; elements[i] = sets[i].getName());
			items = new Item[len = SETTABLE_KEY_CODES + 1];
			proxy = MIDletProxy.getInstance();
			items[0] = itemModelSelect = new ChoiceGroup(
					Item.LAYOUT_NEWLINE_BEFORE, "Предустановки", null, null, Choice.POPUP,
					elements, null);
			for(i = SETTABLE_KEY_CODES; i-- > 0; )
			{
				items[i + 1] = new TextField(Item.LAYOUT_NEWLINE_BEFORE, -1,
						proxy.getKeyName(proxy.getKeyCodeFor(deviceKeys[i])), null, null, null,
						"", 12, Input.NUMERIC);
			}
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND, APPLY_SETTINGS_COMMAND
			};
			result = screenSettingKeyCodes = new FormSettings("Коды клавиш", null, commands, this,
					"Здесь можно задать коды тех клавиш, которые не были предусмотрены " +
					"спецификацией MIDP, в результате чего коды этих клавиш стали зависимыми от " +
					"конкретных моделей устройств. Особенно это касается тех приложений, " +
					"которые работают только на устройствах определённых моделей. Вы можете " +
					"задать особые числовые коды для клавиш или выбрать предустановленные коды " +
					"клавиш для некоторых моделей устройств.",
					items, this, this);
			w = result.getWidth();
			for(i = len; i-- > 0; items[i].setPreferredSize(w, -1));
		}
		return result;
	}

	private Displayable getScreenSettingFonts()
	{
		int i;
		int j;
		int w;
		int len;
		int rcnt;
		Item[] items;
		String[] fontTypes;
		String[] elements;
		Command[] commands;
		ChoiceGroup list;
		Form result;
		if((result = screenSettingFonts) == null)
		{
			fontTypes = new String[] {
					"Мелкий нормальный", "Мелкий жирный",
					"Мелкий курсив", "Мелкий жирный курсив",
					"Средний нормальный", "Средний жирный",
					"Средний курсив", "Средний жирный курсив",
					"Крупный нормальный", "Крупный жирный",
					"Крупный курсив", "Крупный жирный курсив"
			};
			elements = new String[rcnt = RasterFont.getRasterFontsCount()];
			for(i = rcnt; i-- > 0; )
			{
				elements[i] = RasterFont.getRasterFontAtIndex(i).getName();
			}
			items = new Item[len = fontTypes.length];
			for(i = len; i-- > 0; )
			{
				items[i] = list = new ChoiceGroup(Item.LAYOUT_NEWLINE_BEFORE, fontTypes[i],
						null, null, Choice.POPUP, elements, null);
				for(j = rcnt; j-- > 0; )
				{
					list.setFont(j, Font.getRasterFont(j));
				}
			}
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND, APPLY_SETTINGS_COMMAND
			};
			result = screenSettingFonts = new FormSettings("Шрифты", null, commands, this,
					"Здесь можно задать шрифты, которые приложение будет использовать для " +
					"вывода текста.\nНастройки шрифтов вступят в силу только после перезапуска " +
					"программы.",
					items, this, null);
			w = result.getWidth();
			for(i = len; i-- > 0; items[i].setPreferredSize(w, -1));
		}
		return result;
	}

	private Displayable getScreenSettingFrequency()
	{
		Item[] items;
		Command[] commands;
		Form result;
		if((result = screenSettingFrequency) == null)
		{
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND, APPLY_SETTINGS_COMMAND
			};
			items = new Item[] {
					itemFrequency = new Gauge(Item.LAYOUT_DEFAULT, -1, -1, "Максимум кадров в секунду",
							null, null, this, true, 100, 25)
			};
			result = new FormSettings("Частота обновления экрана", null, commands, this,
					"Здесь можно задать максимальную частоту обновления экрана (максимум кадров в секунду). " +
					"Рекомендуемое значение – 25 кадров в секунду. Большие значения (> 50) могут привести к " +
					"«подвисанию» некоторых программ.",
					items, this, this);
		}
		return result;
	}

	private Displayable getScreenSettingSystem()
	{
		Item[] items;
		String[] elements;
		Command[] commands;
		Form result;
		if((result = screenSettingSystem) == null)
		{
			elements = new String[] {
					"Трассировка стека в исключениях\n\n" +
							"Отключите эту опцию, если приложение работает с ошибками и " +
							"генерирует много исключений, увеличивая размер файла /err.txt. " +
							"В противном случае эту опцию рекомендуется включить (для " +
							"отслеживания ошибок в программе).",
					"Вызов метода Canvas.keyRepeated(int)\n\n" +
							"Изменение состояния этого флага может привести к изменению " +
							"поведения приложения в тот момент, когда вы удерживаете нажатой " +
							"какую-нибудь клавишу. Установите или снимите этот флаг в " +
							"соответствии с вашими предпочтениями.",
					"Вызов метода Canvas.paint(Graphics) в порядке общей очереди событий\n\n" +
							"Подавляющее большинство приложений требуют установки этого флага. " +
							"Если, по вашему мнению, приложение не рисует некоторые кадры " +
							"анимации, попробуйте снять этот флаг. Если поведение приложения " +
							"при этом не изменится или приложение будет работать с ошибками, " +
							"снова установите этот флаг."
			};
			items = new Item[] {
					itemSystemSettings = new ChoiceGroup(Item.LAYOUT_DEFAULT, "Поведение системы",
							null, null, Choice.MULTIPLE, elements, null)
			};
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND, APPLY_SETTINGS_COMMAND
			};
			result = screenSettingSystem = new FormSettings("Система", null, commands, this,
					"Здесь можно изменить поведение системы, устанавливая или снимая флаги. " +
					"Выделите опцию, затем нажмите и удерживайте клавишу #, чтобы увидеть " +
					"полное описание выбранной опции.",
					items, this, null);
			items[0].setPreferredSize(result.getWidth(), -1);
			onSystemSettingsScreenShow();
		}
		return result;
	}

	private Displayable getScreenSettingRecords()
	{
		Command[] commands;
		List result;
		if((result = screenSettingRecords) == null)
		{
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND, getCommandUpdate()
			};
			result = screenSettingRecords = new List(false,
					"Записи", null, commands, getCommandProperties(), this,
					Choice.IMPLICIT, new String[0], null, "(записей нет)");
		}
		return result;
	}

	private Displayable getScreenSettingKeypad()
	{
		int devkey;
		int left;
		int top;
		int width;
		int height;
		Item[] items;
		Command[] commands;
		FileInputStream stream;
		DataInputStream data;
		ButtonSet keypad;
		Form result;
		if((result = screenSettingKeypad) == null)
		{
			items = new Item[] {
					itemKeypad = keypad = new ButtonSet(Item.LAYOUT_CENTER |
							Item.LAYOUT_NEWLINE_AFTER, null, loadImage("/ui/keypad.png")),
					itemKeyDescription = new StringItem(Item.LAYOUT_DEFAULT |
							Item.LAYOUT_NEWLINE_AFTER, -1, -1, "Описание", null, null, null,
							"Укажите клавишу на этой виртуальной клавиатуре, чтобы увидеть её " +
							"описание здесь.", null, Item.PLAIN),
					itemKeyEquivalent = new StringItem(Item.LAYOUT_DEFAULT, -1, -1,
							"Эквивалентная клавиша", null, null, null,
							"(клавиша не выбрана)", null, Item.PLAIN)
			};
			try
			{
				(stream = new FileInputStream("/ui/keypad.dat")).checkOpenError();
				try
				{
					for(data = new DataInputStream(stream); data.available() >= 5; )
					{
						devkey = data.readUnsignedByte();
						left = data.readUnsignedByte();
						top = data.readUnsignedByte();
						width = data.readUnsignedByte();
						height = data.readUnsignedByte();
						keypad.addButton(devkey, left, top, width, height);
					}
				}
				finally
				{
					stream.close();
				}
			}
			catch(IOException e)
			{
				e.printRealStackTrace();
			}
			commands = new Command[] {
					BACK_TO_SETTINGS_COMMAND
			};
			result = screenSettingKeypad = new Form("Внешний вид клавиатуры", null, commands, this, items, this);
		}
		return result;
	}

	private Displayable getScreenRecordProperties()
	{
		Item[] items;
		Command[] commands;
		Form result;
		if((result = screenRecordProperties) == null)
		{
			items = new Item[] {
					itemRecordName = new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1,
							"Название", null, null, null, null, null, Item.PLAIN),
					itemRecordSize = new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1,
							"Размер", null, null, null, null, null, Item.PLAIN),
					itemRecordNumRecords = new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1,
							"Количество подзаписей", null, null, null, null, null, Item.PLAIN),
					itemRecordLastModified = new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1,
							"Последнее изменение", null, null, null, null, null, Item.PLAIN),
					itemRecordVersion = new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1,
							"Количество изменений", null, null, null, null, null, Item.PLAIN),
			};
			commands = new Command[] {
					getCommandBack()
			};
			result = screenRecordProperties = new Form("Свойства записи", null, commands, this, items, null);
		}
		return result;
	}

	private Displayable getScreenRecordDelete()
	{
		Command[] commands;
		Alert result;
		if((result = screenRecordDelete) == null)
		{
			commands = new Command[] {
					getCommandNo()
			};
			result = screenRecordDelete = new Alert("Удалить", null, commands, getCommandYes(), this,
					null, null, AlertType.WARNING, null);
		}
		return result;
	}

	private Displayable getScreenRecordDeleteAll()
	{
		Command[] commands;
		Alert result;
		if((result = screenRecordDeleteAll) == null)
		{
			commands = new Command[] {
					getCommandNo()
			};
			result = screenRecordDeleteAll = new Alert("Удалить все", null, commands, getCommandYes(), this,
					"Удаление всех записей приведёт приложение к состоянию, в котором оно " +
					"находилось сразу после установки. Удалить все записи?",
					null, AlertType.CONFIRMATION, null);
		}
		return result;
	}

	private Displayable getScreenRecordDeletionError()
	{
		Alert result;
		if((result = screenRecordDeletionError) == null)
		{
			result = screenRecordDeletionError = new Alert("Ошибка", null, null, Alert.DISMISS_COMMAND, this,
					"Ошибка возникла при удалении записи.", null, AlertType.ERROR, null);
		}
		return result;
	}

	private Displayable getScreenAboutApplication()
	{
		int i;
		int len;
		Item[] items;
		String[] list;
		Command[] commands;
		StringBuffer classNames;
		AttributableMappedTextDecoder descriptor;
		Form result;
		if((result = screenAboutApplication) == null)
		{
			descriptor = MIDletProxy.getInstance().getMIDletDescriptor();
			len = (list = midletClassNames).length - 1;
			classNames = new StringBuffer();
			for(i = 0; i <= len; i++)
			{
				classNames.append(list[i]);
				if(i < len)
				{
					classNames.append('\n');
				}
			}
			items = new Item[] {
					new ImageItem(Item.LAYOUT_NEWLINE_AFTER, null, null, null, null,
							loadMIDletIcon(descriptor), null, Item.PLAIN),
					new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1, null, null, null, null,
							midletName + "\nВерсия: " + descriptor.get("MIDlet-Version") +
							"\nПоставщик: " + descriptor.get("MIDlet-Vendor") +
							"\nПрофиль: " + descriptor.get("MicroEdition-Profile") +
							"\nКонфигурация: " + descriptor.get("MicroEdition-Configuration"),
							Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM),
							Item.PLAIN),
					new StringItem(Item.LAYOUT_DEFAULT, -1, -1, "Класс мидлета", null, null, null,
							classNames.toString(),
							Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL),
							Item.PLAIN)
			};
			commands = new Command[] {
					getCommandBack()
			};
			result = screenAboutApplication = new Form("О приложении", null, commands, this, items, null);
		}
		return result;
	}

	private Displayable getScreenAboutImplementation()
	{
		Item[] items;
		Command[] commands;
		Font font;
		Form result;
		if((result = screenAboutImplementation) == null)
		{
			font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
			items = new Item[] {
					new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1, null, null, null, null,
							"Реализация спецификации Mobile Information Device Profile для " +
							"Малик Эмулятора от Малик Разработчика" +
							"\nВерсия: " + System.getProperty("software.version") +
							"\nПрофиль: " + System.getProperty("microedition.profiles") +
							"\nКонфигурация: " + System.getProperty("microedition.configuration"),
							Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM),
							Item.PLAIN),
					itemLibraryList = new StringItem(Item.LAYOUT_NEWLINE_AFTER, -1, -1,
							"Поддержка интерфейсов программирования приложений", null, null, null,
							"MIDP 2.1 (JSR-120)\nMMAPI 1.2 (JSR-135)\nWMA 1.1 (JSR-118)",
							font, Item.PLAIN),
					new StringItem(Item.LAYOUT_DEFAULT, -1, -1, "Важные классы", null, null, null,
							MIDletProxy.getInstance().getClass().getCanonicalName() + "\n" +
							this.getClass().getCanonicalName(), font, Item.PLAIN),
					new StringItem(Item.LAYOUT_DEFAULT, -1, -1, "Лицензии", null, null, null,
							"GNU LGPL 3\nZLib", font, Item.PLAIN)
			};
			commands = new Command[] {
					getCommandBack()
			};
			result = screenAboutImplementation = new Form("Об этой реализации J2ME", null, commands, this, items, null);
			onAboutImplementationScreenShow();
		}
		return result;
	}

	private Displayable getScreenError(Throwable cause)
	{
		Item[] items;
		Command[] commands;
		String message;
		String text;
		String name;
		Form result;
		name = cause.getClass().getCanonicalName();
		text = (message = cause.getRealMessage()) == null ? name : message + "\n\n– " + name;
		if((result = screenError) == null)
		{
			items = new Item[] {
					new StringItem(Item.LAYOUT_DEFAULT, -1, -1, null, null, null, null, text,
							Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL),
							Item.PLAIN)
			};
			commands = new Command[] {
					getCommandExit()
			};
			result = screenError = new Form("Ошибка приложения", null, commands, this, items, null);
		} else
		{
			((StringItem) result.get(0)).setText(text);
		}
		return result;
	}

	private Displayable getScreenExit()
	{
		Command[] commands;
		Alert result;
		if((result = screenExit) == null)
		{
			commands = new Command[] {
					getCommandNo()
			};
			result = screenExit = new Alert("Выход из приложения", null, commands, getCommandYes(), this,
					"Выйти из приложения " + midletName + "?\nВсе несохранённые данные будут потеряны.",
					null, AlertType.WARNING, null);
		}
		return result;
	}
}
