/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set shiftwidth=4 tabstop=4 autoindent cindent expandtab: */ 'use strict'; (function(Native) { Native.create("com/sun/midp/lcdui/DisplayDeviceContainer.getDisplayDevicesIds0.()[I", function() { var ids = util.newPrimitiveArray("I", 1); ids[0] = 1; return ids; }); Native.create("com/sun/midp/lcdui/DisplayDevice.getDisplayName0.(I)Ljava/lang/String;", function(id) { return null; }); Native.create("com/sun/midp/lcdui/DisplayDevice.isDisplayPrimary0.(I)Z", function(id) { console.warn("DisplayDevice.isDisplayPrimary0.(I)Z not implemented (" + id + ")"); return true; }); Native.create("com/sun/midp/lcdui/DisplayDevice.isbuildInDisplay0.(I)Z", function(id) { return true; }); Native.create("com/sun/midp/lcdui/DisplayDevice.getDisplayCapabilities0.(I)I", function(id) { return 0x3ff; }); Native.create("com/sun/midp/lcdui/DisplayDevice.isDisplayPenSupported0.(I)Z", function(id) { return true; }); Native.create("com/sun/midp/lcdui/DisplayDevice.isDisplayPenMotionSupported0.(I)Z", function(id) { return true; }); Native.create("com/sun/midp/lcdui/DisplayDevice.getReverseOrientation0.(I)Z", function(id) { return false; }); Native.create("com/sun/midp/lcdui/DisplayDevice.getScreenWidth0.(I)I", function(id) { return MIDP.Context2D.canvas.width; }); Native.create("com/sun/midp/lcdui/DisplayDevice.getScreenHeight0.(I)I", function(id) { return MIDP.Context2D.canvas.height; }); Native.create("com/sun/midp/lcdui/DisplayDevice.displayStateChanged0.(II)V", function(hardwareId, state) { console.warn("DisplayDevice.displayStateChanged0.(II)V not implemented (" + hardwareId + ", " + state + ")"); }); Native.create("com/sun/midp/lcdui/DisplayDevice.setFullScreen0.(IIZ)V", function(hardwareId, displayId, mode) { console.warn("DisplayDevice.setFullScreen0.(IIZ)V not implemented (" + hardwareId + ", " + displayId + ", " + mode + ")"); }); Native.create("com/sun/midp/lcdui/DisplayDevice.gainedForeground0.(II)V", function(hardwareId, displayId) { console.warn("DisplayDevice.gainedForeground0.(II)V not implemented (" + hardwareId + ", " + displayId + ")"); }); Native.create("com/sun/midp/lcdui/DisplayDeviceAccess.vibrate0.(IZ)Z", function(displayId, on) { return true; }); Native.create("com/sun/midp/lcdui/DisplayDeviceAccess.isBacklightSupported0.(I)Z", function(displayId) { return true; }); Native.create("com/sun/midp/lcdui/DisplayDevice.refresh0.(IIIIII)V", function(hardwareId, displayId, x1, y1, x2, y2) { console.warn("DisplayDevice.refresh0.(IIIIII)V not implemented (" + hardwareId + ", " + displayId + ", " + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")"); }); function swapRB(pixel) { return (pixel & 0xff00ff00) | ((pixel >> 16) & 0xff) | ((pixel & 0xff) << 16); } function swapRBAndSetAlpha(pixel) { return swapRB(pixel) | 0xff000000; } /** * Extract the image data from `context` and place it in `rgbData`. */ function contextToRgbData(context, rgbData, offset, scanlength, x, y, width, height, converterFunc) { var pixels = new Uint32Array(context.getImageData(x, y, width, height).data.buffer); var i = 0; for (var y1 = y; y1 < y + height; y1++) { for (var x1 = x; x1 < x + width; x1++) { rgbData[offset + (x1 - x) + (y1 - y) * scanlength] = converterFunc(pixels[i++]); } } } /** * Insert `rgbData` into `context`. */ function rgbDataToContext(context, rgbData, offset, scanlength, converterFunc) { var width = context.canvas.width; var height = context.canvas.height; var imageData = context.createImageData(width, height); var pixels = new Uint32Array(imageData.data.buffer); var i = 0; for (var y = 0; y < height; ++y) { var j = offset + y * scanlength; for (var x = 0; x < width; ++x) { pixels[i++] = converterFunc(rgbData[j++]); } } context.putImageData(imageData, 0, 0); } function createContext2d(width, height) { var canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; return canvas.getContext("2d"); } function setImageData(imageData, width, height, data) { imageData.class.getField("I.width.I").set(imageData, width); imageData.class.getField("I.height.I").set(imageData, height); imageData.nativeImageData = data; } /** * Ensure the nativeImageData of the given image points to a * Canvas Context2D, converting (and saving) it if necessary. * * @return {CanvasRenderingContext2D} context */ function convertNativeImageData(imageData) { var data = imageData.nativeImageData; if (!(data instanceof CanvasRenderingContext2D)) { // Assume it's an image. var context = createContext2d(data.width, data.height); context.drawImage(data, 0, 0); imageData.nativeImageData = data = context; } return data; } Native.create("javax/microedition/lcdui/ImageDataFactory.createImmutableImageDecodeImage.(Ljavax/microedition/lcdui/ImageData;[BII)V", function(imageData, bytes, offset, length) { return new Promise(function(resolve, reject) { var blob = new Blob([bytes.subarray(offset, offset + length)], { type: "image/png" }); var img = new Image(); img.src = URL.createObjectURL(blob); img.onload = function() { setImageData(imageData, img.naturalWidth, img.naturalHeight, img); resolve(); } img.onerror = function(e) { reject(new JavaException("java/lang/IllegalArgumentException", "error decoding image")); } }); }); Native.create("javax/microedition/lcdui/ImageDataFactory.createMutableImageData.(Ljavax/microedition/lcdui/ImageData;II)V", function(imageData, width, height) { var context = createContext2d(width, height); context.fillStyle = "rgb(255,255,255)"; // white context.fillRect(0, 0, width, height); setImageData(imageData, width, height, context); }); Native.create("javax/microedition/lcdui/ImageDataFactory.createImmutableImageDecodeRGBImage.(Ljavax/microedition/lcdui/ImageData;[IIIZ)V", function(imageData, rgbData, width, height, processAlpha) { var context = createContext2d(width, height); rgbDataToContext(context, rgbData, 0, width, processAlpha ? swapRB : swapRBAndSetAlpha); setImageData(imageData, width, height, context); }); Native.create("javax/microedition/lcdui/ImageData.getRGB.([IIIIIII)V", function(rgbData, offset, scanlength, x, y, width, height) { contextToRgbData(convertNativeImageData(this), rgbData, offset, scanlength, x, y, width, height, swapRB); }); Native.create("com/nokia/mid/ui/DirectUtils.makeMutable.(Ljavax/microedition/lcdui/Image;)V", function(image) { var imageData = image.class.getField("I.imageData.Ljavax/microedition/lcdui/ImageData;").get(image); imageData.class.getField("I.isMutable.Z").set(imageData, 1); }); Native.create("com/nokia/mid/ui/DirectUtils.setPixels.(Ljavax/microedition/lcdui/Image;I)V", function(image, argb) { var width = image.class.getField("I.width.I").get(image); var height = image.class.getField("I.height.I").get(image); var imageData = image.class.getField("I.imageData.Ljavax/microedition/lcdui/ImageData;").get(image); var ctx = createContext2d(width, height); setImageData(imageData, width, height, ctx); var ctxImageData = ctx.createImageData(width, height); var pixels = new Uint32Array(ctxImageData.data.buffer); var color = swapRB(argb); var i = 0; for (var y = 0; y < height; ++y) { for (var x = 0; x < width; ++x) { pixels[i++] = color; } } ctx.putImageData(ctxImageData, 0, 0); }); var FACE_SYSTEM = 0; var FACE_MONOSPACE = 32; var FACE_PROPORTIONAL = 64; var STYLE_PLAIN = 0; var STYLE_BOLD = 1; var STYLE_ITALIC = 2; var STYLE_UNDERLINED = 4; var SIZE_SMALL = 8; var SIZE_MEDIUM = 0; var SIZE_LARGE = 16; Native.create("javax/microedition/lcdui/Font.init.(III)V", function(face, style, size) { var defaultSize = Math.max(10, (MIDP.Context2D.canvas.height / 48) | 0); if (size & SIZE_SMALL) size = defaultSize / 1.25; else if (size & SIZE_LARGE) size = defaultSize * 1.25; else size = defaultSize; size |= 0; if (style & STYLE_BOLD) style = "bold"; else if (style & STYLE_ITALIC) style = "italic"; else style = ""; if (face & FACE_MONOSPACE) face = "monospace"; else if (face & FACE_PROPORTIONAL) face = "sans-serif"; else face = "Arial, Helvetica, sans-serif"; this.class.getField("I.baseline.I").set(this, (size/2)|0); this.class.getField("I.height.I").set(this, (size * 1.3)|0); this.css = style + " " + size + "pt " + face; }); Native.create("javax/microedition/lcdui/Font.stringWidth.(Ljava/lang/String;)I", function(str) { return withFont(this, MIDP.Context2D, util.fromJavaString(str)); }); Native.create("javax/microedition/lcdui/Font.charWidth.(C)I", function(char) { return withFont(this, MIDP.Context2D, String.fromCharCode(char)); }); Native.create("javax/microedition/lcdui/Font.charsWidth.([CII)I", function(str, offset, len) { return withFont(this, MIDP.Context2D, util.fromJavaChars(str).slice(offset, offset + len)); }); Native.create("javax/microedition/lcdui/Font.substringWidth.(Ljava/lang/String;II)I", function(str, offset, len) { return withFont(this, MIDP.Context2D, util.fromJavaString(str).slice(offset, offset + len)); }); var HCENTER = 1; var VCENTER = 2; var LEFT = 4; var RIGHT = 8; var TOP = 16; var BOTTOM = 32; var BASELINE = 64; function withGraphics(g, cb) { var img = g.class.getField("I.img.Ljavax/microedition/lcdui/Image;").get(g), c = null; if (img === null) { c = MIDP.Context2D; } else { var imgData = img.class.getField("I.imageData.Ljavax/microedition/lcdui/ImageData;").get(img), c = imgData.nativeImageData; } cb(c); } function withClip(g, c, x, y, cb) { var clipX1 = g.class.getField("I.clipX1.S").get(g), clipY1 = g.class.getField("I.clipY1.S").get(g), clipX2 = g.class.getField("I.clipX2.S").get(g), clipY2 = g.class.getField("I.clipY2.S").get(g), clipped = g.class.getField("I.clipped.Z").get(g), transX = g.class.getField("I.transX.I").get(g), transY = g.class.getField("I.transY.I").get(g); c.save(); if (clipped) { c.beginPath(); c.rect(clipX1, clipY1, clipX2 - clipX1, clipY2 - clipY1); c.clip(); } c.translate(transX, transY); cb(x, y); c.restore(); } function withAnchor(g, c, anchor, x, y, w, h, cb) { withClip(g, c, x, y, function(x, y) { if (anchor & RIGHT) x -= w; if (anchor & HCENTER) x -= (w/2)|0; if (anchor & BOTTOM) y -= h; if (anchor & VCENTER) y -= (h/2)|0; cb(x, y); }); } function withFont(font, c, str) { c.font = font.css; return c.measureText(str).width | 0; } function withTextAnchor(g, c, anchor, x, y, str, cb) { withClip(g, c, x, y, function(x, y) { var w = withFont(g.class.getField("I.currentFont.Ljavax/microedition/lcdui/Font;").get(g), c, str); c.textAlign = "left"; c.textBaseline = "top"; if (anchor & RIGHT) x -= w; if (anchor & HCENTER) x -= (w/2)|0; if (anchor & BOTTOM) c.textBaseline = "bottom"; if (anchor & VCENTER) c.textBaseline = "middle"; if (anchor & BASELINE) c.textBaseline = "alphabetic"; cb(x, y, w); }); } function abgrIntToCSS(pixel) { var a = (pixel >> 24) & 0xff; var b = (pixel >> 16) & 0xff; var g = (pixel >> 8) & 0xff; var r = pixel & 0xff; return "rgba(" + r + "," + g + "," + b + "," + (a/255) + ")"; }; function withPixel(g, c, cb) { var pixel = g.class.getField("I.pixel.I").get(g); c.save(); c.fillStyle = c.strokeStyle = abgrIntToCSS(pixel); cb(); c.restore(); } /** * create the outline of an elliptical arc * covering the specified rectangle. * @param x the x-coordinate of the center of the ellipse. * @param y y-coordinate of the center of the ellipse. * @param rw the horizontal radius of the arc. * @param rh the vertical radius of the arc. * @param arcStart the beginning angle * @param arcEnd the ending angle * @param closed if true, draw a closed arc sector. */ function createEllipticalArc(c, x, y, rw, rh, arcStart, arcEnd, closed) { c.save(); c.translate(x, y); if (closed) { c.moveTo(0, 0); } // draw circle arc which will be stretched into an oval arc c.scale(1, rh / rw); c.arc(0, 0, rw, arcStart, arcEnd, false); if (closed) { c.lineTo(0, 0); } c.restore(); } /** * Create a round rectangle path. * @param x the x coordinate of the rectangle * @param y the y coordinate of the rectangle * @param width the width of the rectangle * @param height the height of the rectangle * @param arcWidth the horizontal diameter of the arc at the four corners * @param arcHeight the vertical diameter of the arc at the four corners */ function createRoundRect(c, x, y, width, height, arcWidth, arcHeight) { var rw = arcWidth / 2; var rh = arcHeight / 2; c.moveTo(x + rw, y); c.lineTo(x + width - rw, y); createEllipticalArc(c, x + width - rw, y + rh, rw, rh, 1.5 * Math.PI, 2 * Math.PI, false); c.lineTo(x + width, y + height - rh); createEllipticalArc(c, x + width - rw, y + height - rh, rw, rh, 0, 0.5 * Math.PI, false); c.lineTo(x + rw, y + height); createEllipticalArc(c, x + rw, y + height - rh, rw, rh, 0.5 * Math.PI, Math.PI, false); c.lineTo(x, y + rh); createEllipticalArc(c, x + rw, y + rh, rw, rh, Math.PI, 1.5 * Math.PI, false); } /** * Like withPixel, but ignores alpha channel, setting the alpha value to 1. * Useful when you suspect that the caller is specifying the alpha channel * incorrectly, although we should actually figure out why that's happening. */ function withOpaquePixel(g, c, cb) { var pixel = g.class.getField("I.pixel.I").get(g); c.save(); var b = (pixel >> 16) & 0xff; var g = (pixel >> 8) & 0xff; var r = pixel & 0xff; var style = "rgba(" + r + "," + g + "," + b + "," + 1 + ")"; c.fillStyle = c.strokeStyle = style; cb(); c.restore(); } function withSize(dx, dy, cb) { if (!dx) dx = 1; if (!dy) dy = 1; cb(dx, dy); } function renderImage(graphics, image, x, y, anchor) { var texture = image.class.getField("I.imageData.Ljavax/microedition/lcdui/ImageData;").get(image).nativeImageData; if (!texture) { console.warn("Graphics.render: image missing native data"); return; } if (texture instanceof CanvasRenderingContext2D) { texture = texture.canvas; // Render the canvas, not the context. } withGraphics(graphics, function(c) { withAnchor(graphics, c, anchor, x, y, texture.width, texture.height, function(x, y) { c.drawImage(texture, x, y); }); }); } Override.create("com/sun/midp/chameleon/CGraphicsUtil.draw9pcsBackground.(Ljavax/microedition/lcdui/Graphics;IIII[Ljavax/microedition/lcdui/Image;)V", function(g, x, y, w, h, image) { if (image == null || image.length != 9) { return; } var transX = g.class.getField("I.transX.I"); var transY = g.class.getField("I.transY.I"); transX.set(g, transX.get(g) + x); transY.set(g, transY.get(g) + y); // Top Border var iW = image[1].class.getField("I.width.I").get(image[1]); renderImage(g, image[0], 0, 0, LEFT | TOP); w -= image[2].class.getField("I.width.I").get(image[2]); for (var i = image[0].class.getField("I.width.I").get(image[0]); i < w; i += iW) { renderImage(g, image[1], i, 0, LEFT | TOP); } w += image[2].class.getField("I.width.I").get(image[2]); renderImage(g, image[2], w, 0, RIGHT | TOP); // Tile middle rows if (image[4] != null) { iW = image[4].class.getField("I.width.I").get(image[4]); } var iH = image[3].class.getField("I.height.I").get(image[3]); h -= image[6].class.getField("I.height.I").get(image[6]); w -= image[5].class.getField("I.width.I").get(image[5]); for (var i = image[0].class.getField("I.height.I").get(image[0]); i <= h; i += iH) { renderImage(g, image[3], 0, i, LEFT | TOP); for (var j = image[3].class.getField("I.width.I").get(image[3]); j <= w; j += iW) { renderImage(g, image[4], j, i, LEFT | TOP); } renderImage(g, image[5], w + image[5].class.getField("I.width.I").get(image[5]), i, RIGHT | TOP); } w += image[5].class.getField("I.width.I").get(image[5]); h += image[6].class.getField("I.height.I").get(image[6]); // Bottom border iW = image[7].class.getField("I.width.I").get(image[7]); renderImage(g, image[6], 0, h, LEFT | BOTTOM); w -= image[8].class.getField("I.width.I").get(image[8]); for (var i = image[6].class.getField("I.width.I").get(image[6]); i < w; i += iW) { renderImage(g, image[7], i, h, LEFT | BOTTOM); } w += image[8].class.getField("I.width.I").get(image[8]); renderImage(g, image[8], w, h, RIGHT | BOTTOM); transX.set(g, transX.get(g) - x); transY.set(g, transY.get(g) - y); }); Native.create("javax/microedition/lcdui/Graphics.getDisplayColor.(I)I", function(color) { return color; }); Native.create("javax/microedition/lcdui/Graphics.getPixel.(IIZ)I", function(rgb, gray, isGray) { return swapRB(rgb) | 0xff000000; }); Native.create("com/nokia/mid/ui/DirectGraphicsImp.setARGBColor.(I)V", function(rgba) { var g = this.class.getField("I.graphics.Ljavax/microedition/lcdui/Graphics;").get(this); var red = (rgba >> 16) & 0xff; var green = (rgba >> 8) & 0xff; var blue = rgba & 0xff; g.class.getField("I.pixel.I").set(g, swapRB(rgba)); g.class.getField("I.rgbColor.I").set(g, rgba & 0x00ffffff); // Conversion matches Graphics#grayVal(int, int, int). g.class.getField("I.gray.I").set(g, (red * 76 + green * 150 + blue * 29) >> 8); }); Native.create("com/nokia/mid/ui/DirectGraphicsImp.getAlphaComponent.()I", function() { var g = this.class.getField("I.graphics.Ljavax/microedition/lcdui/Graphics;").get(this); var pixel = g.class.getField("I.pixel.I").get(g); return (pixel >> 24) & 0xff; }); Native.create("com/nokia/mid/ui/DirectGraphicsImp.getPixels.([SIIIIIII)V", function(pixels, offset, scanlength, x, y, width, height, format) { if (pixels == null) { throw new JavaException("java/lang/NullPointerException", "Pixels array is null"); } var converterFunc = null; if (format == 4444) { // TYPE_USHORT_4444_ARGB converterFunc = function(rgba) { var r = (rgba & 0xF0000000) >>> 20; var g = (rgba & 0x00F00000) >> 16; var b = (rgba & 0x0000F000) >> 12; var a = (rgba & 0x000000F0) << 8; return (a | r | g | b); }; } else if (format == 565) { // TYPE_USHORT_565_RGB converterFunc = function(rgba) { var r = (rgba & 0xF8000000) >>> 16; var g = (rgba & 0xFC0000) >>> 13; var b = (rgba & 0xF800) >>> 11; return (r | g | b); }; } else { throw new JavaException("java/lang/IllegalArgumentException", "Format unsupported"); } var graphics = this.class.getField("I.graphics.Ljavax/microedition/lcdui/Graphics;").get(this); var image = graphics.class.getField("I.img.Ljavax/microedition/lcdui/Image;").get(graphics); var imageData = image.class.getField("I.imageData.Ljavax/microedition/lcdui/ImageData;").get(image); contextToRgbData(convertNativeImageData(imageData), pixels, offset, scanlength, x, y, width, height, converterFunc); }); Native.create("com/nokia/mid/ui/DirectGraphicsImp.drawPixels.([SZIIIIIIII)V", function(pixels, transparency, offset, scanlength, x, y, width, height, manipulation, format) { if (pixels == null) { throw new JavaException("java/lang/NullPointerException", "Pixels array is null"); } var converterFunc = null; if (format == 4444 && transparency && !manipulation) { converterFunc = function(argb) { var a = (argb & 0xF000) >>> 8; var r = (argb & 0x0F00) << 20; var g = (argb & 0x00F0) << 16; var b = (argb & 0x000F) << 12; return (r | g | b | a); }; } else { throw new JavaException("java/lang/IllegalArgumentException", "Format unsupported"); } var graphics = this.class.getField("I.graphics.Ljavax/microedition/lcdui/Graphics;").get(this); var context = createContext2d(width, height); rgbDataToContext(context, pixels, offset, scanlength, converterFunc); withGraphics(graphics, function(c) { withClip(graphics, c, x, y, function(x, y) { c.drawImage(context.canvas, x, y); }); }); }); Native.create("javax/microedition/lcdui/Graphics.render.(Ljavax/microedition/lcdui/Image;III)Z", function(image, x, y, anchor) { renderImage(this, image, x, y, anchor); return true; }); Native.create("javax/microedition/lcdui/Graphics.drawString.(Ljava/lang/String;III)V", function(jStr, x, y, anchor) { var str = util.fromJavaString(jStr); var g = this; withGraphics(g, function(c) { withTextAnchor(g, c, anchor, x, y, str, function(x, y) { withOpaquePixel(g, c, function() { c.fillText(str, x, y); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawChars.([CIIIII)V", function(data, offset, len, x, y, anchor) { var str = util.fromJavaChars(data, offset, len); var g = this; withGraphics(g, function(c) { withTextAnchor(g, c, anchor, x, y, str, function(x, y) { withPixel(g, c, function() { c.fillText(str, x, y); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawChar.(CIII)V", function(jChr, x, y, anchor) { var chr = String.fromCharCode(jChr); var g = this; withGraphics(g, function(c) { withTextAnchor(g, c, anchor, x, y, chr, function(x, y) { withPixel(g, c, function() { c.fillText(chr, x, y); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.fillTriangle.(IIIIII)V", function(x1, y1, x2, y2, x3, y3) { var g = this; withGraphics(g, function(c) { withClip(g, c, x1, y1, function(x, y) { withPixel(g, c, function() { withSize(x2 - x1, y2 - y1, function(dx1, dy1) { withSize(x3 - x1, y3 - y1, function(dx2, dy2) { c.beginPath(); c.moveTo(x, y); c.lineTo(x + dx1, y + dy1); c.lineTo(x + dx2, y + dy2); c.closePath(); c.fill(); }); }); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawRect.(IIII)V", function(x, y, w, h) { if (w < 0 || h < 0) { return; } var g = this; withGraphics(g, function(c) { withClip(g, c, x, y, function(x, y) { withPixel(g, c, function() { withSize(w, h, function(w, h) { c.strokeRect(x, y, w, h); }); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawRoundRect.(IIIIII)V", function(x, y, w, h, arcWidth, arcHeight) { if (w < 0 || h < 0) { return; } var g = this; withGraphics(g, function(c) { withClip(g, c, x, y, function(x, y) { withPixel(g, c, function() { withSize(w, h, function(w, h) { c.beginPath(); createRoundRect(c, x, y, w, h, arcWidth, arcHeight); c.stroke(); }); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.fillRect.(IIII)V", function(x, y, w, h) { if (w <= 0 || h <= 0) { return; } var g = this; withGraphics(g, function(c) { withClip(g, c, x, y, function(x, y) { withPixel(g, c, function() { withSize(w, h, function(w, h) { c.fillRect(x, y, w, h); }); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.fillRoundRect.(IIIIII)V", function(x, y, w, h, arcWidth, arcHeight) { if (w <= 0 || h <= 0) { return; } var g = this; withGraphics(g, function(c) { withClip(g, c, x, y, function(x, y) { withPixel(g, c, function() { withSize(w, h, function(w, h) { c.beginPath(); createRoundRect(c, x, y, w, h, arcWidth, arcHeight); c.fill(); }); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawArc.(IIIIII)V", function(x, y, width, height, startAngle, arcAngle) { if (width < 0 || height < 0) { return; } var g = this; withGraphics(g, function(c) { withPixel(g, c, function() { var endRad = -startAngle * 0.0175; var startRad = endRad - arcAngle * 0.0175; c.beginPath(); createEllipticalArc(c, x, y, width / 2, height / 2, startRad, endRad, false); c.stroke(); }); }); }); Native.create("javax/microedition/lcdui/Graphics.fillArc.(IIIIII)V", function(x, y, width, height, startAngle, arcAngle) { if (width <= 0 || height <= 0) { return; } var g = this; withGraphics(g, function(c) { withPixel(g, c, function() { var endRad = -startAngle * 0.0175; var startRad = endRad - arcAngle * 0.0175; c.beginPath(); c.moveTo(x, y); createEllipticalArc(c, x, y, width / 2, height / 2, startRad, endRad, true); c.moveTo(x, y); c.fill(); }); }); }); var TRANS_NONE = 0; var TRANS_MIRROR_ROT180 = 1; var TRANS_MIRROR = 2; var TRANS_ROT180 = 3; var TRANS_MIRROR_ROT270 = 4; var TRANS_ROT90 = 5; var TRANS_ROT270 = 6; var TRANS_MIRROR_ROT90 = 7; Override.create("javax/microedition/lcdui/Graphics.drawRegion.(Ljavax/microedition/lcdui/Image;IIIIIIII)V", function(image, sx, sy, sw, sh, transform, x, y, anchor) { if (!image) { throw new JavaException("java/lang/NullPointerException", "src image is null"); } var imgData = image.class.getField("I.imageData.Ljavax/microedition/lcdui/ImageData;").get(image), texture = imgData.nativeImageData; if (texture instanceof CanvasRenderingContext2D) { texture = texture.canvas; // Render the canvas, not the context. } var w = sw, h = sh; var g = this; withGraphics(g, function(c) { withAnchor(g, c, anchor, x, y, w, h, function(x, y) { c.translate(x, y); if (transform === TRANS_MIRROR || transform === TRANS_MIRROR_ROT180) c.scale(-1, 1); if (transform === TRANS_MIRROR_ROT90 || transform === TRANS_MIRROR_ROT270) c.scale(1, -1); if (transform === TRANS_ROT90 || transform === TRANS_MIRROR_ROT90) c.rotate(Math.PI / 2); if (transform === TRANS_ROT180 || transform === TRANS_MIRROR_ROT180) c.rotate(Math.PI); if (transform === TRANS_ROT270 || transform === TRANS_MIRROR_ROT270) c.rotate(1.5 * Math.PI); c.drawImage(texture, sx, sy, w, h, 0, 0, sw, sh); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawLine.(IIII)V", function(x1, y1, x2, y2) { var dx = x2 - x1, dy = y2 - y1; var g = this; withGraphics(g, function(c) { withClip(g, c, x1, y1, function(x, y) { withSize(dx, dy, function(dx, dy) { withPixel(g, c, function() { var ctx = MIDP.Context2D; ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + dx, y + dy); ctx.stroke(); ctx.closePath(); }); }); }); }); }); Native.create("javax/microedition/lcdui/Graphics.drawRGB.([IIIIIIIZ)V", function(rgbData, offset, scanlength, x, y, width, height, processAlpha) { var context = createContext2d(width, height); rgbDataToContext(context, rgbData, offset, scanlength, processAlpha ? swapRB : swapRBAndSetAlpha); var g = this; withGraphics(g, function(c) { withClip(g, c, x, y, function(x, y) { c.drawImage(context.canvas, x, y); }); }); }); var textEditorId = 0, textEditorResolve = null, dirtyEditors = []; function wakeTextEditorThread(id) { dirtyEditors.push(id); if (textEditorResolve) { textEditorResolve(); textEditorResolve = null; } } Native.create("com/nokia/mid/ui/TextEditor.init.(Ljava/lang/String;IIII)I", function(text, maxSize, constraints, width, height) { if (constraints != 0) { console.warn("TextEditor.constraints not implemented"); } this.textEditorId = ++textEditorId; this.textEditor = document.createElement("textarea"); this.visible = false; this.focused = false; this.backgroundColor = 0xFFFFFFFF | 0; // opaque white this.foregroundColor = 0xFF000000 | 0; // opaque black this.textEditor.style.border = "none"; this.textEditor.style.resize = "none"; this.textEditor.style.backgroundColor = abgrIntToCSS(this.backgroundColor); this.textEditor.style.color = abgrIntToCSS(this.foregroundColor); this.getCaretPosition = function() { if (this.textEditor.parentNode) { return this.textEditor.selectionStart; } if (this.caretPosition !== null) { return this.caretPosition; } return 0; }; this.setCaretPosition = function(index) { if (this.textEditor.parentNode) { this.textEditor.setSelectionRange(index, index); } else { this.caretPosition = index; } }; this.textEditor.value = util.fromJavaString(text); this.setCaretPosition(this.textEditor.value.length); this.textEditor.setAttribute("maxlength", maxSize); this.textEditor.style.width = width + "px"; this.textEditor.style.height = height + "px"; this.textEditor.style.position = "absolute"; this.textEditor.style.visibility = "hidden"; this.textEditor.oninput = function(e) { wakeTextEditorThread(this.textEditorId); }.bind(this); return textEditorId; }); Native.create("com/nokia/mid/ui/CanvasItem.attachNativeImpl.()V", function() { document.getElementById("display").appendChild(this.textEditor); if (this.caretPosition !== null) { this.textEditor.setSelectionRange(this.caretPosition, this.caretPosition); this.caretPosition = null; } }); Native.create("com/nokia/mid/ui/CanvasItem.detachNativeImpl.()V", function() { this.caretPosition = this.textEditor.selectionStart; document.getElementById("display").removeChild(this.textEditor); }); Native.create("com/nokia/mid/ui/CanvasItem.setSize.(II)V", function(width, height) { this.textEditor.style.width = width + "px"; this.textEditor.style.height = height + "px"; }); Native.create("com/nokia/mid/ui/CanvasItem.setVisible.(Z)V", function(visible) { if (visible && !this.visible) { this.textEditor.style.visibility = "visible"; } else if (!visible && this.visible) { this.textEditor.style.visibility = "hidden"; } this.visible = visible; }); Native.create("com/nokia/mid/ui/CanvasItem.getWidth.()I", function() { return parseInt(this.textEditor.style.width); }); Native.create("com/nokia/mid/ui/CanvasItem.getHeight.()I", function() { return parseInt(this.textEditor.style.height); }); Native.create("com/nokia/mid/ui/CanvasItem.setPosition.(II)V", function(x, y) { this.textEditor.style.left = x + "px"; this.textEditor.style.top = y + "px"; }); Native.create("com/nokia/mid/ui/CanvasItem.getPositionX.()I", function() { return parseInt(this.textEditor.style.left) || 0; }); Native.create("com/nokia/mid/ui/CanvasItem.getPositionY.()I", function() { return parseInt(this.textEditor.style.top) || 0; }); Native.create("com/nokia/mid/ui/CanvasItem.isVisible.()Z", function() { return this.visible; }); Native.create("com/nokia/mid/ui/TextEditor.setFocus.(Z)V", function(focused) { if (focused && !this.focused) { this.textEditor.focus(); } else if (!focused && this.focused) { this.textEditor.blur(); } this.focused = focused; }); Native.create("com/nokia/mid/ui/TextEditor.hasFocus.()Z", function() { return this.focused; }); Native.create("com/nokia/mid/ui/TextEditor.setCaret.(I)V", function(index) { this.setCaretPosition(index); }); Native.create("com/nokia/mid/ui/TextEditor.getCaretPosition.()I", function() { return this.getCaretPosition(); }); Native.create("com/nokia/mid/ui/TextEditor.getBackgroundColor.()I", function() { return this.backgroundColor; }); Native.create("com/nokia/mid/ui/TextEditor.getForegroundColor.()I", function() { return this.foregroundColor; }); Native.create("com/nokia/mid/ui/TextEditor.setBackgroundColor.(I)V", function(backgroundColor) { this.backgroundColor = backgroundColor; this.textEditor.style.backgroundColor = abgrIntToCSS(backgroundColor); }); Native.create("com/nokia/mid/ui/TextEditor.setForegroundColor.(I)V", function(foregroundColor) { this.foregroundColor = foregroundColor; this.textEditor.style.color = abgrIntToCSS(foregroundColor); }); Native.create("com/nokia/mid/ui/TextEditor.getContent.()Ljava/lang/String;", function() { return this.textEditor.value; }); Native.create("com/nokia/mid/ui/TextEditor.setContent.(Ljava/lang/String;)V", function(jStr) { var str = util.fromJavaString(jStr); this.textEditor.value = str; this.setCaretPosition(str.length); }); Native.create("com/nokia/mid/ui/TextEditor.insert.(Ljava/lang/String;I)V", function(jStr, pos) { var old = this.textEditor.value; var str = util.fromJavaString(jStr); this.textEditor.value = old.slice(0, pos) + str + old.slice(pos); this.setCaretPosition(pos + str.length); }); Native.create("com/nokia/mid/ui/TextEditor.delete.(II)V", function(offset, length) { var old = this.textEditor.value; if (offset < 0 || offset > old.length || length < 0 || offset + length > old.length) { throw new JavaException("java.lang.StringIndexOutOfBoundsException", "offset/length invalid"); } this.textEditor.value = old.slice(0, offset) + old.slice(offset + length); this.setCaretPosition(offset); }); Native.create("com/nokia/mid/ui/TextEditor.getMaxSize.()I", function() { return parseInt(this.textEditor.getAttribute("maxlength")); }); Native.create("com/nokia/mid/ui/TextEditor.setMaxSize.(I)I", function(maxSize) { if (this.textEditor.value.length > maxSize) { this.textEditor.value = this.textEditor.value.substring(0, maxSize); if (this.getCaretPosition() > maxSize) { this.setCaretPosition(maxSize); } } this.textEditor.setAttribute("maxlength", maxSize); // The return value is the assigned size, which could be less than // the size that was requested, although in this case we always set it // to the requested size. return maxSize; }); Native.create("com/nokia/mid/ui/TextEditor.size.()I", function() { return this.textEditor.value.length; }); Native.create("com/nokia/mid/ui/TextEditorThread.sleep.()V", function() { return new Promise(function(resolve, reject) { if (!dirtyEditors.length) { textEditorResolve = resolve; } }); }); Native.create("com/nokia/mid/ui/TextEditorThread.getNextDirtyEditor.()I", function() { if (!dirtyEditors.length) { console.error("ERROR: getNextDirtyEditor called but no dirty editors"); return 0; } return dirtyEditors.shift(); }); })(Native);