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

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

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

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

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


package javax.microedition.lcdui;

import java.lang.ref.*;
import malik.emulator.media.graphics.*;

public class ChoiceGroup extends Item
		implements Choice
{
	private static final class Element extends ChoiceElement
	{
		public boolean visible;
		public int top;
		public int bottom;
		public String truncated;

		Element(boolean selected, Font font, String text, Image icon)
		{
			super(selected, font, text, icon);
		}

		public int getWidth()
		{
			return List.CHECKBOX_WIDTH + font.stringWidth(text) + (icon != null ? 36 : 9);
		}

		public int getHeight()
		{
			return Math.max(icon != null ? 24 : 0,
					Math.max(font.getHeight(), List.CHECKBOX_HEIGHT)) + 6;
		}
	}

	private static final class PopupCommandHandler extends WeakReference
			implements CommandListener
	{
		PopupCommandHandler(ChoiceGroup thisItem)
		{
			super(thisItem);
		}

		public void commandAction(Command command, Displayable screen)
		{
			int i;
			int f;
			List list;
			ChoiceGroup thisItem;
			if((thisItem = (ChoiceGroup) get()) == null || screen != (list = thisItem.popupList))
			{
				return;
			}
			if(command == List.SELECT_COMMAND)
			{
				synchronized(thisItem.lock)
				{
					i = thisItem.focusedElementIndex;
					f = thisItem.focusedElementIndex = Math.max(list.getSelectedIndex(), 0);
					thisItem.showSourceScreen();
					if(i != f)
					{
						thisItem.invokeNotifyStateChanged();
					}
				}
				return;
			}
			if(command == BACK)
			{
				synchronized(thisItem.lock)
				{
					thisItem.showSourceScreen();
				}
			}
		}
	}

	private static final Command[] SELECT;
	private static final Command BACK;
	private static final Command POPUP_COMMAND;
	private static final String POPUP_LABEL = "Выбрать…";

	static
	{
		SELECT = new Command[] {
				new Command("Отметить все", Command.ITEM, 0),
				new Command("Снять все", Command.ITEM, 0),
				new Command("Переключить все", Command.ITEM, 0)
		};
		BACK = new Command("Назад", Command.BACK, 0);
		POPUP_COMMAND = new Command(POPUP_LABEL, Command.OK, 0);
	}


	private boolean controlFocused;
	private int type;
	private int fitPolicy;
	private int contentWidth;
	private int contentHeight;
	private int checkboxFocused;
	private int checkboxPressed;
	private int checkedElementIndex;
	private int focusedElementIndex;
	private int elementsCount;
	private Element[] elements;
	private Command selectCommand;
	private List popupList;
	private Displayable sourceScreen;
	private Object lock;

	public ChoiceGroup(String label, int type)
	{
		this(LAYOUT_DEFAULT, label, null, null, type, new String[0], null);
	}

	public ChoiceGroup(String label, int type, String[] elements, Image[] icons)
	{
		this(LAYOUT_DEFAULT, label, null, null, type, elements, icons);
	}

	public ChoiceGroup(int layout, String label,
			Command[] commands, ItemCommandListener listener,
			int type, String[] elements, Image[] icons)
	{
		super(layout, -1, -1, label,
				type == MULTIPLE ? Displayable.concatCommands(SELECT, commands) : commands,
				elements == null || elements.length == 0 ? null :
				type == POPUP ? POPUP_COMMAND : List.CHECK_COMMAND, listener);
		int i;
		int len;
		String[] lines;
		Element[] list;
		Command[] popupCommands;
		Font font;
		if(type != EXCLUSIVE && type != MULTIPLE && type != POPUP)
		{
			throw new IllegalArgumentException("ChoiceGroup: " +
					"недопустимое значение параметра type.");
		}
		if(elements == null)
		{
			throw new NullPointerException("ChoiceGroup: " +
					"параметр elements равен нулевой ссылке.");
		}
		Array.copy(elements, 0, lines = new String[len = elements.length], 0, len);
		for(i = len; i-- > 0; )
		{
			if(lines[i] == null)
			{
				throw new NullPointerException("ChoiceGroup: " +
						"параметр elements содержит нулевые ссылки.");
			}
		}
		if(icons != null && icons.length != len)
		{
			throw new IllegalArgumentException("ChoiceGroup: " +
					"параметр icons имеет длину, отличную от длины параметра elements.");
		}
		font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
		for(list = new Element[i = len]; i-- > 0; )
		{
			list[i] = new Element(type == EXCLUSIVE && i == 0, font,
					lines[i], icons == null ? null : icons[i]);
		}
		popupCommands = new Command[] {
				BACK
		};
		this.type = type;
		this.fitPolicy = TEXT_WRAP_DEFAULT;
		this.checkboxFocused = -1;
		this.checkboxPressed = -1;
		this.checkedElementIndex = 0;
		this.focusedElementIndex = 0;
		this.elementsCount = len;
		this.elements = list;
		this.selectCommand = type != POPUP ? List.CHECK_COMMAND : POPUP_COMMAND;
		this.popupList = type != POPUP ? null : new List(false, POPUP_LABEL, null,
				popupCommands, List.SELECT_COMMAND, new PopupCommandHandler(this),
				IMPLICIT, elements, icons);
		this.lock = new Object();
	}

	public void setFitPolicy(int policy)
	{
		if(policy != TEXT_WRAP_DEFAULT && policy != TEXT_WRAP_ON && policy != TEXT_WRAP_OFF)
		{
			throw new IllegalArgumentException("ChoiceGroup.setFitPolicy: " +
					"недопустимое значение параметра policy.");
		}
		this.fitPolicy = policy;
	}

	public void setSelectedIndex(int elementIndex, boolean selected)
	{
		int error;
		int checked;
		Element[] list;
		Element elem;
		Element prev;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					break label0;
				}
				switch(type)
				{
				case EXCLUSIVE:
					if(elementIndex != (checked = checkedElementIndex))
					{
						checkedElementIndex = elementIndex;
						(elem = (list = elements)[elementIndex]).selected = true;
						(prev = list[checked]).selected = false;
						if(elem.visible || prev.visible)
						{
							notifyPaint();
						}
					}
					break;
				case MULTIPLE:
					if((elem = elements[elementIndex]).selected != selected)
					{
						elem.selected = selected;
						if(elem.visible)
						{
							notifyPaint();
						}
					}
					break;
				case POPUP:
					if(elementIndex != focusedElementIndex)
					{
						focusedElementIndex = elementIndex;
						notifyPaint();
					}
					break;
				}
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.setSelectedIndex: " +
					"параметр elementIndex выходит из диапазона.");
		}
	}

	public void setSelectedFlags(boolean[] selected)
	{
		boolean sel;
		boolean needRepaint;
		int i;
		int len;
		int error;
		int checked;
		int elementIndex;
		Element[] list;
		Element elem;
		Element prev;
		if(selected == null)
		{
			throw new NullPointerException("ChoiceGroup.setSelectedFlags: " +
					"параметр selected равен нулевой ссылке.");
		}
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(selected.length < (len = elementsCount))
				{
					error = 1;
					break label0;
				}
				switch(type)
				{
				case EXCLUSIVE:
					elementIndex = -1;
					for(i = 0; i < len; i++)
					{
						if(selected[i] == false)
						{
							continue;
						}
						elementIndex = i;
						break;
					}
					if(elementIndex < 0)
					{
						break;
					}
					if(elementIndex != (checked = checkedElementIndex))
					{
						checkedElementIndex = elementIndex;
						(elem = (list = elements)[elementIndex]).selected = true;
						(prev = list[checked]).selected = false;
						if(elem.visible || prev.visible)
						{
							notifyPaint();
						}
					}
					break;
				case MULTIPLE:
					needRepaint = false;
					list = elements;
					for(i = len; i-- > 0; )
					{
						if((elem = list[i]).selected == (sel = selected[i]))
						{
							continue;
						}
						elem.selected = sel;
						if(elem.visible)
						{
							needRepaint = true;
						}
					}
					if(needRepaint)
					{
						notifyPaint();
					}
					break;
				case POPUP:
					elementIndex = -1;
					for(i = 0; i < len; i++)
					{
						if(selected[i] == false)
						{
							continue;
						}
						elementIndex = i;
						break;
					}
					if(elementIndex < 0)
					{
						break;
					}
					if(elementIndex != focusedElementIndex)
					{
						focusedElementIndex = elementIndex;
						notifyPaint();
					}
					break;
				}
			}
		}
		if(error == 1)
		{
			throw new IllegalArgumentException("ChoiceGroup.setSelectedFlags: " +
					"длина параметра selected меньше количества элементов списка.");
		}
	}

	public void setFont(int elementIndex, Font font)
	{
		int error;
		Font f;
		Element elem;
		f = font != null ? font :
				Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					break label0;
				}
				if(f != (elem = elements[elementIndex]).font)
				{
					elem.font = f;
					correctTruncated(elem);
					if(type == POPUP)
					{
						popupList.setFont(elementIndex, font);
					}
					notifyUpdate();
				}
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.setFont: " +
					"параметр elementIndex выходит из диапазона.");
		}
	}

	public void set(int elementIndex, String text, Image icon)
	{
		int error;
		Element elem;
		if(text == null)
		{
			throw new NullPointerException("ChoiceGroup.set: " +
					"параметр text равен нулевой ссылке.");
		}
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					break label0;
				}
				(elem = elements[elementIndex]).text = text;
				elem.icon = icon;
				correctTruncated(elem);
				if(type == POPUP)
				{
					popupList.set(elementIndex, text, icon);
				}
				notifyUpdate();
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.set: " +
					"параметр elementIndex выходит из диапазона.");
		}
	}

	public void insert(int elementIndex, String text, Image icon)
	{
		int t;
		int i;
		int len;
		int error;
		Element[] list;
		if(text == null)
		{
			throw new NullPointerException("ChoiceGroup.insert: " +
					"параметр text равен нулевой ссылке.");
		}
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex > (len = elementsCount))
				{
					error = 1;
					break label0;
				}
				if(len == (list = elements).length)
				{
					Array.copy(list, 0, list = elements = new Element[(len << 1) + 1], 0, len);
				}
				if((i = len - elementIndex) > 0)
				{
					Array.copy(list, elementIndex, list, elementIndex + 1, i);
				}
				list[elementIndex] = new Element((t = type) == EXCLUSIVE && len == 0,
						Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL),
						text, icon);
				elementsCount = ++len;
				checkboxPressed = -1;
				checkboxFocused = -1;
				if(len > 1)
				{
					if((i = checkedElementIndex) >= elementIndex)
					{
						checkedElementIndex = i + 1;
					}
					if((i = focusedElementIndex) >= elementIndex)
					{
						focusedElementIndex = i + 1;
					}
				} else
				{
					super.setDefaultCommand(selectCommand);
				}
				if(t == POPUP)
				{
					popupList.insert(elementIndex, text, icon);
				}
				notifyUpdate();
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.insert: " +
					"параметр elementIndex выходит из диапазона.");
		}
	}

	public void delete(int elementIndex)
	{
		int t;
		int i;
		int len;
		int error;
		Element[] list;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= (len = elementsCount))
				{
					error = 1;
					break label0;
				}
				list = elements;
				if((i = len - elementIndex - 1) > 0)
				{
					Array.copy(list, elementIndex + 1, list, elementIndex, i);
				}
				list[--len] = null;
				elementsCount = len;
				checkboxPressed = -1;
				checkboxFocused = -1;
				t = type;
				if(len > 0)
				{
					label1:
					{
						if((i = checkedElementIndex) > elementIndex)
						{
							checkedElementIndex = i - 1;
							break label1;
						}
						if(i == elementIndex)
						{
							checkedElementIndex = i = i == len ? i - 1 : i;
							if(t == EXCLUSIVE)
							{
								list[i].selected = true;
							}
						}
					}
					if((i = focusedElementIndex) == elementIndex && i == len || i > elementIndex)
					{
						focusedElementIndex = i - 1;
					}
				} else
				{
					super.removeCommand(selectCommand);
				}
				if(t == POPUP)
				{
					popupList.delete(elementIndex);
				}
				notifyUpdate();
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.delete: " +
					"параметр elementIndex выходит из диапазона.");
		}
	}

	public void deleteAll()
	{
		int i;
		int len;
		Element[] list;
		synchronized(lock)
		{
			if((len = elementsCount) > 0)
			{
				for(list = elements, i = len; i-- > 0; list[i] = null);
				elementsCount = 0;
				checkboxPressed = -1;
				checkboxFocused = -1;
				checkedElementIndex = 0;
				focusedElementIndex = 0;
				super.removeCommand(selectCommand);
				if(type == POPUP)
				{
					popupList.deleteAll();
				}
				notifyUpdate();
			}
		}
	}

	public boolean isSelected(int elementIndex)
	{
		boolean result = false;
		int error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					break label0;
				}
				switch(type)
				{
				case EXCLUSIVE:
					result = checkedElementIndex == elementIndex;
					break;
				case MULTIPLE:
					result = elements[elementIndex].selected;
					break;
				case POPUP:
					result = focusedElementIndex == elementIndex;
					break;
				}
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.isSelected: " +
					"параметр elementIndex выходит из диапазона.");
		}
		return result;
	}

	public int append(String text, Image icon)
	{
		int t;
		int len;
		int result;
		Element[] list;
		if(text == null)
		{
			throw new NullPointerException("ChoiceGroup.append: " +
					"параметр text равен нулевой ссылке.");
		}
		synchronized(lock)
		{
			if((len = elementsCount) == (list = elements).length)
			{
				Array.copy(list, 0, list = elements = new Element[(len << 1) + 1], 0, len);
			}
			list[result = len] = new Element((t = type) == EXCLUSIVE && len == 0,
					Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL),
					text, icon);
			elementsCount = ++len;
			checkboxPressed = -1;
			checkboxFocused = -1;
			if(len == 1)
			{
				super.setDefaultCommand(selectCommand);
			}
			if(t == POPUP)
			{
				popupList.append(text, icon);
			}
			notifyUpdate();
		}
		return result;
	}

	public int size()
	{
		return elementsCount;
	}

	public int getFitPolicy()
	{
		return fitPolicy;
	}

	public int getSelectedIndex()
	{
		if(elementsCount == 0)
		{
			return -1;
		}
		switch(type)
		{
		default:
			return -1;
		case EXCLUSIVE:
			return checkedElementIndex;
		case POPUP:
			return focusedElementIndex;
		}
	}

	public int getSelectedFlags(boolean[] selected)
	{
		int f;
		int i;
		int len;
		int error;
		int result;
		Element[] list;
		if(selected == null)
		{
			throw new NullPointerException("ChoiceGroup.getSelectedFlags: " +
					"параметр selected равен нулевой ссылке.");
		}
		result = 0;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(selected.length < (len = elementsCount))
				{
					error = 1;
					break label0;
				}
				switch(type)
				{
				case EXCLUSIVE:
					result = len > 0 ? 1 : 0;
					f = checkedElementIndex;
					for(i = len; i-- > 0; selected[i] = i == f);
					break;
				case MULTIPLE:
					result = 0;
					list = elements;
					for(i = len; i-- > 0; )
					{
						if(selected[i] = list[i].selected)
						{
							result++;
						}
					}
					break;
				case POPUP:
					result = len > 0 ? 1 : 0;
					f = focusedElementIndex;
					for(i = len; i-- > 0; selected[i] = i == f);
					break;
				}
			}
		}
		if(error == 1)
		{
			throw new IllegalArgumentException("ChoiceGroup.getSelectedFlags: " +
					"длина параметра selected меньше количества элементов списка.");
		}
		return result;
	}

	public Font getFont(int elementIndex)
	{
		int error;
		Font result;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					result = null;
					break label0;
				}
				result = elements[elementIndex].font;
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.getFont: " +
					"параметр elementIndex выходит из диапазона.");
		}
		return result;
	}

	public Image getImage(int elementIndex)
	{
		int error;
		Image result;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					result = null;
					break label0;
				}
				result = elements[elementIndex].icon;
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.getImage: " +
					"параметр elementIndex выходит из диапазона.");
		}
		return result;
	}

	public String getString(int elementIndex)
	{
		int error;
		String result;
		error = 0;
		synchronized(lock)
		{
			label0:
			{
				if(elementIndex < 0 || elementIndex >= elementsCount)
				{
					error = 1;
					result = null;
					break label0;
				}
				result = elements[elementIndex].text;
			}
		}
		if(error == 1)
		{
			throw new IndexOutOfBoundsException("ChoiceGroup.getString: " +
					"параметр elementIndex выходит из диапазона.");
		}
		return result;
	}

	void paintContent(Graphics render, int contentWidth, int contentHeight)
	{
		boolean cfocused;
		int s;
		int x;
		int y;
		int t;
		int i;
		int len;
		int top;
		int bottom;
		int pressed;
		int focused;
		int elementHeight;
		int leftMarginCheckbox;
		int leftMarginIcon;
		int leftMarginText;
		int topMarginCheckbox;
		int topMarginElement;
		Element[] list;
		Element elem;
		Image icon;
		Font font;
		synchronized(lock)
		{
			cfocused = controlFocused;
			pressed = checkboxPressed;
			focused = focusedElementIndex;
			len = elementsCount;
			list = elements;
			if((t = type) == POPUP)
			{
				if(len > 0)
				{
					render.setColor(RasterCanvas.getSystemColor(
							(i = cfocused ? (pressed >= 0 ? 1 : 3) : 0) + 0x24));
					render.drawElementOfGUI(4, i, 0, 0, 0, contentWidth, contentHeight);
					i = i == 1 ? 1 : 0;
					if((icon = (elem = list[focused]).icon) == null)
					{
						leftMarginText = 3;
					} else
					{
						leftMarginText = 30;
						render.drawStretchRegion(icon, 0, 0, icon.getWidth(), icon.getHeight(), 0,
								i + 3, i + ((contentHeight - 24) >> 1), 24, 24, 0);
					}
					render.setFont(font = elem.font);
					render.drawString(elem.truncated, i + leftMarginText,
							i + ((contentHeight - font.getHeight()) >> 1), 0);
					s = List.CHECKBOX_WIDTH > 7 ? 7 : ((List.CHECKBOX_WIDTH - 1) & 1) + 1;
					x = i + contentWidth - s - 3;
					y = i + ((contentHeight - s) >> 1) + 2;
					for(i = s; i-- > 0; y++)
					{
						render.drawLine(x, y, x + s - 1, y);
						if((i & 1) == 0)
						{
							x++;
							s -= 2;
						}
					}
				}
			} else
			{
				bottom = (top = render.getClipY()) + render.getClipHeight();
				leftMarginText = (leftMarginIcon = (leftMarginCheckbox = 3) +
						List.CHECKBOX_WIDTH + 3) + 27;
				topMarginElement = 0;
				for(i = 0; i < len; render.translate(0, elementHeight),
						topMarginElement += elementHeight, i++)
				{
					elementHeight = (elem = list[i]).getHeight();
					elem.top = topMarginElement;
					elem.bottom = topMarginElement + elementHeight;
					if(!(elem.visible = topMarginElement < bottom &&
							topMarginElement + elementHeight > top))
					{
						continue;
					}
					if(cfocused && i == focused)
					{
						render.drawElementOfGUI(1, 0, 0, 0, 0, contentWidth, elementHeight);
					}
					topMarginCheckbox = (elementHeight - List.CHECKBOX_HEIGHT) >> 1;
					switch(t)
					{
					case EXCLUSIVE:
						render.drawElementOfGUI(12,
								elem.selected ? 1 : 0, i == pressed ? 1 : 0,
								leftMarginCheckbox, topMarginCheckbox,
								List.CHECKBOX_WIDTH, List.CHECKBOX_HEIGHT);
						break;
					case MULTIPLE:
						render.drawElementOfGUI(15,
								elem.selected ? 1 : 0, i == pressed ? 1 : 0,
								leftMarginCheckbox, topMarginCheckbox,
								List.CHECKBOX_WIDTH, List.CHECKBOX_HEIGHT);
						break;
					}
					if((icon = elem.icon) != null)
					{
						render.drawStretchRegion(icon, 0, 0, icon.getWidth(), icon.getHeight(), 0,
								leftMarginIcon, (elementHeight - 24) >> 1, 24, 24, 0);
					}
					render.setFont(font = elem.font);
					render.drawString(elem.truncated,
							icon != null ? leftMarginText : leftMarginIcon,
							(elementHeight - font.getHeight()) >> 1, 0);
				}
			}
		}
	}

	void onSizeChanged(int contentWidth, int contentHeight)
	{
		int i;
		Element[] list;
		this.contentWidth = contentWidth;
		this.contentHeight = contentHeight;
		synchronized(lock)
		{
			for(list = elements, i = elementsCount; i-- > 0; correctTruncated(list[i]));
		}
	}

	void onKeyPressed(int key, int charCode)
	{
		int i;
		MIDletProxy proxy;
		if(key == (proxy = MIDletProxy.getInstance()).getKeyUsedAs(MIDletProxy.DEVICE_KEY_POUND))
		{
			synchronized(lock)
			{
				if((i = focusedElementIndex) >= 0 && i < elementsCount)
				{
					ChoiceElementDetailsScreen.show(proxy.getEmulatorScreen(),
							super.getLabel(), elements[i], getOwner());
				}
			}
		}
	}

	void onContentPointerPressed(int x, int y, int button)
	{
		int i;
		synchronized(lock)
		{
			if(button == MIDletProxy.BUTTON_MAIN && (i = getElementIndex(x, y)) >= 0)
			{
				if(type == POPUP || x < List.CHECKBOX_WIDTH + 6)
				{
					checkboxFocused = i;
					checkboxPressed = i;
				}
				focusedElementIndex = i;
				notifyPaint();
			}
		}
	}

	void onContentPointerDragged(int x, int y)
	{
		int i;
		int f;
		synchronized(lock)
		{
			if((f = checkboxFocused) >= 0)
			{
				if(checkboxPressed != (checkboxPressed = f == (type == POPUP ||
						x < List.CHECKBOX_WIDTH + 6 ? getElementIndex(x, y) : -1) ? f : -1))
				{
					notifyPaint();
				}
			}
			else if((i = getElementIndex(x, y)) >= 0)
			{
				focusedElementIndex = i;
				notifyPaint();
			}
		}
	}

	void onContentPointerReleased(int x, int y, int button)
	{
		int i;
		int f;
		Element[] list;
		Element elem;
		synchronized(lock)
		{
			if(button == MIDletProxy.BUTTON_MAIN)
			{
				f = checkboxPressed;
				checkboxFocused = -1;
				checkboxPressed = -1;
				if(f >= 0 && f < elementsCount)
				{
					switch(type)
					{
					case EXCLUSIVE:
						(list = elements)[i = checkedElementIndex].selected = false;
						list[checkedElementIndex = f].selected = true;
						notifyPaint();
						if(i != f)
						{
							super.notifyStateChanged();
						}
						break;
					case MULTIPLE:
						(elem = elements[f]).selected = !elem.selected;
						notifyPaint();
						super.notifyStateChanged();
						break;
					case POPUP:
						showPopupList();
						break;
					}
				}
			}
		}
	}

	void onCommandAction(Command command)
	{
		boolean need;
		int i;
		int f;
		Element[] list;
		Element elem;
		if(command == SELECT[0])
		{
			synchronized(lock)
			{
				need = false;
				for(list = elements, i = elementsCount; i-- > 0; )
				{
					if(!(elem = list[i]).selected)
					{
						need = true;
						elem.selected = true;
					}
				}
				if(need)
				{
					notifyPaint();
					super.notifyStateChanged();
				}
			}
			return;
		}
		if(command == SELECT[1])
		{
			synchronized(lock)
			{
				need = false;
				for(list = elements, i = elementsCount; i-- > 0; )
				{
					if((elem = list[i]).selected)
					{
						need = true;
						elem.selected = false;
					}
				}
				if(need)
				{
					notifyPaint();
					super.notifyStateChanged();
				}
			}
			return;
		}
		if(command == SELECT[2])
		{
			synchronized(lock)
			{
				need = false;
				for(list = elements, i = elementsCount; i-- > 0; )
				{
					need = true;
					(elem = list[i]).selected = !elem.selected;
				}
				if(need)
				{
					notifyPaint();
					super.notifyStateChanged();
				}
			}
			return;
		}
		if(command == POPUP_COMMAND)
		{
			synchronized(lock)
			{
				showPopupList();
			}
			return;
		}
		if(command == List.CHECK_COMMAND)
		{
			synchronized(lock)
			{
				if((f = focusedElementIndex) >= 0 && f < elementsCount)
				{
					switch(type)
					{
					case EXCLUSIVE:
						(list = elements)[i = checkedElementIndex].selected = false;
						list[checkedElementIndex = f].selected = true;
						notifyPaint();
						if(i != f)
						{
							super.notifyStateChanged();
						}
						break;
					case MULTIPLE:
						(elem = elements[f]).selected = !elem.selected;
						notifyPaint();
						super.notifyStateChanged();
						break;
					}
				}
			}
			return;
		}
		super.onCommandAction(command);
	}

	void onTraverseOut()
	{
		controlFocused = false;
		notifyPaint();
	}

	boolean onTraverseNeedStayOn(int direction, int viewportWidth, int viewportHeight,
			int[] visibleRectangle)
	{
		boolean result;
		boolean cfocused;
		int labelHeight;
		int focused;
		int tmp;
		int elt;
		int elh;
		int vrt;
		int vrh;
		Element elem;
		if((cfocused = controlFocused) != (controlFocused = true))
		{
			notifyPaint();
		}
		if(checkboxFocused >= 0)
		{
			return true;
		}
		switch(direction)
		{
		case Canvas.LEFT:
		case Canvas.RIGHT:
			if(type == POPUP)
			{
				return !cfocused;
			}
			labelHeight = super.getPreferredHeight() - contentHeight;
			if(!cfocused)
			{
				synchronized(lock)
				{
					focused = focusedElementIndex;
					if((vrh = visibleRectangle[HEIGHT]) < (elh =
							(elem = elements[focused]).getHeight()))
					{
						visibleRectangle[HEIGHT] = vrh = elh;
					}
					if((vrt = visibleRectangle[TOP]) > (tmp = labelHeight + (elt = elem.top)))
					{
						visibleRectangle[TOP] = tmp;
					}
					else if(vrt < (tmp = labelHeight + elt + elh - vrh))
					{
						visibleRectangle[TOP] = tmp;
					}
					notifyPaint();
				}
				return true;
			}
			return false;
		case Canvas.UP:
			if(type == POPUP)
			{
				return !cfocused;
			}
			labelHeight = super.getPreferredHeight() - contentHeight;
			synchronized(lock)
			{
				if(result = cfocused ? (focused = focusedElementIndex) > 0 :
						(focused = elementsCount) > 0)
				{
					focusedElementIndex = --focused;
					if((vrt = visibleRectangle[TOP]) > (tmp =
							labelHeight + (elem = elements[focused]).top))
					{
						visibleRectangle[TOP] = tmp;
					}
					else if(vrt < (tmp =
							labelHeight + elem.bottom - visibleRectangle[HEIGHT]))
					{
						visibleRectangle[TOP] = tmp;
					}
					notifyPaint();
				}
			}
			return result;
		case Canvas.DOWN:
			if(type == POPUP)
			{
				return !cfocused;
			}
			labelHeight = super.getPreferredHeight() - contentHeight;
			synchronized(lock)
			{
				if(result = cfocused ? (focused = focusedElementIndex) < elementsCount - 1 :
						elementsCount > (focused = -1) + 1)
				{
					focusedElementIndex = ++focused;
					if((vrt = visibleRectangle[TOP]) > (tmp =
							labelHeight + (elem = elements[focused]).top))
					{
						visibleRectangle[TOP] = tmp;
					}
					else if(vrt < (tmp =
							labelHeight + elem.bottom - visibleRectangle[HEIGHT]))
					{
						visibleRectangle[TOP] = tmp;
					}
					notifyPaint();
				}
			}
			return result;
		}
		return true;
	}

	boolean keyHandling(int key)
	{
		return key == MIDletProxy.getInstance().getKeyUsedAs(MIDletProxy.DEVICE_KEY_POUND);
	}

	int getPreferredContentWidth(int contentHeight, int containerClientWidth)
	{
		int i;
		int tmp;
		int result = 0;
		Element[] list;
		synchronized(lock)
		{
			for(list = elements, i = elementsCount; i-- > 0; )
			{
				if(result < (tmp = list[i].getWidth()))
				{
					result = tmp;
				}
			}
		}
		return result < containerClientWidth ? result : containerClientWidth;
	}

	int getPreferredContentHeight(int contentWidth)
	{
		int i;
		int tmp;
		int result = 0;
		Element[] list;
		synchronized(lock)
		{
			if(type == POPUP)
			{
				for(list = elements, i = elementsCount; i-- > 0; )
				{
					if(result < (tmp = list[i].getHeight()))
					{
						result = tmp;
					}
				}
			} else
			{
				for(list = elements, i = elementsCount; i-- > 0; )
				{
					result += list[i].getHeight();
				}
			}
		}
		return result;
	}

	private void showPopupList()
	{
		int i;
		List screen;
		if((screen = popupList) != null && (sourceScreen = getOwner()) != null)
		{
			if((i = focusedElementIndex) >= 0 && i < screen.size())
			{
				screen.setSelectedIndex(i, true);
			}
			MIDletProxy.getInstance().getEmulatorScreen().setCurrent(screen);
		}
	}

	private void showSourceScreen()
	{
		Displayable next;
		Display display = MIDletProxy.getInstance().getEmulatorScreen();
		if((next = sourceScreen).isSystem())
		{
			display.setCurrent(next);
		} else
		{
			display.hideSystemScreen();
		}
		sourceScreen = null;
	}

	private void invokeNotifyStateChanged()
	{
		super.notifyStateChanged();
	}

	private void correctTruncated(Element elem)
	{
		int t;
		elem.truncated = Displayable.truncate(elem.text, elem.font, contentWidth -
				((t = type) != POPUP ? List.CHECKBOX_WIDTH + 3 : 0) -
				(elem.icon != null ? 27 : 0) - (t == POPUP ? 16 : 6));
	}

	private int getElementIndex(int x, int y)
	{
		int a;
		int b;
		int c;
		Element[] list;
		Element elem;
		if(x < 0 || x >= contentWidth || y < 0 || y >= contentHeight)
		{
			return -1;
		}
		if(type == POPUP)
		{
			return focusedElementIndex;
		}
		a = 0;
		b = elementsCount - 1;
		list = elements;
		while(a <= b)
		{
			elem = list[c = (a + b) >> 1];
			if(y < elem.top)
			{
				b = c - 1;
				continue;
			}
			if(y >= elem.bottom)
			{
				a = c + 1;
				continue;
			}
			return c;
		}
		return -1;
	}
}
