/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016–2017, 2019–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.microedition.lcdui;
import javax.microedition.lcdui.*;
import malik.emulator.application.*;
import malik.emulator.media.graphics.*;
import malik.emulator.microedition.*;
import malik.emulator.util.*;
public class ChoiceList extends CustomSurfaceScreen implements TextImageMenu
{
private static final int ICON_WIDTH = 32;
private static final int ICON_HEIGHT = 32;
private static final int MARGIN = 3;
private static final int ELEMENT_HEIGHT = ICON_HEIGHT + MARGIN * 2;
private static final int INITIAL_SCROLL = 32;
public static int getBestImageWidth() {
return ICON_WIDTH;
}
public static int getBestImageHeight() {
return ICON_HEIGHT;
}
private static final class Element extends Object
{
public String text;
public Image icon;
public Font font;
public Element(String text, Image icon, Font font) {
this.text = text;
this.icon = icon;
this.font = font;
}
}
private int selected;
private int scroll;
private int count;
private Element[] elements;
private final Object monitor;
public ChoiceList(String title, ScrollBarStyle style) {
this(title, style, new String[0], null);
}
public ChoiceList(String title, ScrollBarStyle style, String[] elements) {
this(title, style, elements, null);
}
public ChoiceList(String title, ScrollBarStyle style, String[] elements, Image[] icons) {
super(title, elements == null ? 0 : ELEMENT_HEIGHT * elements.length, true, style);
int count;
String[] copyElements;
Element[] choiceElements;
if(elements == null)
{
throw new NullPointerException("ChoiceList: аргумент elements равен нулевой ссылке.");
}
Array.copy(elements, 0, copyElements = new String[count = elements.length], 0, count);
if(Array.findf(copyElements, 0, null) < count)
{
throw new NullPointerException("ChoiceList: аргумент elements содержит элементы, равные нулевой ссылке.");
}
if(icons != null && icons.length != count)
{
throw new IllegalArgumentException("ChoiceList: длина аргумента icons отличается от длины аргумента elements.");
}
choiceElements = new Element[count <= 0x0f ? 0x0f : StringBuilder.optimalCapacity(count)];
for(int i = count; i-- > 0; ) choiceElements[i] = new Element(copyElements[i], icons == null ? null : icons[i], null);
this.scroll = INITIAL_SCROLL;
this.count = count;
this.elements = choiceElements;
this.monitor = new Object();
}
public void insert(int elementIndex, String text) {
if(text == null)
{
throw new NullPointerException("ChoiceList.insert: аргумент text равен нулевой ссылке.");
}
insert(elementIndex, new Element(text, null, null));
}
public void insert(int elementIndex, String text, Image icon) {
if(text == null)
{
throw new NullPointerException("ChoiceList.insert: аргумент text равен нулевой ссылке.");
}
insert(elementIndex, new Element(text, icon, null));
}
public void insert(int elementIndex, String text, Image icon, Font font) {
if(text == null)
{
throw new NullPointerException("ChoiceList.insert: аргумент text равен нулевой ссылке.");
}
insert(elementIndex, new Element(text, icon, font));
}
public void delete(int elementIndex) {
int error = 0;
synchronized(monitor)
{
label0:
{
int sel;
int len;
Element[] list;
if(elementIndex < 0 || elementIndex > (len = count - 1))
{
error = 1;
break label0;
}
list = elements;
if(elementIndex < len) Array.copy(list, elementIndex + 1, list, elementIndex, len - elementIndex);
list[count = len] = null;
sel = selected;
if(len > 0)
{
if(sel == elementIndex)
{
if(sel >= len) sel = len - 1;
selected = sel;
scroll = INITIAL_SCROLL;
}
else if(sel > elementIndex)
{
selected = --sel;
}
} else
{
scroll = INITIAL_SCROLL;
}
super.getScrollBar().setRange(ELEMENT_HEIGHT * len);
correctScrollBarPosition(sel);
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.delete: аргумент elementIndex выходит из диапазона.");
}
}
public void deleteAll() {
synchronized(monitor)
{
Array.fill(elements, 0, count, null);
selected = 0;
scroll = INITIAL_SCROLL;
count = 0;
super.getScrollBar().setRange(0);
}
}
public void setSelectedIndex(int elementIndex) {
synchronized(monitor)
{
int lim;
if(elementIndex > (lim = count - 1)) elementIndex = lim;
if(elementIndex < 0) elementIndex = 0;
correctScrollBarPosition(selected = elementIndex);
scroll = INITIAL_SCROLL;
}
}
public void setString(int elementIndex, String text) {
int error;
if(text == null)
{
throw new NullPointerException("ChoiceList.setString: аргумент text равен нулевой ссылке.");
}
error = 0;
synchronized(monitor)
{
label0:
{
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
break label0;
}
elements[elementIndex].text = text;
repaint();
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.setString: аргумент elementIndex выходит из диапазона.");
}
}
public void setString(int elementIndex, String text, Image icon) {
int error;
if(text == null)
{
throw new NullPointerException("ChoiceList.setString: аргумент text равен нулевой ссылке.");
}
error = 0;
synchronized(monitor)
{
label0:
{
Element element;
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
break label0;
}
(element = elements[elementIndex]).text = text;
element.icon = icon;
repaint();
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.setString: аргумент elementIndex выходит из диапазона.");
}
}
public void setString(int elementIndex, String text, Image icon, Font font) {
int error;
if(text == null)
{
throw new NullPointerException("ChoiceList.setString: аргумент text равен нулевой ссылке.");
}
error = 0;
synchronized(monitor)
{
label0:
{
Element element;
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
break label0;
}
(element = elements[elementIndex]).text = text;
element.icon = icon;
element.font = font;
repaint();
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.setString: аргумент elementIndex выходит из диапазона.");
}
}
public void setImage(int elementIndex, Image icon) {
int error = 0;
synchronized(monitor)
{
label0:
{
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
break label0;
}
elements[elementIndex].icon = icon;
repaint();
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.setImage: аргумент elementIndex выходит из диапазона.");
}
}
public void setFont(int elementIndex, Font font) {
int error = 0;
synchronized(monitor)
{
label0:
{
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
break label0;
}
elements[elementIndex].font = font;
repaint();
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.setFont: аргумент elementIndex выходит из диапазона.");
}
}
public void setFont(Font font) {
synchronized(monitor)
{
Element[] list = elements;
for(int i = count; i-- > 0; list[i].font = font);
repaint();
}
}
public int append(String text) {
if(text == null)
{
throw new NullPointerException("ChoiceList.append: аргумент text равен нулевой ссылке.");
}
return append(new Element(text, null, null));
}
public int append(String text, Image icon) {
if(text == null)
{
throw new NullPointerException("ChoiceList.append: аргумент text равен нулевой ссылке.");
}
return append(new Element(text, icon, null));
}
public int append(String text, Image icon, Font font) {
if(text == null)
{
throw new NullPointerException("ChoiceList.append: аргумент text равен нулевой ссылке.");
}
return append(new Element(text, icon, font));
}
public int getSize() {
return count;
}
public int getSelectedIndex() {
return count <= 0 ? -1 : selected;
}
public String getSelectedString() {
int error = 0;
String result;
synchronized(monitor)
{
label0:
{
if(count <= 0)
{
error = 1;
result = null;
break label0;
}
result = elements[selected].text;
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.getSelectedString: список пуст.");
}
return result;
}
public String getString(int elementIndex) {
int error = 0;
String result;
synchronized(monitor)
{
label0:
{
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
result = null;
break label0;
}
result = elements[elementIndex].text;
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.getString: аргумент elementIndex выходит из диапазона.");
}
return result;
}
public Image getImage(int elementIndex) {
int error = 0;
Image result;
synchronized(monitor)
{
label0:
{
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
result = null;
break label0;
}
result = elements[elementIndex].icon;
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.getImage: аргумент elementIndex выходит из диапазона.");
}
return result;
}
public Font getFont(int elementIndex) {
int error = 0;
Font result;
synchronized(monitor)
{
label0:
{
if(elementIndex < 0 || elementIndex >= count)
{
error = 1;
result = null;
break label0;
}
result = elements[elementIndex].font;
}
}
if(error == 1)
{
throw new IndexOutOfBoundsException("ChoiceList.getFont: аргумент elementIndex выходит из диапазона.");
}
return result;
}
public Font getFont() {
return null;
}
protected void paint(Graphics render) {
int width = super.getWidth();
int clipLeft = render.getClipX();
int clipTop = render.getClipY();
int clipWidth = render.getClipWidth();
int clipHeight = render.getClipHeight();
synchronized(monitor)
{
int srow;
int frow;
int length = this.count;
int selected = this.selected;
Element[] elements = this.elements;
srow = clipTop / ELEMENT_HEIGHT;
frow = (clipTop + clipHeight - 1) / ELEMENT_HEIGHT;
for(int top = srow * ELEMENT_HEIGHT, index = srow; index <= frow && index < length; top += ELEMENT_HEIGHT, index++)
{
int left = MARGIN;
Element element;
String text;
Image icon;
Font font;
text = (element = elements[index]).text;
icon = element.icon;
if((font = element.font) == null) font = Font.getDefaultFont();
if(index == selected)
{
int pos;
int wid = font.stringWidth(text);
int scroll = this.scroll;
render.setColor(RasterCanvas.getSystemColor(0x0d));
render.fillRect(0, top, width, ELEMENT_HEIGHT);
if(icon != null)
{
render.drawStretch(icon, left, top + MARGIN, ICON_WIDTH, ICON_HEIGHT, 0);
left += ICON_WIDTH + MARGIN;
}
render.setFont(font);
render.setColor(RasterCanvas.getSystemColor(0x0e));
render.setClip(left, top, pos = width - left - MARGIN, ELEMENT_HEIGHT);
render.drawString(text, wid > pos ? left + scroll : left, top + ((ELEMENT_HEIGHT - font.getHeight()) >> 1), 0);
render.setClip(clipLeft, clipTop, clipWidth, clipHeight);
if((scroll -= 2) < -wid || scroll >= pos) scroll = pos - 2;
if(wid > pos) repaint();
this.scroll = scroll;
continue;
}
if(icon != null)
{
render.drawStretch(icon, left, top + MARGIN, ICON_WIDTH, ICON_HEIGHT, 0);
left += ICON_WIDTH + MARGIN;
}
render.setFont(font);
render.setColor(RasterCanvas.getSystemColor(0x07));
render.drawString(MultilinedStringBuilder.truncate(text, font, width - left - MARGIN), left, top + ((ELEMENT_HEIGHT - font.getHeight()) >> 1), 0);
}
}
}
protected void keyboardNotify(KeyboardEvent event) {
int key = event.getKey();
DeviceSettings settings = DeviceManager.getInstance().getSettings();
switch(event.getAction())
{
case KeyboardEvent.ACTION_KEY_PRESSED:
case KeyboardEvent.ACTION_KEY_REPEATED:
if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_UP) == key)
{
synchronized(monitor)
{
int len;
if((len = count) > 0)
{
int sel = (selected + len - 1) % len;
correctScrollBarPosition(selected = sel);
scroll = INITIAL_SCROLL;
}
}
break;
}
if(settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_DOWN) == key)
{
synchronized(monitor)
{
int len;
if((len = count) > 0)
{
int sel = (selected + 1) % len;
correctScrollBarPosition(selected = sel);
scroll = INITIAL_SCROLL;
}
}
break;
}
break;
}
}
protected void pointerNotify(PointerEvent event) {
int action = event.getAction();
if(
event.isButtonPressed(PointerEvent.BUTTON_MAIN) || (action == PointerEvent.ACTION_BUTTON_RELEASED ||
action == PointerEvent.ACTION_POINTER_RELEASED) && event.getButton() == PointerEvent.BUTTON_MAIN
) synchronized(monitor)
{
int lim;
int index = event.getY() / ELEMENT_HEIGHT;
if(index > (lim = count - 1)) index = lim;
if(index < 0) index = 0;
correctScrollBarPosition(selected = index);
if(action != PointerEvent.ACTION_BUTTON_RELEASED && action != PointerEvent.ACTION_POINTER_RELEASED) scroll = INITIAL_SCROLL;
}
}
private void correctScrollBarPosition(int selected) {
int pos;
int page;
int line = ELEMENT_HEIGHT;
int top1 = selected * line;
int top2 = line + top1;
ScrollBar scroll = super.getScrollBar();
if(top1 < (pos = scroll.getPosition()))
{
scroll.setPosition(top1);
}
else if(top2 > pos + (page = scroll.getPage()))
{
scroll.setPosition(top2 - page);
}
else
{
repaint();
}
}
private void insert(int index, Element element) {
synchronized(monitor)
{
int sel;
int len;
Element[] list;
if(index > (len = count)) index = len;
if(index < 0) index = 0;
if(len == (list = elements).length) Array.copy(list, 0, list = elements = new Element[(len << 1) + 1], 0, len);
if(index < len) Array.copy(list, index, list, index + 1, len - index);
list[index] = element;
count = ++len;
sel = selected;
if(len > 1 && sel >= index) selected = ++sel;
super.getScrollBar().setRange(ELEMENT_HEIGHT * len);
correctScrollBarPosition(sel);
}
}
private int append(Element element) {
int result;
synchronized(monitor)
{
int len;
Element[] list;
if((result = len = count) == (list = elements).length) Array.copy(list, 0, list = elements = new Element[(len << 1) + 1], 0, len);
list[len++] = element;
count = len;
super.getScrollBar().setRange(ELEMENT_HEIGHT * len);
}
return result;
}
}