/*
Реализация спецификаций 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.*;
public abstract class Displayable extends Object
{
private static class EmptyCommand extends Command
{
public EmptyCommand(String label, int type, int priority) {
super(label, type, priority);
}
public int getPriority() {
return 0;
}
public String getLabel() {
return "";
}
}
static final byte ALL = -1;
static final byte NONE = 0;
static final byte PANEL = 0x01;
static final byte CLIENT = 0x02;
static final byte TITLE = 0x04;
static final byte TICKER = 0x08;
private static final byte FULL_NO_LISTENER = CLIENT;
private static final byte FULL_WITH_LISTENER = CLIENT | PANEL;
private static final byte NORMAL_NO_TIKER = TITLE | CLIENT | PANEL;
private static final byte NORMAL_WITH_TICKER = TICKER | TITLE | CLIENT | PANEL;
private static final int MENU_DISTANCE = 13; /* расстояние между панелью и меню */
private static final int MENU_SPIN_WIDTH = 16; /* ширина кнопки прокрутки */
private static final int MENU_SPIN_HEIGHT = 16; /* высота кнопки прокрутки */
private static final int MENU_SPIN_MARGIN = 3; /* расстояние между элементами меню и кнопками прокрутки */
private static final int MENU_INNER_MARGIN = 5; /* поля внутри меню */
private static final int MENU_OUTER_MARGIN = 8; /* отступы слева и справа от краёв экрана */
private static final int MENU_MAXIMUM_COMMANDS = 7; /* максимальное количество команд в меню */
private static final int[] COMMAND_TYPES_FOR_SOFT2;
private static final Command[] PANEL_MENU;
private static final Command MENU;
private static final Command EXEC;
private static final Command BACK;
private static final Font MENU_FONT;
static {
Command exec = createScreenCommand("Выбрать", Command.OK, 0);
Command back = createScreenCommand("Назад", Command.BACK, 0);
Command menu = createScreenCommand("Меню", Command.SCREEN, 0);
COMMAND_TYPES_FOR_SOFT2 = new int[] { Command.BACK, Command.CANCEL, Command.STOP, Command.EXIT };
PANEL_MENU = new Command[] { null, exec, back };
MENU = menu;
EXEC = exec;
BACK = back;
MENU_FONT = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
}
static int getOptimalLength(int count) {
int result;
if(count <= 0) return 1;
for(result = count; count > 1; result |= (count >>>= 1));
return result;
}
static Command createDefaultCommand(String label, int type, int priority) {
return new EmptyCommand(label, type, priority) {
public int getCommandType() {
return OK;
}
};
}
static Command createScreenCommand(String label, int type, int priority) {
return new EmptyCommand(label, type, priority) {
public int getCommandType() {
return SCREEN;
}
};
}
private static int getMenuItemsCount(int length, int height) {
int max;
int result;
if((result = length) > MENU_MAXIMUM_COMMANDS) result = MENU_MAXIMUM_COMMANDS;
if(result > (max = (height - ((MENU_INNER_MARGIN << 1) + MENU_DISTANCE)) / MENU_FONT.getHeight())) result = max;
return result;
}
private static Command[] copy(Command[] commands) {
int len;
Command[] result;
Array.copy(commands, 0, result = new Command[len = commands.length], 0, len);
return result;
}
private static Command find(Command.Enumeration enumeration, int type, Command ignore) {
Command result = null;
for(int priority = Integer.MAX_VALUE, i = enumeration.size(); i-- > 0; )
{
int p;
Command command;
if((command = enumeration.commandAt(i)) != null && command != ignore && command.type == type && (p = command.priority) <= priority)
{
result = command;
priority = p;
}
}
return result;
}
class Helper extends Display.OneShotAction implements Command.Enumeration
{
private boolean paintNoUpdate;
private byte paintElements;
private int paintLeft;
private int paintTop;
private int paintWidth;
private int paintHeight;
private int sizeWidth;
private int sizeHeight;
private int commandSize;
private Command[] commandList;
private Command commandDefault;
private Image paintBuffer;
public Helper() {
this.commandList = new Command[7];
}
public int size() {
return commandSize;
}
public Command commandAt(int index) {
return index >= 0 && index < commandSize ? commandList[index] : null;
}
public Command defaultCommand() {
return commandDefault;
}
public final synchronized void justPaint() {
Displayable owner = Displayable.this;
paintNoUpdate = true;
paintElements = Displayable.ALL;
setPaintRectangle(0, 0, owner.getApplicationWidth(), owner.getApplicationHeight());
}
public final void request(Display parent, byte elements) {
synchronized(this)
{
byte prevElements = paintElements;
paintElements = (byte) (elements | prevElements);
if((elements & Displayable.CLIENT) != 0)
{
Displayable owner = Displayable.this;
if((prevElements & Displayable.CLIENT) == 0)
{
setPaintRectangle(0, 0, owner.getApplicationWidth(), owner.getApplicationHeight());
} else
{
unionPaintRectangle(0, 0, owner.getApplicationWidth(), owner.getApplicationHeight());
}
}
}
request(parent);
}
public final void request(Display parent, byte elements, int clipLeft, int clipTop, int clipWidth, int clipHeight, Image clipBuffer) {
synchronized(this)
{
byte prevElements = paintElements;
paintElements = (byte) (elements | prevElements);
paintBuffer = clipBuffer;
if((elements & Displayable.CLIENT) != 0)
{
if((prevElements & Displayable.CLIENT) == 0)
{
setPaintRectangle(clipLeft, clipTop, clipWidth, clipHeight);
} else
{
unionPaintRectangle(clipLeft, clipTop, clipWidth, clipHeight);
}
}
}
request(parent);
}
public final void addCommand(Command command) {
int len;
Command[] commands;
if(command == null || command == commandDefault || Array.findf(commands = commandList, 0, command) < (len = commandSize)) return;
if(len == commands.length) Array.copy(commands, 0, commands = commandList = new Command[(len << 1) + 1], 0, len);
commands[len++] = command;
commandSize = len;
}
public final void removeCommand(Command command) {
int len;
int index;
Command[] commands;
if(command == null) return;
if(command == commandDefault)
{
commandDefault = null;
return;
}
if((index = Array.findf(commands = commandList, 0, command)) > (len = commandSize - 1)) return;
if(index < len) Array.copy(commands, index + 1, commands, index, len - index);
commands[len] = null;
commandSize = len;
}
public final void setDefaultCommand(Command command) {
removeCommand(command);
commandDefault = command;
}
protected void execute() {
serviceSizeChanged();
servicePaint();
}
protected final void servicePaint() {
boolean update;
byte elements;
int clipLeft;
int clipTop;
int clipWidth;
int clipHeight;
Image clipBuffer;
Display parent;
Displayable owner;
if((parent = (owner = Displayable.this).getParentDisplay()) == null) return;
synchronized(this)
{
update = !paintNoUpdate;
elements = paintElements;
clipLeft = paintLeft;
clipTop = paintTop;
clipWidth = paintWidth;
clipHeight = paintHeight;
clipBuffer = paintBuffer;
paintNoUpdate = false;
paintElements = 0;
}
owner.eventPaint(parent.graphics(), elements, clipLeft, clipTop, clipWidth, clipHeight, clipBuffer);
if(update) parent.update();
}
protected final void serviceSizeChanged() {
int width;
int height;
Displayable owner;
if((owner = Displayable.this).getParentDisplay() == null) return;
width = owner.getApplicationWidth();
height = owner.getApplicationHeight();
if(sizeWidth == width && sizeHeight == height) return;
try
{
owner.onSizeChanged(sizeWidth = width, sizeHeight = height);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
protected final int availableWidth() {
return sizeWidth;
}
protected final int availableHeight() {
return sizeHeight;
}
private void setPaintRectangle(int clipLeft, int clipTop, int clipWidth, int clipHeight) {
paintLeft = clipLeft;
paintTop = clipTop;
paintWidth = clipWidth;
paintHeight = clipHeight;
}
private void unionPaintRectangle(int clipLeft, int clipTop, int clipWidth, int clipHeight) {
int tmp;
int left = paintLeft;
int top = paintTop;
int right = left + paintWidth;
int bottom = top + paintHeight;
if(left > clipLeft) left = clipLeft;
if(top > clipTop) top = clipTop;
if(right < (tmp = clipLeft + clipWidth)) right = tmp;
if(bottom < (tmp = clipTop + clipHeight)) bottom = tmp;
paintLeft = left;
paintTop = top;
paintWidth = right - left;
paintHeight = bottom - top;
}
}
private boolean fullScreen;
private boolean menuOpened;
private int menuScroll;
private int menuIndex;
private int focused;
private int pressed;
private Command[] menuCommands;
private final Command[] panelCommands;
private CommandListener listener;
private Ticker ticker;
private String title;
private Display parent;
final Helper helper;
Displayable(String title, Ticker ticker, boolean fullscreen) {
this.fullScreen = fullscreen;
this.menuCommands = new Command[MENU_MAXIMUM_COMMANDS];
this.panelCommands = new Command[3];
this.ticker = ticker;
this.title = title;
this.helper = createHelper();
}
public void addCommand(Command command) {
if(command == null)
{
throw new NullPointerException("Displayable.addCommand: аргумент command равен нулевой ссылке.");
}
synchronized(Command.MONITOR)
{
helper.addCommand(command);
}
placeCommands();
requestPaintAll();
}
public void removeCommand(Command command) {
if(command == null) return;
synchronized(Command.MONITOR)
{
helper.removeCommand(command);
}
placeCommands();
requestPaintAll();
}
public void setCommandListener(CommandListener listener) {
this.listener = listener;
requestPaint((byte) (CLIENT | PANEL));
}
public void setTicker(Ticker ticker) {
this.ticker = ticker;
requestPaint((byte) (TICKER | TITLE | CLIENT));
}
public void setTitle(String title) {
this.title = title;
requestPaint(TITLE);
}
public boolean isShown() {
return parent != null;
}
public int getWidth() {
return getApplicationWidth();
}
public int getHeight() {
return getApplicationHeight();
}
public Ticker getTicker() {
return ticker;
}
public String getTitle() {
return title;
}
protected void sizeChanged(int width, int height) {
}
abstract void paint(ScreenGraphics render);
void paintBackground(ScreenGraphics render, int width, int height, byte visibleElements, byte drawingElements) {
int elementTop = 0;
int elementHeight;
if((visibleElements & TICKER) != 0)
{
elementHeight = getTickerHeight();
if((drawingElements & TICKER) != 0) render.drawElement(13, 0, 0, 0, elementTop, width, elementHeight);
elementTop += elementHeight;
}
if((visibleElements & TITLE) != 0)
{
elementHeight = getTitleHeight();
if((drawingElements & TITLE) != 0) render.drawElement(6, 1, 0, 0, elementTop, width, elementHeight);
elementTop += elementHeight;
}
if((drawingElements &= CLIENT | PANEL) == NONE) return;
if((visibleElements &= CLIENT | PANEL) == (CLIENT | PANEL))
{
if(drawingElements == CLIENT)
{
render.setClip(0, elementTop, width, height - elementTop - getPanelHeight());
}
else if(drawingElements == PANEL)
{
render.setClip(0, height - (elementHeight = getPanelHeight()), width, elementHeight);
}
}
render.drawElement(6, 5, 0, 0, elementTop, width, height - elementTop);
}
void paintTicker(ScreenGraphics render, int width, int height, Ticker ticker) {
render.setColor(RasterCanvas.getSystemColor(0x29));
render.drawString(ticker.getDisplayedString(), ticker.getPosition(), 2, 0);
}
void paintTitle(ScreenGraphics render, int width, int height, String title) {
render.setColor(RasterCanvas.getSystemColor(0x09));
render.drawString(MultilinedStringBuilder.truncate(title, render.getFont(), width - 4), 2, 2, 0);
}
void paintClient(ScreenGraphics render, int width, int height, int clipLeft, int clipTop, int clipWidth, int clipHeight, Image clipBuffer) {
int translatedX;
int translatedY;
int marginLeft = getMarginLeft();
int marginTop = getMarginTop();
int restrictedWidth = width - marginLeft - getMarginRight();
int restrictedHeight = height - marginTop - getMarginBottom();
render.translate(marginLeft, marginTop);
render.restricts(translatedX = render.getTranslateX(), translatedY = render.getTranslateY(), restrictedWidth, restrictedHeight);
render.setStartPoint(translatedX, translatedY);
render.setClip(0, 0, restrictedWidth, restrictedHeight);
if(!menuOpened)
{
render.clipRect(clipLeft, clipTop, clipWidth, clipHeight);
}
if(clipBuffer != null)
{
render.drawImage(clipBuffer, 0, 0, 0);
return;
}
paint(render);
}
void paintPanel(ScreenGraphics render, int width, int height, Command[] commands, int pressedCommandIndex) {
int p1 = width / 3;
int p2 = width - p1;
paintCommand(render, 0, 0, p1, height, commands[0], false, pressedCommandIndex == 0);
paintCommand(render, p1, 0, p2 - p1, height, commands[1], true, pressedCommandIndex == 1);
paintCommand(render, p2, 0, width - p2, height, commands[2], false, pressedCommandIndex == 2);
}
void paintCommand(ScreenGraphics render, int left, int top, int width, int height, Command command, boolean asDefault, boolean asPressed) {
int x;
int y;
int ofs;
Font font;
if(command == null) return;
ofs = asPressed ? 1 : asDefault ? 3 : 0;
x = left + (width >> 1) + (asPressed ? 1 : 0);
y = top + (asDefault ? 0 : 1) + (asPressed ? 3 : 2);
render.setFont(font = asDefault ? getDefaultCommandFont() : getNormalCommandFont());
render.setColor(RasterCanvas.getSystemColor(0x24 + ofs));
render.drawElement(4, ofs, 0, left, top, width, height);
render.drawString(command.getTruncatedLabel(width - 4, font), x, y, Graphics.HCENTER | Graphics.TOP);
}
void setFullScreenMode(boolean fullScreen) {
this.fullScreen = fullScreen;
requestPaintAll();
}
void setDefaultCommand(Command command) {
synchronized(Command.MONITOR)
{
helper.setDefaultCommand(command);
}
placeCommands();
requestPaintAll();
}
void onShow() {
}
void onHide() {
}
void onSizeChanged(int width, int height) {
sizeChanged(width, height);
}
void onCommandAction(Command command) {
CommandListener listener;
if(command == null) return;
if(command == MENU)
{
menuOpened = true;
requestPaintAll();
return;
}
if(command == BACK)
{
menuOpened = false;
requestPaintAll();
return;
}
if((listener = this.listener) != null)
{
if(command == EXEC)
{
menuOpened = false;
requestPaintAll();
if((command = menuCommands[menuIndex]) != null) requestCommandAction(command);
} else
{
listener.commandAction(command, this);
}
return;
}
if(command == EXEC)
{
menuOpened = false;
requestPaintAll();
}
}
void onPaint(ScreenGraphics render, byte drawingElements, int clipLeft, int clipTop, int clipWidth, int clipHeight, Image clipBuffer) {
byte visibleElements;
int totalWidth;
int totalHeight;
int tickerHeight;
int titleHeight;
int clientHeight;
int panelHeight;
Display parent;
String title = this.title;
Ticker ticker = this.ticker;
visibleElements = fullScreen ? listener == null ? FULL_NO_LISTENER : FULL_WITH_LISTENER : ticker == null ? NORMAL_NO_TIKER : NORMAL_WITH_TICKER;
if((drawingElements &= visibleElements) == 0) return;
totalWidth = (parent = this.parent).getWidth();
totalHeight = parent.getHeight();
tickerHeight = (visibleElements & TICKER) != 0 ? getTickerHeight() : 0;
titleHeight = (visibleElements & TITLE) != 0 ? getTitleHeight() : 0;
panelHeight = (visibleElements & PANEL) != 0 ? getPanelHeight() : 0;
clientHeight = totalHeight - tickerHeight - titleHeight - panelHeight;
try
{
render.reset();
paintBackground(render, totalWidth, totalHeight, visibleElements, drawingElements);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
if((drawingElements & TICKER) != 0 && ticker != null)
{
try
{
Font font = getTickerFont();
render.reset();
render.translate(0, 0);
render.setClip(0, 0, totalWidth, tickerHeight);
render.setFont(font);
ticker.scroll(getTickerScroll(), totalWidth, font);
paintTicker(render, totalWidth, tickerHeight, ticker);
requestPaint(TICKER);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
if((drawingElements & TITLE) != 0 && title != null)
{
try
{
Font font = getTitleFont();
render.reset();
render.translate(0, tickerHeight);
render.setClip(0, 0, totalWidth, titleHeight);
render.setFont(font);
paintTitle(render, totalWidth, titleHeight, title);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
if((drawingElements & PANEL) != 0)
{
try
{
Command[] commands;
synchronized(Command.MONITOR)
{
commands = copy(panelCommands);
}
render.reset();
render.translate(0, totalHeight - panelHeight);
render.setClip(0, 0, totalWidth, panelHeight);
paintPanel(render, totalWidth, panelHeight, commands, menuOpened ? -1 : pressed - 1);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
if((drawingElements & CLIENT) != 0)
{
try
{
render.reset();
render.translate(0, tickerHeight + titleHeight);
render.setClip(0, 0, totalWidth, clientHeight);
paintClient(render, totalWidth, clientHeight, clipLeft, clipTop, clipWidth, clipHeight, clipBuffer);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
}
boolean onKeyboardEvent(KeyboardEvent event) {
int key = event.getKey();
int action = event.getAction();
DeviceSettings settings = DeviceManager.getInstance().getSettings();
if((getElements() & PANEL) == 0) return false;
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_SOFT1))
{
switch(action)
{
case KeyboardEvent.ACTION_KEY_PRESSED:
if(pressed == 0 && panelCommands[0] != null)
{
pressed = 1;
requestPaint(PANEL);
}
break;
case KeyboardEvent.ACTION_KEY_RELEASED:
switch(pressed)
{
case 0:
break;
case 1:
pressed = 0;
requestPaint(PANEL);
requestCommandAction(panelCommands[0]);
break;
default:
pressed = 0;
requestPaint(PANEL);
break;
}
break;
}
return true;
}
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_SELECT))
{
switch(action)
{
case KeyboardEvent.ACTION_KEY_PRESSED:
if(pressed == 0 && panelCommands[1] != null)
{
pressed = 2;
requestPaint(PANEL);
}
break;
case KeyboardEvent.ACTION_KEY_RELEASED:
switch(pressed)
{
case 0:
break;
case 2:
pressed = 0;
requestPaint(PANEL);
requestCommandAction(panelCommands[1]);
break;
default:
pressed = 0;
requestPaint(PANEL);
break;
}
break;
}
return true;
}
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_SOFT2))
{
switch(action)
{
case KeyboardEvent.ACTION_KEY_PRESSED:
if(pressed == 0 && panelCommands[2] != null)
{
pressed = 3;
requestPaint(PANEL);
}
break;
case KeyboardEvent.ACTION_KEY_RELEASED:
switch(pressed)
{
case 0:
break;
case 3:
pressed = 0;
requestPaint(PANEL);
requestCommandAction(panelCommands[2]);
break;
default:
pressed = 0;
requestPaint(PANEL);
break;
}
break;
}
return true;
}
return false;
}
boolean onPointerEvent(PointerEvent event) {
int e;
int f;
switch(event.getAction())
{
case PointerEvent.ACTION_BUTTON_PRESSED:
case PointerEvent.ACTION_POINTER_PRESSED:
if(event.getButton() == PointerEvent.BUTTON_MAIN && (e = getScreenFocusedElement(event.getX(), event.getY())) > 0)
{
focused = pressed = e;
requestPaint(PANEL);
return true;
}
break;
case PointerEvent.ACTION_POINTER_DRAGGED:
if((f = focused) > 0)
{
if(pressed != (pressed = getScreenFocusedElement(event.getX(), event.getY()) != f ? 0 : f)) requestPaint(PANEL);
return true;
}
break;
case PointerEvent.ACTION_POINTER_RELEASED:
case PointerEvent.ACTION_BUTTON_RELEASED:
if(event.getButton() == PointerEvent.BUTTON_MAIN && focused > 0)
{
Command command;
e = pressed;
focused = pressed = 0;
if(e > 0 && e <= 3 && (getElements() & PANEL) != 0 && (command = panelCommands[e - 1]) != null)
{
requestPaint(PANEL);
requestCommandAction(command);
}
return true;
}
break;
}
return false;
}
boolean isFullScreenMode() {
return fullScreen;
}
float getTickerScroll() {
return 60.f / DeviceManager.getInstance().getSettings().getMaximumFrequency();
}
int getCommandIndexAt(int x, int y, int panelWidth, int panelHeight) {
if(x >= 0 && x < panelWidth && y >= 0 && y < panelHeight)
{
int p1 = panelWidth / 3;
int p2 = panelWidth - p1;
return x < p1 ? 0 : x < p2 ? 1 : 2;
}
return -1;
}
int getMarginLeft() {
return 0;
}
int getMarginTop() {
return 0;
}
int getMarginRight() {
return 0;
}
int getMarginBottom() {
return 0;
}
int getTickerHeight() {
return getTickerFont().getHeight() + 4;
}
int getTitleHeight() {
return getTitleFont().getHeight() + 4;
}
int getPanelHeight() {
return getDefaultCommandFont().getHeight() + 6;
}
Font getTickerFont() {
return Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
}
Font getTitleFont() {
return Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
}
Font getNormalCommandFont() {
return Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
}
Font getDefaultCommandFont() {
return Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
}
Displayable.Helper createHelper() {
return this.new Helper();
}
final void eventShow(Display parent) {
try
{
onShow();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
this.parent = parent;
}
final void eventHide() {
this.parent = null;
try
{
onHide();
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
final void eventKeyboard(KeyboardEvent event) {
try
{
if(menuOpened)
{
onKeyboardEventMenu(event);
} else
{
onKeyboardEvent(event);
}
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
final void eventPointer(PointerEvent event) {
try
{
if(menuOpened)
{
onPointerEventMenu(event);
} else
{
onPointerEvent(event);
}
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
final void eventPaint(ScreenGraphics render, byte elements, int clipLeft, int clipTop, int clipWidth, int clipHeight, Image clipBuffer) {
try
{
Display parent = this.parent;
if(menuOpened & updateMenu())
{
int panelHeight = getPanelHeight();
int screenWidth = parent.getWidth();
int screenHeight = parent.getHeight();
try
{
onPaint(render, ALL, clipLeft, clipTop, clipWidth, clipHeight, clipBuffer);
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
render.reset();
render.setARGBColor(0xc0000000);
render.fillRect(0, 0, screenWidth, screenHeight);
render.reset();
render.translate(0, screenHeight - panelHeight);
render.setClip(0, 0, screenWidth, panelHeight);
paintPanel(render, screenWidth, panelHeight, copy(PANEL_MENU), pressed - 1);
render.reset();
render.setClip(0, 0, screenWidth, screenHeight -= panelHeight);
paintMenu(render, screenWidth, screenHeight);
} else
{
onPaint(render, elements, clipLeft, clipTop, clipWidth, clipHeight, clipBuffer);
}
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
final void requestAction(Runnable action) {
Display parent;
if(action != null && (parent = this.parent) != null) parent.callSerially(action);
}
final void requestCommandAction(final Command command) {
requestAction(command == null ? null : new Runnable() {
public void run() {
(Displayable.this).onCommandAction(command);
}
});
}
final void requestPaint(byte elements) {
Display parent;
Displayable active;
if(elements != 0 && (parent = this.parent) != null && (active = parent.getActiveForeground()) != null) active.helper.request(parent, elements);
}
final void requestPaintAll() {
Display parent;
Displayable active;
if((parent = this.parent) != null && (active = parent.getActiveForeground()) != null) active.helper.request(parent, ALL);
}
final void requestPaintClient(int clipLeft, int clipTop, int clipWidth, int clipHeight) {
Display parent;
Displayable active;
if((parent = this.parent) != null && (active = parent.getActiveForeground()) != null) active.helper.request(parent, CLIENT, clipLeft, clipTop, clipWidth, clipHeight, null);
}
final void requestPaintClient(int clipLeft, int clipTop, int clipWidth, int clipHeight, Image clipBuffer) {
Display parent;
Displayable active;
if((parent = this.parent) != null && (active = parent.getActiveForeground()) != null) active.helper.request(parent, CLIENT, clipLeft, clipTop, clipWidth, clipHeight, clipBuffer);
}
final void servicePaint() {
Display parent;
Displayable active;
if((parent = this.parent) != null && (active = parent.getActiveForeground()) != null) active.helper.service();
}
final void placeCommands() {
synchronized(Command.MONITOR)
{
int len;
int placed;
int[] types;
Command[] c;
Command left;
Command right;
Command center;
Command.Enumeration list;
placed = 1;
if((center = (list = helper).defaultCommand()) == null && (center = find(list, Command.OK, null)) != null) placed++;
right = null;
len = (types = COMMAND_TYPES_FOR_SOFT2).length;
for(int i = 0; i < len; i++) if((right = find(list, types[i], center)) != null)
{
placed++;
break;
}
left = null;
if((len = list.size()) <= placed)
{
Array.fill(c = menuCommands, 0, c.length, null);
for(int i = len == placed ? len : 0; i-- > 0; )
{
Command command;
if((command = list.commandAt(i)) != null && command != center && command != right)
{
left = command;
break;
}
}
} else
{
int count;
if((c = menuCommands).length < (count = len - placed + 1)) c = menuCommands = new Command[getOptimalLength(count)];
left = MENU;
{
int i = 0;
for(int index = 0; index < len; index++)
{
Command command;
if((command = list.commandAt(index)) != null && command != center && command != right) c[i++] = command;
}
Array.fill(c, count = i, c.length - count, null);
}
for(int lim = count - 1, i = 0; i < lim; i++)
{
int j;
int jpriority;
Command jcommand;
jpriority = (jcommand = c[j = i]).priority;
for(int k = i + 1; k < count; k++)
{
int kpriority;
Command kcommand;
if((kpriority = (kcommand = c[k]).priority) < jpriority)
{
j = k;
jpriority = kpriority;
jcommand = kcommand;
}
}
if(i < j)
{
Array.copy(c, i, c, i + 1, j - i);
c[i] = jcommand;
}
}
}
(c = panelCommands)[0] = left;
c[1] = center;
c[2] = right;
}
}
final boolean isMenuOpened() {
return menuOpened;
}
final byte getElements() {
return fullScreen ? listener == null ? FULL_NO_LISTENER : FULL_WITH_LISTENER : ticker == null ? NORMAL_NO_TIKER : NORMAL_WITH_TICKER;
}
final int getClientHeight() {
byte elements = getElements();
int result = getTotalHeight();
if((elements & TICKER) != 0) result -= getTickerHeight();
if((elements & TITLE) != 0) result -= getTitleHeight();
if((elements & PANEL) != 0) result -= getPanelHeight();
return result;
}
final int getTotalWidth() {
Display parent;
if((parent = this.parent) == null) parent = DeviceManager.getInstance().getCurrentDisplay();
return parent.getWidth();
}
final int getTotalHeight() {
Display parent;
if((parent = this.parent) == null) parent = DeviceManager.getInstance().getCurrentDisplay();
return parent.getHeight();
}
final int getApplicationWidth() {
return getTotalWidth() - getMarginLeft() - getMarginRight();
}
final int getApplicationHeight() {
return getClientHeight() - getMarginTop() - getMarginBottom();
}
final Display getParentDisplay() {
return parent;
}
private void paintMenu(Graphics render, int width, int height) {
boolean upEnabled;
boolean downEnabled;
int itemHeight;
int itemsWidth;
int itemsHeight;
int menuIndex;
int menuCount;
int menuScroll;
int menuLength;
int menuPressed;
Command[] commands;
synchronized(Command.MONITOR)
{
commands = copy(menuCommands);
}
menuIndex = this.menuIndex;
menuScroll = this.menuScroll;
menuLength = Array.findf(commands, 0, null);
itemsWidth = width - (((MENU_OUTER_MARGIN + MENU_INNER_MARGIN) << 1) + MENU_SPIN_MARGIN + MENU_SPIN_WIDTH);
itemsHeight = (menuCount = getMenuItemsCount(menuLength, height)) * (itemHeight = MENU_FONT.getHeight());
render.translate(MENU_OUTER_MARGIN, height - itemsHeight - ((MENU_INNER_MARGIN << 1) + MENU_DISTANCE));
render.setClip(0, 0, width -= (MENU_OUTER_MARGIN << 1), height = itemsHeight + (MENU_INNER_MARGIN << 1));
render.setColor(RasterCanvas.getSystemColor(0x04));
render.fillRect(0, 0, width, height);
render.translate(MENU_INNER_MARGIN, MENU_INNER_MARGIN);
render.setClip(0, 0, itemsWidth, itemsHeight);
render.setFont(MENU_FONT);
for(int itemTop = 0, itemIndex = menuScroll, i = menuCount; i-- > 0; itemTop += itemHeight, itemIndex++)
{
if(itemIndex != menuIndex)
{
render.setColor(RasterCanvas.getSystemColor(0x07));
} else
{
render.setColor(RasterCanvas.getSystemColor(0x0d));
render.fillRect(0, itemTop, itemsWidth, itemHeight);
render.setColor(RasterCanvas.getSystemColor(0x0e));
}
render.drawString(commands[itemIndex].getTruncatedLabel(itemsWidth - 4, MENU_FONT), 2, itemTop, 0);
}
menuPressed = pressed;
render.translate(itemsWidth + MENU_SPIN_MARGIN, 0);
render.setClip(0, 0, MENU_SPIN_WIDTH, itemsHeight);
if((upEnabled = menuScroll > 0) | (downEnabled = menuScroll + menuCount < menuLength))
{
int menuFocused = focused;
render.drawElement(14, 0, menuPressed != 4 ? upEnabled || menuFocused == 4 ? 0 : 2 : 1, 0, itemsHeight - (MENU_SPIN_HEIGHT << 1), MENU_SPIN_WIDTH, MENU_SPIN_HEIGHT);
render.drawElement(14, 1, menuPressed != 5 ? downEnabled || menuFocused == 5 ? 0 : 2 : 1, 0, itemsHeight - MENU_SPIN_HEIGHT, MENU_SPIN_WIDTH, MENU_SPIN_HEIGHT);
}
}
private void onKeyboardEventMenu(KeyboardEvent event) {
boolean down;
int key = event.getKey();
int action = event.getAction();
DeviceSettings settings = DeviceManager.getInstance().getSettings();
if(action == KeyboardEvent.ACTION_KEY_PRESSED && ((down = key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_DOWN)) || key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_UP)))
{
int menuIndex = this.menuIndex;
int menuScroll = this.menuScroll;
int menuLength = Array.findf(menuCommands, 0, null);
int count = getMenuItemsCount(menuLength, getTotalHeight() - getPanelHeight());
menuIndex = (down ? menuIndex + 1 : menuIndex + menuLength - 1) % menuLength;
if(menuIndex < menuScroll) menuScroll = menuIndex;
if(menuIndex >= menuScroll + count) menuScroll = menuIndex - count + 1;
if(menuScroll + count > menuLength) menuScroll = menuLength - count;
if(menuScroll < 0) menuScroll = 0;
this.menuScroll = menuScroll;
this.menuIndex = menuIndex;
requestPaintAll();
return;
}
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_SOFT1))
{
switch(action)
{
case KeyboardEvent.ACTION_KEY_PRESSED:
if(pressed == 0 && PANEL_MENU[0] != null)
{
pressed = 1;
requestPaint(PANEL);
}
break;
case KeyboardEvent.ACTION_KEY_RELEASED:
switch(pressed)
{
case 0:
break;
case 1:
pressed = 0;
requestPaint(PANEL);
requestCommandAction(PANEL_MENU[0]);
break;
default:
pressed = 0;
requestPaint(PANEL);
break;
}
break;
}
return;
}
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_SELECT))
{
switch(action)
{
case KeyboardEvent.ACTION_KEY_PRESSED:
if(pressed == 0 && PANEL_MENU[1] != null)
{
pressed = 2;
requestPaint(PANEL);
}
break;
case KeyboardEvent.ACTION_KEY_RELEASED:
switch(pressed)
{
case 0:
break;
case 2:
pressed = 0;
requestPaint(PANEL);
requestCommandAction(PANEL_MENU[1]);
break;
default:
pressed = 0;
requestPaint(PANEL);
break;
}
break;
}
return;
}
if(key == settings.getKeyUsedAs(DeviceSettings.DEVICE_KEY_SOFT2))
{
switch(action)
{
case KeyboardEvent.ACTION_KEY_PRESSED:
if(pressed == 0 && PANEL_MENU[2] != null)
{
pressed = 3;
requestPaint(PANEL);
}
break;
case KeyboardEvent.ACTION_KEY_RELEASED:
switch(pressed)
{
case 0:
break;
case 3:
pressed = 0;
requestPaint(PANEL);
requestCommandAction(PANEL_MENU[2]);
break;
default:
pressed = 0;
requestPaint(PANEL);
break;
}
break;
}
return;
}
}
private void onPointerEventMenu(PointerEvent event) {
int e;
int f;
switch(event.getAction())
{
case PointerEvent.ACTION_BUTTON_PRESSED:
case PointerEvent.ACTION_POINTER_PRESSED:
if(event.getButton() == PointerEvent.BUTTON_MAIN)
{
if((e = getMenuFocusedElement(event.getX(), event.getY())) > 0 && e <= 5)
{
int menuScroll = this.menuScroll;
int menuLength = Array.findf(menuCommands, 0, null);
int menuCount = getMenuPage(menuLength);
switch(e)
{
case 4:
if(menuScroll > 0)
{
this.menuScroll = menuScroll - 1;
focused = pressed = 4;
requestPaintAll();
break;
}
focused = 9;
break;
case 5:
if(menuScroll + menuCount < menuLength)
{
this.menuScroll = menuScroll + 1;
focused = pressed = 5;
requestPaintAll();
break;
}
focused = 9;
break;
default:
focused = pressed = e;
requestPaintAll();
break;
}
break;
}
if(e == 9)
{
focused = 9;
break;
}
if(e >= 10)
{
focused = 10;
if(menuIndex != (menuIndex = e - 10)) requestPaintAll();
}
}
break;
case PointerEvent.ACTION_POINTER_DRAGGED:
if((f = focused) > 0)
{
e = getMenuFocusedElement(event.getX(), event.getY());
if(f > 0 && f <= 5 && pressed != (pressed = e != f ? 0 : f) || f == 10 && e >= 10 && menuIndex != (menuIndex = e - 10)) requestPaintAll();
}
break;
case PointerEvent.ACTION_POINTER_RELEASED:
case PointerEvent.ACTION_BUTTON_RELEASED:
if(event.getButton() == PointerEvent.BUTTON_MAIN)
{
Command command;
e = pressed;
f = focused;
focused = pressed = 0;
if(f == 0)
{
menuOpened = false;
requestPaintAll();
break;
}
if(e > 0 && e <= 3 && (command = PANEL_MENU[e - 1]) != null)
{
requestPaintAll();
requestCommandAction(command);
break;
}
requestPaintAll();
}
break;
}
}
private boolean updateMenu() {
int count;
int menuIndex;
int menuScroll;
int menuLength;
if((menuLength = Array.findf(menuCommands, 0, null)) <= 0) return menuOpened = false;
count = getMenuPage(menuLength);
menuIndex = this.menuIndex;
menuScroll = this.menuScroll;
if(menuIndex < 0) menuIndex = 0;
if(menuIndex >= menuLength) menuIndex = menuLength - 1;
if(menuScroll + count > menuLength) menuScroll = menuLength - count;
if(menuScroll < 0) menuScroll = 0;
this.menuScroll = menuScroll;
this.menuIndex = menuIndex;
return true;
}
private int getScreenFocusedElement(int x, int y) {
int b;
int h;
int w;
int t;
if((getElements() & PANEL) == 0) return 0;
h = getPanelHeight();
w = getTotalWidth();
t = getTotalHeight() - h;
return x >= 0 && x < w && y >= t && y < t + h && (b = getCommandIndexAt(x, y - t, w, h)) >= 0 && b < 3 && panelCommands[b] != null ? b + 1 : 0;
}
private int getMenuFocusedElement(int x, int y) {
int w = getTotalWidth();
int h = getPanelHeight();
int l;
int t = getTotalHeight() - h;
int itemHeight;
int itemsWidth;
int itemsHeight;
int menuScroll;
int menuLength;
if(x >= 0 && x < w && y >= t && y < t + h)
{
int b;
return (b = getCommandIndexAt(x, y - t, w, h)) >= 0 && b < 3 && PANEL_MENU[b] != null ? b + 1 : 0;
}
h = t;
menuScroll = this.menuScroll;
menuLength = Array.findf(menuCommands, 0, null);
itemsWidth = w - (((MENU_OUTER_MARGIN + MENU_INNER_MARGIN) << 1) + MENU_SPIN_MARGIN + MENU_SPIN_WIDTH);
itemsHeight = getMenuItemsCount(menuLength, h) * (itemHeight = MENU_FONT.getHeight());
l = MENU_OUTER_MARGIN;
t -= itemsHeight + ((MENU_INNER_MARGIN << 1) + MENU_DISTANCE);
w -= MENU_OUTER_MARGIN << 1;
h = itemsHeight + (MENU_INNER_MARGIN << 1);
if(x < l || x >= l + w || y < t || y >= t + h) return 0;
l += MENU_INNER_MARGIN;
t += MENU_INNER_MARGIN;
w = itemsWidth;
h = itemsHeight;
if(x >= l && x < l + w && y >= t && y < t + h) return 10 + menuScroll + (y - t) / itemHeight;
l += w + MENU_SPIN_MARGIN;
t += h - (MENU_SPIN_HEIGHT << 1);
w = MENU_SPIN_WIDTH;
h = MENU_SPIN_HEIGHT;
if(x >= l && x < l + w && y >= t && y < t + h) return 4;
t += MENU_SPIN_HEIGHT;
if(x >= l && x < l + w && y >= t && y < t + h) return 5;
return 9;
}
private int getMenuPage(int menuLength) {
return getMenuItemsCount(menuLength, getTotalHeight() - getPanelHeight());
}
}