/*
Реализация спецификаций 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 javax.microedition.lcdui;
import malik.emulator.application.*;
import malik.emulator.media.graphics.*;
import malik.emulator.microedition.lcdui.*;
public class TextField extends InteractiveItem implements Input
{
private static final Font EDITOR_FONT;
static {
EDITOR_FONT = Font.getFont(Font.FONT_INPUT_TEXT);
}
private int scroll;
private char[] buffer;
private final CommandOwner box;
private final Input alternative;
private final InputStringBuilder builder;
private final Object monitor;
public TextField(String label, String inputText, int maximumLength, int constraints) {
super(label);
Command[] commands;
CommandOwner box;
InputStringBuilder builder;
Object monitor = builder = new InputStringBuilder(inputText, maximumLength, constraints);
this.box = box = builder.new AdditionalCapabilities(false, monitor) {
public void commandAction(Command command, Displayable screen) {
super.commandAction(command, screen);
synchronized(monitor)
{
if(parentBuilder().isModified()) (TextField.this).onStateChanged();
}
}
};
this.alternative = builder.newInput();
this.builder = builder;
this.monitor = monitor;
for(int len = (commands = box.getMyOwnedCommands()) == null ? 0 : commands.length, i = 0; i < len; i++)
{
Command command;
if((command = commands[i]) != null) super.addCommand(command);
}
}
public void insert(char[] src, int offset, int length, int position) {
int error;
if(src == null)
{
throw new NullPointerException("TextField.insert: аргумент src равен нулевой ссылке.");
}
Array.checkBound("TextField.insert", src.length, offset, length);
if(length <= 0) return;
error = 0;
synchronized(monitor)
{
label0:
{
int currentLength;
InputStringBuilder builder = this.builder;
if(length + (currentLength = builder.length()) > builder.getMaximumLength())
{
error = 1;
break label0;
}
alternative.insert(src, offset, length, position);
if(currentLength == builder.length()) error = 2;
}
}
switch(error)
{
case 1:
throw new IllegalArgumentException("TextField.insert: попытка превысить максимальную длину.");
case 2:
throw new IllegalArgumentException("TextField.insert: текст не соответствует ограничениям.");
}
requestPaint();
}
public void insert(String text, int position) {
int error;
int length;
if(text == null)
{
throw new NullPointerException("TextField.insert: аргумент text равен нулевой ссылке.");
}
if((length = text.length()) <= 0) return;
error = 0;
synchronized(monitor)
{
label0:
{
int currentLength;
InputStringBuilder builder = this.builder;
if(length + (currentLength = builder.length()) > builder.getMaximumLength())
{
error = 1;
break label0;
}
alternative.insert(text, position);
if(currentLength == builder.length()) error = 2;
}
}
switch(error)
{
case 1:
throw new IllegalArgumentException("TextField.insert: попытка превысить максимальную длину.");
case 2:
throw new IllegalArgumentException("TextField.insert: текст не соответствует ограничениям.");
}
requestPaint();
}
public void delete(int offset, int length) {
int error = 0;
synchronized(monitor)
{
label0:
{
int currentLength;
InputStringBuilder builder;
if(!Array.isBoundValid(currentLength = (builder = this.builder).length(), offset, length))
{
error = 1;
break label0;
}
alternative.delete(offset, length);
if(length > 0 && currentLength == builder.length()) error = 2;
}
}
switch(error)
{
case 1:
throw new StringIndexOutOfBoundsException("TextField.delete: индекс выходит из диапазона.");
case 2:
throw new IllegalArgumentException("TextField.delete: текст не соответствует ограничениям.");
}
requestPaint();
}
public void setConstraints(int constraints) {
if(!InputStringBuilder.isConstraintsValid(constraints))
{
throw new IllegalArgumentException("TextField.setConstraints: аргумент constraints имеет недопустимое значение.");
}
synchronized(monitor)
{
Command[] commands;
CommandOwner box;
for(int i = (commands = (box = this.box).getMyOwnedCommands()) == null ? 0 : commands.length; i-- > 0; ) super.removeCommand(commands[i]);
alternative.setConstraints(constraints);
for(int len = (commands = box.getMyOwnedCommands()) == null ? 0 : commands.length, i = 0; i < len; i++)
{
Command command;
if((command = commands[i]) != null) super.addCommand(command);
}
}
requestPaint();
}
public void setChars(char[] src, int offset, int length) {
int error;
if(src != null)
{
Array.checkBound("TextField.setChars", src.length, offset, length);
Array.copy(src, offset, src = new char[length], offset = 0, length);
} else
{
offset = length = 0;
}
error = 0;
synchronized(monitor)
{
label0:
{
InputStringBuilder builder;
if(length > (builder = this.builder).getMaximumLength())
{
error = 1;
break label0;
}
if(!InputStringBuilder.isConstraintsMatch(builder.getConstraints(), src, offset, length))
{
error = 2;
break label0;
}
alternative.setChars(src, offset, length);
}
}
switch(error)
{
case 1:
throw new IllegalArgumentException("TextField.setChars: попытка превысить максимальную длину.");
case 2:
throw new IllegalArgumentException("TextField.setChars: текст не соответствует ограничениям.");
}
requestPaint();
}
public void setString(String inputText) {
int error = 0;
int length = inputText == null ? 0 : inputText.length();
synchronized(monitor)
{
label0:
{
InputStringBuilder builder;
if(length > (builder = this.builder).getMaximumLength())
{
error = 1;
break label0;
}
if(!InputStringBuilder.isConstraintsMatch(builder.getConstraints(), inputText))
{
error = 2;
break label0;
}
alternative.setString(inputText);
}
}
switch(error)
{
case 1:
throw new IllegalArgumentException("TextField.setString: попытка превысить максимальную длину.");
case 2:
throw new IllegalArgumentException("TextField.setString: текст не соответствует ограничениям.");
}
requestPaint();
}
public void setInitialInputMode(String characterSubset) {
alternative.setInitialInputMode(characterSubset);
}
public int setMaxSize(int maximumSize) {
int result;
if(maximumSize <= 0)
{
throw new IllegalArgumentException("TextField.setMaxSize: аргумент maximumSize может быть только положительным.");
}
synchronized(monitor)
{
result = alternative.setMaxSize(maximumSize);
}
requestPaint();
return result;
}
public int size() {
return alternative.size();
}
public int getConstraints() {
return alternative.getConstraints();
}
public int getCaretPosition() {
return alternative.getCaretPosition();
}
public int getChars(char[] dst) {
int error;
int result;
if(dst == null)
{
throw new NullPointerException("TextField.getChars: аргумент dst равен нулевой ссылке.");
}
error = 0;
synchronized(monitor)
{
label0:
{
if(dst.length < builder.length())
{
error = 1;
result = 0;
break label0;
}
result = alternative.getChars(dst);
}
}
if(error == 1)
{
throw new ArrayIndexOutOfBoundsException("TextField.getChars: индекс массива выходит из диапазона.");
}
return result;
}
public int getMaxSize() {
return alternative.getMaxSize();
}
public String getString() {
String result;
synchronized(monitor)
{
result = alternative.getString();
}
return result;
}
void paint(Graphics render, int contentWidth, int contentHeight) {
synchronized(monitor)
{
boolean f;
boolean r;
int bpos;
int blen;
int bwid;
int bscr;
char[] lbuf;
Font font = EDITOR_FONT;
InputStringBuilder builder = this.builder;
f = focused;
r = (builder.getConstraints() & UNEDITABLE) != 0;
bpos = builder.getCaretPosition();
blen = builder.length();
if((lbuf = buffer) == null || lbuf.length < blen) lbuf = buffer = new char[InputStringBuilder.optimalCapacity(blen - 1) + 1];
builder.copy(0, blen, lbuf, 0);
if((bwid = font.charsWidth(lbuf, 0, bpos)) < (bscr = scroll) || bwid >= bscr + contentWidth - 8)
{
int nscr = bwid - ((contentWidth - 8) >> 1);
scroll = bscr = nscr < 0 ? 0 : nscr;
}
render.setFont(font);
render.setColor(RasterCanvas.getSystemColor(f ? r ? 0x23 : 0x21 : 0x20));
render.drawElement(8, f ? r ? 3 : 1 : 0, 0, 0, 0, contentWidth, contentHeight);
render.clipRect(3, 2, contentWidth - 6, contentHeight - 4);
render.drawChars(lbuf, 0, blen, 4 - bscr, 2, 0);
if(f) render.drawLine(bscr = bwid - bscr + 3, 2, bscr, font.getHeight() + 1);
}
}
void onCommandAction(Command command) {
Screen owner;
CommandOwner box;
if((box = this.box).isMyOwnedCommand(command) && (owner = this.owner) != null)
{
box.commandAction(command, owner);
return;
}
super.onCommandAction(command);
}
void onKeyboardEvent(KeyboardEvent event) {
synchronized(monitor)
{
InputStringBuilder builder;
(builder = this.builder).keyboardEvent(event);
if(builder.isModified()) onStateChanged();
}
}
void onPointerEvent(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)
{
moveCaret(builder, scroll + event.getX() - 4);
}
}
boolean onFocusMove(int direction, int viewportWidth, int viewportHeight, int[] visibleRectangle) {
boolean result;
if(!super.onFocusMove(direction, viewportWidth, viewportHeight, visibleRectangle))
{
switch(direction)
{
case DIR_RIGHT:
synchronized(monitor)
{
builder.setCaretPosition(0);
}
break;
case DIR_LEFT:
synchronized(monitor)
{
InputStringBuilder builder;
(builder = this.builder).setCaretPosition(builder.length());
}
break;
}
requestPaint();
return true;
}
switch(direction)
{
case DIR_NONE:
return true;
case DIR_UP:
case DIR_DOWN:
return false;
case DIR_RIGHT:
synchronized(monitor)
{
int position;
InputStringBuilder builder;
if(result = (position = (builder = this.builder).getCaretPosition()) < builder.length()) builder.setCaretPosition(position + 1);
}
if(!result) return false;
break;
case DIR_LEFT:
synchronized(monitor)
{
int position;
InputStringBuilder builder;
if(result = (position = (builder = this.builder).getCaretPosition()) > 0) builder.setCaretPosition(position - 1);
}
if(!result) return false;
break;
}
requestPaint();
return true;
}
int getMinimumContentWidth() {
return 100;
}
int getMinimumContentHeight() {
return EDITOR_FONT.getHeight() + 4;
}
final void onStateChanged() {
super.notifyStateChanged();
super.requestPaint();
}
private void moveCaret(InputStringBuilder builder, int charsWidth) {
int blen = builder.length();
int caretCol;
char[] lbuf;
Font font = EDITOR_FONT;
if(charsWidth < 0) charsWidth = 0;
if((lbuf = buffer) == null || lbuf.length < blen) lbuf = buffer = new char[InputStringBuilder.optimalCapacity(blen - 1) + 1];
builder.copy(0, blen, lbuf, 0);
label0:
{
for(int w1 = 0, w2, i = 1; i <= blen; w1 = w2, i++)
{
w2 = font.charsWidth(lbuf, 0, i);
if(charsWidth >= w1 && charsWidth < w2)
{
caretCol = charsWidth - w1 <= w2 - charsWidth ? i - 1 : i;
break label0;
}
}
caretCol = blen;
}
builder.setCaretPosition(caretCol);
if(builder.isModified()) requestPaint();
}
}