/*
Реализация спецификаций 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.*;
import malik.emulator.microedition.lcdui.*;
import malik.emulator.util.*;
public class Alert extends SurfaceScreen
{
static final int ICON_WIDTH = 48;
static final int ICON_HEIGHT = 48;
public static final int FOREVER = -2;
private static final Font FONT;
public static final Command DISMISS_COMMAND;
private static final CommandListener DEFAULT_LISTENER;
static {
FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
DISMISS_COMMAND = createDefaultCommand("Закрыть", Command.OK, Integer.MIN_VALUE);
DEFAULT_LISTENER = new CommandListener() {
public void commandAction(Command command, Displayable screen) {
Display parent;
if(command == Alert.DISMISS_COMMAND && (parent = screen.getParentDisplay()) != null) parent.hideForeground();
}
};
}
class Helper extends SurfaceScreen.Helper
{
public Helper() {
}
protected void execute() {
serviceSizeChanged();
servicePlaceItems();
serviceScroll();
servicePaint();
}
protected final void servicePlaceItems() {
(Alert.this).placeItems();
}
}
private boolean placeItemsNeeded;
private char[] buffer;
private AlertType type;
private Image icon;
private Gauge indicator;
private String textAsString;
private final MultilinedStringBuilder textAsBuilder;
public Alert(String title) {
this(title, null, null, null, null, null);
}
public Alert(String title, String text, Image icon, AlertType type) {
this(title, null, text, icon, null, type);
}
public Alert(String title, ScrollBarStyle style, String text, Image icon, Gauge indicator, AlertType type) {
super(title, 0, true, style);
MultilinedStringBuilder builder;
if(indicator != null)
{
int error;
Command.Owner commands;
if((commands = indicator.getCommands()).size() > 0 || commands.defaultCommand() != null)
{
throw new IllegalArgumentException("Alert: аргумент indicator имеет команды.");
}
if(indicator.getListener() != null)
{
throw new IllegalArgumentException("Alert: аргумент indicator имеет обработчик команд.");
}
if(indicator.hasLabel())
{
throw new IllegalArgumentException("Alert: аргумент indicator имеет метку.");
}
if(indicator.suppliedLayout() != Item.LAYOUT_DEFAULT)
{
throw new IllegalArgumentException("Alert: аргумент indicator имеет компоновку, отличную от Item.LAYOUT_DEFAULT.");
}
if(indicator.suppliedPreferredWidth() > (-1) || indicator.suppliedPreferredHeight() > (-1))
{
throw new IllegalArgumentException("Alert: аргумент indicator имеет заблокированный предпочитаемый размер.");
}
if(indicator.interactive)
{
throw new IllegalArgumentException("Alert: аргумент indicator является интерактивным.");
}
error = 0;
synchronized(Item.OWNER_MONITOR)
{
label0:
{
if(indicator.owner != null)
{
error = 1;
break label0;
}
indicator.owner = this;
}
}
if(error == 1)
{
throw new IllegalArgumentException("Alert: аргумент indicator уже принадлежит экрану.");
}
}
this.type = type;
this.icon = icon == null ? null : Image.createImage(icon);
this.indicator = indicator;
this.textAsString = text;
this.textAsBuilder = builder = new FastMultilinedStringBuilder();
super.setCommandListener(DEFAULT_LISTENER);
helper.setDefaultCommand(DISMISS_COMMAND);
builder.append(text);
placeCommands();
}
public void addCommand(Command command) {
if(command == null)
{
throw new NullPointerException("Alert.addCommand: аргумент command равен нулевой ссылке.");
}
if(command != DISMISS_COMMAND)
{
Displayable.Helper commands = helper;
synchronized(Command.MONITOR)
{
commands.setDefaultCommand(null);
commands.addCommand(command);
}
placeCommands();
requestPaintAll();
}
}
public void removeCommand(Command command) {
if(command != null && command != DISMISS_COMMAND)
{
Displayable.Helper commands = helper;
synchronized(Command.MONITOR)
{
commands.removeCommand(command);
if(commands.size() <= 0) commands.setDefaultCommand(DISMISS_COMMAND);
}
placeCommands();
requestPaintAll();
}
}
public void setCommandListener(CommandListener listener) {
super.setCommandListener(listener == null ? DEFAULT_LISTENER : listener);
}
public void setTimeout(int timeout) {
if(timeout != FOREVER && timeout <= 0)
{
throw new IllegalArgumentException("Alert.setTimeout: аргумент timeout имеет недопустимое значение.");
}
}
public void setString(String text) {
MultilinedStringBuilder builder;
synchronized(builder = textAsBuilder)
{
this.placeItemsNeeded = true;
this.textAsString = text;
builder.clear();
builder.append(text);
}
scroll.setPosition(0);
requestPaint(CLIENT);
}
public void setImage(Image icon) {
synchronized(textAsBuilder)
{
this.placeItemsNeeded = true;
this.icon = icon == null ? null : Image.createImage(icon);
}
requestPaint(CLIENT);
}
public void setIndicator(Gauge indicator) {
if(indicator != null)
{
int error;
Command.Owner commands;
if((commands = indicator.getCommands()).size() > 0 || commands.defaultCommand() != null)
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет команды.");
}
if(indicator.getListener() != null)
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет обработчик команд.");
}
if(indicator.hasLabel())
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет метку.");
}
if(indicator.suppliedLayout() != Item.LAYOUT_DEFAULT)
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет компоновку, отличную от Item.LAYOUT_DEFAULT.");
}
if(indicator.suppliedPreferredWidth() > (-1) || indicator.suppliedPreferredHeight() > (-1))
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator имеет заблокированный предпочитаемый размер.");
}
if(indicator.interactive)
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator является интерактивным.");
}
error = 0;
synchronized(Item.OWNER_MONITOR)
{
label0:
{
if(indicator.owner != null)
{
error = 1;
break label0;
}
indicator.owner = this;
}
}
if(error == 1)
{
throw new IllegalArgumentException("Alert.setIndicator: аргумент indicator уже принадлежит экрану.");
}
}
synchronized(textAsBuilder)
{
Gauge previous;
if((previous = this.indicator) != null) previous.owner = null;
this.placeItemsNeeded = true;
this.indicator = indicator;
}
requestPaint(CLIENT);
}
public void setType(AlertType type) {
synchronized(textAsBuilder)
{
this.placeItemsNeeded = true;
this.type = type;
}
requestPaint(CLIENT);
}
public int getDefaultTimeout() {
return FOREVER;
}
public int getTimeout() {
return FOREVER;
}
public String getString() {
return textAsString;
}
public Image getImage() {
return icon;
}
public Gauge getIndicator() {
return indicator;
}
public AlertType getType() {
return type;
}
void paint(ScreenGraphics render) {
int line;
int srow;
int frow;
int rectLeft;
int rectTop;
int rectWidth;
int rectHeight;
int clipTop = render.getClipY();
int clipWidth = render.getClipWidth();
int clipHeight = render.getClipHeight();
int indicatorWidth = 0;
int indicatorHeight = 0;
Item indicator = this.indicator;
Font font = FONT;
Image icon = getUsedIcon();
MultilinedStringBuilder builder;
/* получение размеров индикатора */
if(indicator != null)
{
indicator.visible = true;
indicatorWidth = indicator.width;
indicatorHeight = indicator.height;
}
/* вывод значка */
rectLeft = 0;
rectTop = clipTop;
rectWidth = clipWidth;
rectHeight = indicatorHeight > 0 ? clipHeight - indicatorHeight - 2 : clipHeight;
if(icon != null)
{
render.drawStretch(icon, 0, rectTop + (rectHeight >> 1), ICON_WIDTH, ICON_HEIGHT, Graphics.LEFT | Graphics.VCENTER);
rectLeft += ICON_WIDTH + 2;
rectWidth -= ICON_WIDTH + 2;
}
/* вывод текста */
srow = rectTop / (line = font.getHeight());
frow = (rectTop + rectHeight - 1) / line;
render.setFont(font);
render.setColor(RasterCanvas.getSystemColor(0x28));
render.setClip(0, clipTop, clipWidth, rectHeight);
synchronized(builder = textAsBuilder)
{
int len;
int lhei;
char[] lbuf = buffer;
lhei = font.getHeight() * (len = builder.lines());
for(int top = rectHeight < lhei ? srow * line : (rectHeight - lhei) >> 1, index = srow; index <= frow && index < len; top += line, index++)
{
int lofs = builder.lineOffset(index);
int llen = builder.lineLength(index);
if(lbuf == null || lbuf.length < llen) lbuf = buffer = new char[StringBuilder.optimalCapacity(llen)];
builder.copy(lofs, lofs + llen, lbuf, 0);
render.drawChars(lbuf, 0, llen, rectLeft, top, Graphics.LEFT | Graphics.TOP);
}
}
/* вывод индикатора */
if(indicator != null)
{
int translateX;
int translateY;
rectLeft = 0;
rectTop += rectHeight + 2;
rectWidth = clipWidth;
rectHeight = indicatorHeight;
translateX = render.getTranslateX() + render.getStartPointX();
translateY = render.getTranslateY() + render.getStartPointY();
render.reset();
render.translate(translateX, translateY);
render.clipRect(rectLeft, rectTop, indicatorWidth, indicatorHeight);
render.clipRect(rectLeft, rectTop, rectWidth, rectHeight);
render.translate(rectLeft, rectTop);
indicator.onPaint(render);
}
}
void onSizeChanged(int width, int height) {
synchronized(textAsBuilder)
{
placeItemsNeeded = true;
}
super.onSizeChanged(width, height);
}
void onClientKeyboardEvent(KeyboardEvent event) {
if(event.getAction() != KeyboardEvent.ACTION_KEY_RELEASED)
{
int key = event.getKey();
ScrollBar scrollbar = scroll;
DeviceSettings settings = DeviceManager.getInstance().getSettings();
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_UP))
{
scrollbar.scroll(-FONT.getHeight());
}
else if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_DOWN))
{
scrollbar.scroll(+FONT.getHeight());
}
}
}
Displayable.Helper createHelper() {
return this.new Helper();
}
final void placeItems() {
boolean needed;
int clientWidth;
int textHeight;
int indicatorHeight;
Item indicator;
MultilinedStringBuilder builder;
synchronized(builder = textAsBuilder)
{
if(needed = placeItemsNeeded) placeItemsNeeded = false;
indicator = this.indicator;
}
if(!needed) return;
clientWidth = getApplicationWidth();
if(indicator != null)
{
int minWidth;
indicator.resetContentSize();
indicator.minimumWidth = minWidth = indicator.computeMinimumWidth();
indicator.minimumHeight = indicatorHeight = indicator.computeMinimumHeight();
indicator.width = clientWidth < minWidth ? minWidth : clientWidth;
indicator.height = indicatorHeight;
indicator.onSizeChanged();
indicatorHeight += 2;
} else
{
indicatorHeight = 0;
}
if(getUsedIcon() != null) clientWidth -= ICON_WIDTH + 2;
synchronized(builder)
{
Font font = FONT;
builder.split(font, clientWidth);
textHeight = font.getHeight() * builder.lines();
}
scroll.setRange(textHeight + indicatorHeight);
}
private Image getUsedIcon() {
Image result;
AlertType type;
if((result = icon) == null && (type = this.type) != null) result = type.getIcon();
return result;
}
}