/*
* Copyright (C) 2014 Sergey Basalaev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package alchemy.libs;
import alchemy.fs.Filesystem;
import alchemy.io.ConnectionInputStream;
import alchemy.io.IO;
import alchemy.libs.ui.FontManager;
import alchemy.libs.ui.UiCanvas;
import alchemy.libs.ui.UiEditBox;
import alchemy.libs.ui.UiImage;
import alchemy.libs.ui.UiListBox;
import alchemy.libs.ui.UiMenu;
import alchemy.libs.ui.UiMsgBox;
import alchemy.libs.ui.UiScreen;
import alchemy.platform.Platform;
import alchemy.platform.UI;
import alchemy.system.Cache;
import alchemy.system.NativeLibrary;
import alchemy.system.Process;
import alchemy.system.UIServer;
import alchemy.types.Int32;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.PixelGrabber;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
/**
* User interface library, pc implementation.
*
* Ether classes are mapped as follows:
*
* <table border="1">
* <tr>
* <th>Ether</th>
* <th>Java</th>
* </tr>
* <tr>
* <td>Graphics</td>
* <td>java.awt.Graphics2D</td>
* </tr>
* <tr>
* <td>Image</td>
* <td>alchemy.libs.ui.ImageImpl</td>
* </tr>
* </table>
*
* @author Sergey Basalaev
*/
public final class LibUI2 extends NativeLibrary {
// transform constants
private static final int TRANS_NONE = 0;
private static final int TRANS_MIRROR_ROT180 = 1;
private static final int TRANS_MIRROR = 2;
private static final int TRANS_ROT180 = 3;
private static final int TRANS_MIRROR_ROT270 = 4;
private static final int TRANS_ROT90 = 5;
private static final int TRANS_ROT270 = 6;
private static final int TRANS_MIRROR_ROT90 = 7;
// stroke styles
public static final Stroke SOLID = new BasicStroke();
public static final Stroke DOTTED = new BasicStroke(
1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER,
10.0f, new float[] { 0.0f, 1.0f }, 0.0f
);
public LibUI2() throws IOException {
load("/symbols/ui2");
name = "libui.2.so";
}
private final UI ui = Platform.getPlatform().getUI();
private HashMap<Int32, Color> colorCache = new HashMap<Int32, Color>();
private Color color(Int32 rgb) {
Color c = colorCache.get(rgb);
if (c == null) {
c = new Color(rgb.value);
colorCache.put(rgb, c);
}
return c;
}
private void drawRGB(Graphics2D g, int[] data, int offset, int scanlen, int xofs, int yofs, int w, int h, boolean alpha) {
for (int x = 0; x < w; x++)
for (int y = 0; y < h; y++) {
int rgb = data[offset + y*scanlen + x];
if (!alpha) rgb |= 0xFF000000;
g.setColor(color(Ival(rgb)));
g.fillRect(x + xofs, y + yofs, 1, 1);
}
}
@Override
protected Object invokeNative(int index, Process p, Object[] args) throws Exception {
switch (index) {
/* == Header: font.eh == */
case 0: // stringWidth(font: Int, str: String): Int
return Ival(FontManager.getFontMetrics(ival(args[0])).stringWidth((String)args[1]));
case 1: // fontHeight(font: Int): Int
return Ival(FontManager.getFontMetrics(ival(args[0])).getHeight());
case 2: // fontBaseline(font: Int): Int
return Ival(FontManager.getFontMetrics(ival(args[0])).getAscent());
/* == Header: graphics.eh == */
case 3: // Graphics.getColor(): Int
return Ival(((Graphics2D)args[0]).getColor().getRGB());
case 4: // Graphics.setColor(rgb: Int)
((Graphics2D)args[0]).setColor(color((Int32)args[1]));
return null;
case 5: // Graphics.getStroke(): Int
return Ival(((Graphics2D)args[0]).getStroke() == DOTTED);
case 6: // Graphics.setStroke(stroke: Int)
((Graphics2D)args[0]).setStroke(bval(args[1]) ? DOTTED : SOLID);
return null;
case 7: // Graphics.getFont(): Int
return FontManager.getFontMask(((Graphics2D)args[0]).getFont());
case 8: // Graphics.setFont(font: Int)
((Graphics2D)args[0]).setFont(FontManager.getFont(ival(args[1])));
return null;
case 9: // Graphics.drawLine(x1: Int, y1: Int, x2: Int, y2: Int)
((Graphics2D)args[0]).drawLine(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]));
return null;
case 10: // Graphics.drawRect(x: Int, y: Int, w: Int, h: Int)
((Graphics2D)args[0]).drawRect(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]));
return null;
case 11: // Graphics.fillRect(x: Int, y: Int, w: Int, h: Int)
((Graphics2D)args[0]).fillRect(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]));
return null;
case 12: // Graphics.drawRoundrect(x: Int, y: Int, w: Int, h: Int, arcw: Int, arch: Int)
((Graphics2D)args[0]).drawRoundRect(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]), ival(args[5]), ival(args[6]));
return null;
case 13: // Graphics.fillRoundrect(x: Int, y: Int, w: Int, h: Int, arcw: Int, arch: Int)
((Graphics2D)args[0]).fillRoundRect(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]), ival(args[5]), ival(args[6]));
return null;
case 14: // Graphics.drawArc(x: Int, y: Int, w: Int, h: Int, sta: Int, a: Int)
((Graphics2D)args[0]).drawArc(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]), ival(args[5]), ival(args[6]));
return null;
case 15: // Graphics.fillArc(x: Int, y: Int, w: Int, h: Int, sta: Int, a: Int)
((Graphics2D)args[0]).fillArc(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]), ival(args[5]), ival(args[6]));
return null;
case 16: { // Graphics.fillTriangle(x1: Int, y1: Int, x2: Int, y2: Int, x3: Int, y3: Int)
int[] xpoints = { ival(args[1]), ival(args[3]), ival(args[5]) };
int[] ypoints = { ival(args[2]), ival(args[4]), ival(args[6]) };
((Graphics2D)args[0]).fillPolygon(xpoints, ypoints, 3);
return null;
}
case 17: { // Graphics.drawString(str: String, x: Int, y: Int)
Graphics2D g = (Graphics2D) args[0];
int x = ival(args[2]);
int y = ival(args[3]) + g.getFontMetrics().getAscent();
g.drawString((String)args[1], x, y);
return null;
}
case 18: // Graphics.drawImage(im: Image, x: Int, y: Int)
((Graphics2D)args[0]).drawImage(((UiImage)args[1]).image, ival(args[2]), ival(args[3]), null);
return null;
case 19: // Graphics.drawRGB(rgb: [Int], ofs: Int, scanlen: Int, x: Int, y: Int, w: Int, h: Int, alpha: Bool)
drawRGB(((Graphics2D)args[0]), (int[])args[1], ival(args[2]), ival(args[3]), ival(args[4]), ival(args[5]), ival(args[6]), ival(args[7]), bval(args[8]));
return null;
case 20: { // Graphics.copyArea(xsrc: Int, ysrc: Int, w: Int, h: Int, xdst: Int, ydst: Int)
int x = ival(args[1]);
int y = ival(args[2]);
int dx = x - ival(args[5]);
int dy = x - ival(args[6]);
((Graphics2D)args[0]).copyArea(x, y, ival(args[3]), ival(args[4]), dx, dy);
return null;
}
case 21: { // Graphics.drawRegion(im: Image, xsrc: Int, ysrc: Int, w: Int, h: Int, trans: Int, xdst: Int, ydst: Int)
// based on Microemu implementation
// author: Andres Navarro
// license: LGPL 2.1+
// read arguments
Graphics2D g = (Graphics2D) args[0];
Image img = ((UiImage)args[1]).image;
int x_src = ival(args[2]);
int y_src = ival(args[3]);
int width = ival(args[4]);
int height = ival(args[5]);
int transform = ival(args[6]);
int x_dst = ival(args[7]);
int y_dst = ival(args[8]);
// prepare transformation
AffineTransform t = new AffineTransform();
int dW = width;
int dH = height;
switch (transform) {
case TRANS_NONE: {
break;
}
case TRANS_ROT90: {
t.translate((double) height, 0);
t.rotate(Math.PI / 2);
dW = height;
dH = width;
break;
}
case TRANS_ROT180: {
t.translate(width, height);
t.rotate(Math.PI);
break;
}
case TRANS_ROT270: {
t.translate(0, width);
t.rotate(Math.PI * 3 / 2);
dW = height;
dH = width;
break;
}
case TRANS_MIRROR: {
t.translate(width, 0);
t.scale(-1, 1);
break;
}
case TRANS_MIRROR_ROT90: {
t.translate((double) height, 0);
t.rotate(Math.PI / 2);
t.translate((double) width, 0);
t.scale(-1, 1);
dW = height;
dH = width;
break;
}
case TRANS_MIRROR_ROT180: {
t.translate(width, 0);
t.scale(-1, 1);
t.translate(width, height);
t.rotate(Math.PI);
break;
}
case TRANS_MIRROR_ROT270: {
t.rotate(Math.PI * 3 / 2);
t.scale(-1, 1);
dW = height;
dH = width;
break;
}
default:
throw new IllegalArgumentException("Bad transform");
}
AffineTransform savedT = g.getTransform();
g.translate(x_dst, y_dst);
g.transform(t);
g.drawImage(img, 0, 0, width, height, x_src, y_src, x_src + width, y_src + height, null);
// return to saved
g.setTransform(savedT);
return null;
}
/* == Header: image.eh == */
case 22: // Image.new(w: Int, h: Int)
return new UiImage(ival(args[0]), ival(args[1]));
case 23: // Image.graphics(): Graphics
return ((UiImage)args[0]).getGraphics();
case 24: { // imageFromARGB(argb: [Int], w: Int, h: Int, alpha: Bool): Image
int[] data = (int[]) args[0];
int w = ival(args[1]);
int h = ival(args[2]);
boolean alpha = bval(args[3]);
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
drawRGB(img.createGraphics(), data, 0, w, 0, 0, w, h, alpha);
return new UiImage(img);
}
case 25: { // imageFromFile(file: String): Image
String filename = p.toFile((String)args[0]);
long tstamp = Filesystem.lastModified(filename);
UiImage img = (UiImage) Cache.get(filename, tstamp);
if (img == null) {
String url = Filesystem.getNativeURL(filename);
if (url != null) {
img = new UiImage(Toolkit.getDefaultToolkit().createImage(new URL(url)));
} else {
ConnectionInputStream in = new ConnectionInputStream(Filesystem.read(filename));
p.addConnection(in);
byte[] buf = IO.readFully(in);
in.close();
p.removeConnection(in);
img = new UiImage(Toolkit.getDefaultToolkit().createImage(buf));
}
Cache.put(filename, tstamp, img);
}
return img;
}
case 26: { // imageFromStream(input: IStream): Image
byte[] buf = IO.readFully((InputStream) args[0]);
return new UiImage(Toolkit.getDefaultToolkit().createImage(buf));
}
case 27: { // imageFromData(data: [Byte], ofs: Int = 0, len: Int = -1): Image
final byte[] buf = (byte[])args[0];
int ofs = ival(args[1]);
int len = ival(args[2]);
if (len < 0) len = buf.length - ofs;
return new UiImage(Toolkit.getDefaultToolkit().createImage(buf, ofs, len));
}
case 28: { // imageFromImage(im: Image, x: Int, y: Int, w: Int, h: Int): Image
UiImage img = (UiImage) args[0];
int x = ival(args[1]);
int y = ival(args[2]);
int w = ival(args[3]);
int h = ival(args[4]);
BufferedImage newimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
newimg.getGraphics().drawImage(img.image, x, y, null);
return new UiImage(newimg);
}
case 29: // Image.getARGB(argb: [Int], ofs: Int, scanlen: Int, x: Int, y: Int, w: Int, h: Int)
new PixelGrabber(((UiImage)args[0]).image, ival(args[4]), ival(args[5]), ival(args[6]), ival(args[7]), (int[])args[1], ival(args[2]), ival(args[3])).grabPixels();
return null;
case 30: // Image.getWidth(): Int
return Ival(((UiImage)args[0]).image.getWidth(null));
case 31: // Image.getHeight(): Int
return Ival(((UiImage)args[0]).image.getHeight(null));
case 32: // Image.isMutable(): Bool
return Ival(((UiImage)args[0]).isMutable);
/* == Header: ui.eh == */
case 33: // Menu.new(text: String, priority: Int, mtype: Int = MT_SCREEN): Menu
return new UiMenu((String)args[0], ival(args[1]), ival(args[2]));
case 34: // Menu.getLabel(): String
return ((UiMenu)args[0]).getLabel();
case 35: // Menu.getPriority(): Int
return Ival(((UiMenu)args[0]).getPriority());
case 36: // Menu.getType(): Int
return Ival(((UiMenu)args[0]).getType());
case 37: // Screen.isShown(): Bool
return Ival(args[0] == ui.getCurrentScreen());
case 38: // Screen.getHeight(): Int
return Ival(((UiScreen)args[0]).getHeight());
case 39: // Screen.getWidth(): Int
return Ival(((UiScreen)args[0]).getWidth());
case 40: // Screen.getTitle(): String
return ((UiScreen)args[0]).getTitle();
case 41: { // Screen.setTitle(title: String)
UiScreen screen = (UiScreen) args[0];
String title = (String) args[1];
screen.setTitle(title);
ui.screenTitleChanged(screen, title);
return null;
}
case 42: { // Screen.addMenu(menu: Menu)
UiScreen screen = (UiScreen) args[0];
UiMenu menu = (UiMenu) args[1];
screen.addMenu(menu);
ui.screenMenuAdded(screen, menu);
return null;
}
case 43: { // Screen.removeMenu(menu: Menu)
UiScreen screen = (UiScreen) args[0];
UiMenu menu = (UiMenu) args[1];
screen.removeMenu(menu);
ui.screenMenuRemoved(screen, menu);
return null;
}
case 44: // uiReadEvent(): UIEvent
return UIServer.readEvent(p, false);
case 45: // uiWaitEvent(): UIEvent
return UIServer.readEvent(p, true);
case 46: // uiVibrate(millis: Int): Bool
return Ival(ui.vibrate(ival(args[0])));
case 47: // uiFlash(millis: Int): Bool
return Ival(ui.flash(ival(args[0])));
case 48: // uiGetScreen(): Screen
return UIServer.getScreen(p);
case 49: // uiSetScreen(scr: Screen)
UIServer.setScreen(p, args[0]);
return null;
case 50: // uiSetDefaultTitle(title: String)
p.setGlobal(null, "ui.title", (String)args[0]);
return null;
case 51: { // uiSetIcon(icon: Image)
p.setGlobal(null, "ui.icon", (UiImage)args[0]);
UIServer.displayCurrent();
return null;
}
/* == Header: canvas.eh == */
case 52: // Canvas.new(full: Bool = false): Canvas
return new UiCanvas(bval(args[0]));
case 53: // Canvas.graphics(): Graphics
return ((UiCanvas)args[0]).getGraphics();
case 54: // Canvas.repaint(x: Int, y: Int, w: Int, h: Int)
((UiCanvas)args[0]).repaint(ival(args[1]), ival(args[2]), ival(args[3]), ival(args[4]));
return null;
case 55: // Canvas.refresh()
((UiCanvas)args[0]).refresh();
return null;
case 56: // Canvas.actionCode(key: Int): Int
return Ival(((UiCanvas)args[0]).actionCode(ival(args[1])));
case 57: // Canvas.keyName(key: Int): String
return ((UiCanvas)args[0]).keyName(ival(args[1]));
case 58: // Canvas.hasPtrEvents(): Bool
return Ival(((UiCanvas)args[0]).hasPtrEvents());
case 59: // Canvas.hasPtrDragEvent(): Bool
return Ival(((UiCanvas)args[0]).hasPtrDragEvent());
case 60: // Canvas.hasHoldEvent(): Bool
return Ival(((UiCanvas)args[0]).hasHoldEvent());
/* == Header: msgbox.eh == */
case 61: // MsgBox.new(title: String, text: String = null, image: Image = null)
return new UiMsgBox((String)args[0], (String)args[1], (UiImage)args[2]);
case 62: // MsgBox.getText(): String
return ((UiMsgBox)args[0]).getText();
case 63: // MsgBox.setText(text: String)
((UiMsgBox)args[0]).setText((String)args[1]);
return null;
case 64: // MsgBox.getImage(): Image
return ((UiMsgBox)args[0]).getImage();
case 65: // MsgBox.setImage(img: Image)
((UiMsgBox)args[0]).setImage((UiImage)args[1]);
return null;
case 66: // MsgBox.getFont(): Int
return Ival(((UiMsgBox)args[0]).getFont());
case 67: // MsgBox.setFont(font: Int)
((UiMsgBox)args[0]).setFont(ival(args[1]));
return null;
/* == Header: editbox.eh == */
case 68: // EditBox.new(title: String, text: String = null): EditBox
return new UiEditBox((String)args[0], (String)args[1]);
case 69: // EditBox.getText(): String
return ((UiEditBox)args[0]).getText();
case 70: // EditBox.setText(text: String)
((UiEditBox)args[0]).setText((String)args[1]);
return null;
case 71: // EditBox.getMaxSize(): Int
return Ival(((UiEditBox)args[0]).getMaxSize());
case 72: // EditBox.setMaxSize(size: Int)
((UiEditBox)args[0]).setMaxSize(ival(args[1]));
return null;
case 73: // EditBox.getSize(): Int
return Ival(((UiEditBox)args[0]).getSize());
case 74: // EditBox.getCaret(): Int
return Ival(((UiEditBox)args[0]).getCaret());
/* == Header: listbox.eh == */
case 75: { // ListBox.new(title: String, strings: [String], images: [Image], select: Menu): ListBox
Object[] objStrings = (Object[])args[1];
String[] strings = null;
if (objStrings != null) {
strings = new String[objStrings.length];
System.arraycopy(objStrings, 0, strings, 0, objStrings.length);
}
Object[] objImages = (Object[])args[2];
UiImage[] images = null;
if (objImages != null) {
images = new UiImage[objImages.length];
System.arraycopy(objImages, 0, images, 0, objImages.length);
}
return new UiListBox((String)args[0], strings, images, (UiMenu)args[3]);
}
case 76: // ListBox.getIndex(): Int
return Ival(((UiListBox)args[0]).getIndex());
case 77: // ListBox.setIndex(index: Int)
((UiListBox)args[0]).setIndex(ival(args[1]));
return null;
case 78: // ListBox.add(str: String, img: Image = null)
((UiListBox)args[0]).add((String)args[1], (UiImage)args[2]);
return null;
case 79: // ListBox.insert(at: Int, str: String, img: Image = null)
((UiListBox)args[0]).insert(ival(args[1]), (String)args[2], (UiImage)args[3]);
return null;
case 80: // ListBox.set(at: Int, str: String, img: Image = null)
((UiListBox)args[0]).set(ival(args[1]), (String)args[2], (UiImage)args[3]);
return null;
case 81: // ListBox.delete(at: Int)
((UiListBox)args[0]).delete(ival(args[1]));
return null;
case 82: // ListBox.getString(at: Int): String
return ((UiListBox)args[0]).getString(ival(args[1]));
case 83: // ListBox.getImage(at: Int): Image
return ((UiListBox)args[0]).getImage(ival(args[1]));
case 84: // ListBox.getFont(at: Int): Font
return FontManager.getFontMask(((UiListBox)args[0]).getFont(ival(args[1])));
case 85: // ListBox.setFont(at: Int, font: Int)
((UiListBox)args[0]).setFont(ival(args[1]), FontManager.getFont(ival(args[2])));
return null;
case 86: // ListBox.clear()
((UiListBox)args[0]).clear();
return null;
case 87: // ListBox.len(): Int
return Ival(((UiListBox)args[0]).size());
default:
throw new RuntimeException("Not implemented");
}
}
}