/*
Реализация спецификаций 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.microedition.lcdui.*;
public abstract class ScrollingScreen extends Screen
{
static final byte SCROLL_NONE = 0x00;
static final byte SCROLL_HORZ = 0x10;
static final byte SCROLL_VERT = 0x20;
static final byte SCROLL_BOTH = 0x30;
private static final int WHEEL_SCROLL = 16; /* скорость прокрутки с помощью колеса мыши */
class Helper extends Screen.Helper
{
private int scrollHorz;
private int scrollVert;
public Helper() {
this.scrollHorz = -1;
this.scrollVert = -1;
}
protected void execute() {
serviceSizeChanged();
serviceScroll();
servicePaint();
}
protected final void serviceScroll() {
int hpos;
int vpos;
ScrollingScreen owner;
if((owner = ScrollingScreen.this).getParentDisplay() == null) return;
hpos = owner.horz.invalidatePosition();
vpos = owner.vert.invalidatePosition();
if(scrollHorz == hpos && scrollVert == vpos) return;
try
{
owner.onClientScroll(scrollHorz = hpos, scrollVert = vpos, availableWidth(), availableHeight());
}
catch(RuntimeException e)
{
e.printRealStackTrace();
}
}
protected final int positionHorizontal() {
return scrollHorz;
}
protected final int positionVertical() {
return scrollVert;
}
}
private abstract class BasicScrollBar extends CustomScrollBar
{
protected BasicScrollBar(boolean visibility, int range, Object monitor) {
super(visibility, range, monitor);
}
public abstract int getPage();
public void repaint() {
(ScrollingScreen.this).requestPaint(Displayable.CLIENT);
}
protected void notifyFieldsChanged(int fields) {
(ScrollingScreen.this).requestPaint(Displayable.CLIENT);
}
}
private final class HorzScrollBar extends BasicScrollBar
{
public HorzScrollBar(boolean visibility, int range, Object monitor) {
super(visibility, range, monitor);
}
public int getPage() {
return (ScrollingScreen.this).getApplicationWidth();
}
}
private final class VertScrollBar extends BasicScrollBar
{
public VertScrollBar(boolean visibility, int range, Object monitor) {
super(visibility, range, monitor);
}
public int getPage() {
return (ScrollingScreen.this).getApplicationHeight();
}
}
private boolean shift;
private int focused;
private final int size;
private final int[] rect;
final CustomScrollBar vert;
final CustomScrollBar horz;
private final ScrollBarStyle style;
ScrollingScreen(String title, Ticker ticker, boolean fullScreen, byte scrollbars, ScrollBarStyle style) {
this(title, ticker, fullScreen, 0, 0, scrollbars, style);
}
ScrollingScreen(String title, Ticker ticker, boolean fullScreen, int horizontalRange, int verticalRange, byte scrollbars, ScrollBarStyle style) {
super(title, ticker, fullScreen);
Object monitor = new Object();
if(style == null) style = new ScreenScrollBarStyle();
this.size = style.size();
this.rect = new int[4];
this.vert = new VertScrollBar((scrollbars & SCROLL_VERT) != 0, verticalRange, monitor);
this.horz = new HorzScrollBar((scrollbars & SCROLL_HORZ) != 0, horizontalRange, monitor);
this.style = style;
}
public final void setFullScreenMode(boolean fullScreen) {
super.setFullScreenMode(fullScreen);
}
public final boolean isFullScreenMode() {
return super.isFullScreenMode();
}
public final void scroll(int deltaHorizontal, int deltaVertical) {
boolean exec = false;
int hpage = getApplicationWidth();
int vpage = getApplicationHeight();
ScrollBar horz = this.horz;
ScrollBar vert = this.vert;
synchronized(horz.monitor)
{
int maximum;
int oldPosition;
int newPosition;
oldPosition = horz.position;
newPosition = deltaHorizontal + oldPosition;
maximum = horz.range - hpage;
if(newPosition > maximum) newPosition = maximum;
if(newPosition < 0) newPosition = 0;
if(oldPosition != newPosition)
{
horz.position = newPosition;
exec = true;
}
oldPosition = vert.position;
newPosition = deltaVertical + oldPosition;
maximum = vert.range - vpage;
if(newPosition > maximum) newPosition = maximum;
if(newPosition < 0) newPosition = 0;
if(oldPosition != newPosition)
{
vert.position = newPosition;
exec = true;
}
}
if(exec) requestPaint(CLIENT);
}
public final void scrollTo(int positionHorizontal, int positionVertical) {
boolean exec = false;
int hpage = getApplicationWidth();
int vpage = getApplicationHeight();
ScrollBar horz = this.horz;
ScrollBar vert = this.vert;
synchronized(horz.monitor)
{
int maximum;
int oldPosition;
int newPosition;
oldPosition = horz.position;
newPosition = positionHorizontal;
maximum = horz.range - hpage;
if(newPosition > maximum) newPosition = maximum;
if(newPosition < 0) newPosition = 0;
if(oldPosition != newPosition)
{
horz.position = newPosition;
exec = true;
}
oldPosition = vert.position;
newPosition = positionVertical;
maximum = vert.range - vpage;
if(newPosition > maximum) newPosition = maximum;
if(newPosition < 0) newPosition = 0;
if(oldPosition != newPosition)
{
vert.position = newPosition;
exec = true;
}
}
if(exec) requestPaint(CLIENT);
}
public final ScrollBar getVerticalScrollBar() {
return vert;
}
public final ScrollBar getHorizontalScrollBar() {
return horz;
}
abstract void paint(ScreenGraphics render);
void paintClient(ScreenGraphics render, int width, int height, int clipLeft, int clipTop, int clipWidth, int clipHeight, Image clipBuffer) {
boolean hvisible;
boolean vvisible;
int hpos;
int vpos;
int size = this.size;
int left = super.getMarginLeft();
int top = super.getMarginTop();
int right = super.getMarginRight();
int bottom = super.getMarginBottom();
int tx = left + render.getTranslateX();
int ty = top + render.getTranslateY();
ScrollBar horz = this.horz;
ScrollBar vert = this.vert;
ScrollBarStyle style = this.style;
hvisible = horz.visibility;
vvisible = vert.visibility;
hpos = horz.position;
vpos = vert.position;
width -= left + right + (vvisible ? size : 0);
height -= top + bottom + (hvisible ? size : 0);
if(hvisible)
{
render.reset();
render.translate(tx, ty + height);
style.horizontalScrollBarPaintEvent(horz, render, width, size);
}
if(vvisible)
{
render.reset();
render.translate(tx + width, ty);
style.verticalScrollBarPaintEvent(vert, render, size, height);
if(hvisible)
{
render.reset();
render.translate(tx + width, ty + height);
style.sizeGripPaintEvent(false, render, size, size);
}
}
render.reset();
render.restricts(tx, ty, width, height);
render.setStartPoint(tx - hpos, ty - vpos);
render.setClip(tx, ty, width, height);
render.clipRect(tx + clipLeft, ty + clipTop, clipWidth, clipHeight);
render.translate(tx - hpos, ty - vpos);
paint(render);
}
boolean onKeyboardEvent(KeyboardEvent event) {
if(super.onKeyboardEvent(event)) return true;
shift = event.isKeyPressed(KeyboardEvent.KEY_SHIFT) || event.isKeyPressed(KeyboardEvent.KEY_LSHIFT) || event.isKeyPressed(KeyboardEvent.KEY_RSHIFT);
onClientKeyboardEvent(event);
return true;
}
boolean onPointerEvent(PointerEvent event) {
boolean down;
int b;
int f;
if(super.onPointerEvent(event)) return true;
switch(event.getAction())
{
case PointerEvent.ACTION_POINTER_PRESSED:
if((down = (b = event.getButton()) == PointerEvent.BUTTON_WHEEL_DOWN) || b == PointerEvent.BUTTON_WHEEL_UP)
{
((ScrollBar) (shift ? horz : vert)).scroll(down ? WHEEL_SCROLL : -WHEEL_SCROLL);
return true;
}
return handlePointerEvent(focused = getFocusedElement(event.getX(), event.getY(), b), event);
case PointerEvent.ACTION_BUTTON_PRESSED:
if((down = (b = event.getButton()) == PointerEvent.BUTTON_WHEEL_DOWN) || b == PointerEvent.BUTTON_WHEEL_UP)
{
((ScrollBar) (shift ? horz : vert)).scroll(down ? WHEEL_SCROLL : -WHEEL_SCROLL);
return true;
}
/* fall through */
case PointerEvent.ACTION_POINTER_DRAGGED:
return handlePointerEvent(focused, event);
case PointerEvent.ACTION_BUTTON_RELEASED:
return (b = event.getButton()) == PointerEvent.BUTTON_WHEEL_DOWN || b == PointerEvent.BUTTON_WHEEL_UP || handlePointerEvent(focused, event);
case PointerEvent.ACTION_POINTER_RELEASED:
f = focused;
focused = 0;
return (b = event.getButton()) == PointerEvent.BUTTON_WHEEL_DOWN || b == PointerEvent.BUTTON_WHEEL_UP || handlePointerEvent(f, event);
}
return false;
}
Displayable.Helper createHelper() {
return this.new Helper();
}
void onClientScroll(int positionHorizontal, int positionVertical, int clientWidth, int clientHeight) {
}
final int getMarginLeft() {
return super.getMarginLeft();
}
final int getMarginTop() {
return super.getMarginTop();
}
final int getMarginRight() {
return super.getMarginRight() + (vert.visibility ? size : 0);
}
final int getMarginBottom() {
return super.getMarginBottom() + (horz.visibility ? size : 0);
}
private boolean handlePointerEvent(int focused, PointerEvent event) {
int a;
int[] r = rect;
switch(focused)
{
case 1:
event.translate(r[0], r[1]);
style.horizontalScrollBarPointerEvent(horz, event, r[2], r[3]);
return true;
case 2:
event.translate(r[0], r[1]);
style.verticalScrollBarPointerEvent(vert, event, r[2], r[3]);
return true;
case 3:
event.translate(r[0] - horz.position, r[1] - vert.position);
onClientPointerEvent(event);
return true;
case 4:
if((a = event.getAction()) == PointerEvent.ACTION_POINTER_DRAGGED || a == PointerEvent.ACTION_BUTTON_RELEASED || a == PointerEvent.ACTION_POINTER_RELEASED)
{
event.translate(r[0], r[1]);
scroll(event.historicalX(1) - event.getX(), event.historicalY(1) - event.getY());
}
return true;
}
return false;
}
private int getFocusedElement(int x, int y, int button) {
boolean hvisible = horz.visibility;
boolean vvisible = vert.visibility;
byte e = getElements();
int l = super.getMarginLeft();
int t = super.getMarginTop() + ((e & TICKER) != 0 ? getTickerHeight() : 0) + ((e & TITLE) != 0 ? getTitleHeight() : 0);
int w = getTotalWidth() - super.getMarginRight() - l;
int h = getTotalHeight() - super.getMarginBottom() - t - ((e & PANEL) != 0 ? getPanelHeight() : 0);
int s = size;
int[] r = rect;
if(hvisible)
{
int st = t + h - s;
int sw = vvisible ? w - s : w;
if(x >= l && x < l + sw && y >= st && y < st + s)
{
r[0] = l;
r[1] = st;
r[2] = sw;
r[3] = s;
return 1;
}
}
if(vvisible)
{
int sl = l + w - s;
int sh = hvisible ? h - s : h;
if(x >= sl && x < sl + s && y >= t && y < t + sh)
{
r[0] = sl;
r[1] = t;
r[2] = s;
r[3] = sh;
return 2;
}
}
if(hvisible) h -= s;
if(vvisible) w -= s;
if(x >= l && x < l + w && y >= t && y < t + h)
{
r[0] = l;
r[1] = t;
r[2] = w;
r[3] = h;
return button == PointerEvent.BUTTON_WHEEL ? 4 : 3;
}
return 0;
}
}