/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.sun.lwuit.impl;
import com.sun.lwuit.*;
import com.sun.lwuit.animations.Animation;
import com.sun.lwuit.geom.Dimension;
import com.sun.lwuit.geom.Rectangle;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* Represents a vendor extension mechanizm for LWUIT, <b>WARNING: this class is for internal
* use only and is subject to change in future API revisions</b>. To replace the way in which
* LWUIT performs its task this class can be extended and its functionality replaced or
* enhanced.
* <p>It is the responsibility of the implementation class to grab and fire all events to the
* Display specifically for key, pointer events and screen resolution.
*
* @author Shai Almog
*/
public abstract class LWUITImplementation {
/**
* Indicates the range of "hard" RTL bidi characters in unicode
*/
private static final char RTL_RANGE_BEGIN = 0x590;
private static final char RTL_RANGE_END = 0x7BF;
private Object lightweightClipboard;
private Hashtable linearGradientCache;
private Hashtable radialGradientCache;
private boolean builtinSoundEnabled = true;
private int dragActivationCounter = 0;
private int dragActivationX = 0;
private int dragActivationY = 0;
private int dragStartPercentage = 3;
private Form currentForm;
private static Object displayLock;
private Animation[] paintQueue = new Animation[50];
private Animation[] paintQueueTemp = new Animation[50];
private int paintQueueFill = 0;
private Graphics lwuitGraphics;
private static boolean bidi;
/**
* Useful since the content of a single element touch event is often recycled
* and always arrives on 1 thread. Even on multi-tocuh devices a single coordinate
* touch event should be very efficient
*/
private int[] xPointerEvent = new int[1];
/**
* Useful since the content of a single element touch event is often recycled
* and always arrives on 1 thread. Even on multi-tocuh devices a single coordinate
* touch event should be very efficient
*/
private int[] yPointerEvent = new int[1];
private int pointerPressedX;
private int pointerPressedY;
private Hashtable builtinSounds = new Hashtable();
/**
* Invoked by the display init method allowing the implementation to "bind"
*
* @param m the object passed to the Display init method
*/
public abstract void init(Object m);
/**
* Some implementations might need to perform initializations of the EDT thread
*/
public void initEDT() {
}
/**
* Allows subclasses to cleanup if necessary
*/
public void deinitialize() {}
/**
* Invoked when a dialog is shown, this method allows a dialog to play a sound
*
* @param type the type of the dialog matching the dialog classes defined types
*/
public void playDialogSound(final int type) {
}
/**
* Vibrates the device for the given length of time
*
* @param duration length of time to vibrate
*/
public void vibrate(int duration) {
}
/**
* Flash the backlight of the device for the given length of time
*
* @param duration length of time to flash the backlight
*/
public void flashBacklight(int duration) {
}
/**
* Returns the width dimention of the display controlled by this implementation
*
* @return the width
*/
public abstract int getDisplayWidth();
/**
* Returns the height dimention of the display controlled by this implementation
*
* @return the height
*/
public abstract int getDisplayHeight();
/**
* Invoked when an exception occurs on the EDT, allows the implementation to
* take control of the device to produce testing information.
*
* @param err the exception that was caught in the EDT loop
* @return false by default, true if the exception shouldn't be handled further
* by the EDT
*/
public boolean handleEDTException(Throwable err) {
return false;
}
/**
* Encapsulates the editing code which is specific to the platform, some platforms
* would allow "in place editing" MIDP does not.
*
* @param cmp the {@link TextArea} component
* @param maxSize the maximum size from the text area
* @param constraint the constraints of the text area
* @param text the string to edit
* @param initiatingKeycode the keycode used to initiate the edit.
*/
public abstract void editString(Component cmp, int maxSize, int constraint, String text, int initiatingKeycode);
/**
* Invoked if LWUIT needs to dispose the native text editing but would like the editor
* to store its state.
*/
public void saveTextEditingState() {
}
/**
* Returns true if the implementation still has elements to paint.
*
* @return false by default
*/
public boolean hasPendingPaints() {
return paintQueueFill != 0;
}
/**
* Returns the video control for the media player
*
* @param player the media player
* @return the video control for the media player
*/
public Object getVideoControl(Object player) {
return null;
}
/**
* Return the number of alpha levels supported by the implementation.
*
* @return the number of alpha levels supported by the implementation
*/
public int numAlphaLevels() {
return 255;
}
/**
* Returns the number of colors applicable on the device, note that the API
* does not support gray scale devices.
*
* @return the number of colors applicable on the device
*/
public int numColors() {
return 65536;
}
/**
* This method allows customizing/creating a graphics context per component which is useful for
* some elaborate implementations of LWUIT. This method is only relevant for elborate components
* such as container which render their own components rather than invoke repaint()
*
* @param cmp component being rendered
* @param currentContext the current graphics context
* @return a graphics object thats appropriate for the given component.
*/
public Graphics getComponentScreenGraphics(Component cmp, Graphics currentContext) {
return currentContext;
}
/**
* Allows for painting an overlay on top of the implementation for notices during
* testing etc.
*
* @param g graphics context on which to draw the overlay
*/
protected void paintOverlay(Graphics g) {
}
/**
* Invoked by the EDT to paint the dirty regions
*/
public void paintDirty() {
int size = 0;
synchronized (displayLock) {
size = paintQueueFill;
Animation[] array = paintQueue;
paintQueue = paintQueueTemp;
paintQueueTemp = array;
paintQueueFill = 0;
}
if (size > 0) {
Graphics wrapper = getLWUITGraphics();
int topX = getDisplayWidth();
int topY = getDisplayHeight();
int bottomX = 0;
int bottomY = 0;
for (int iter = 0; iter < size; iter++) {
Animation ani = paintQueueTemp[iter];
// might happen due to paint queue removal
if(ani == null) {
continue;
}
paintQueueTemp[iter] = null;
wrapper.translate(-wrapper.getTranslateX(), -wrapper.getTranslateY());
wrapper.setClip(0, 0, getDisplayWidth(), getDisplayHeight());
if (ani instanceof Component) {
Component cmp = (Component) ani;
Rectangle dirty = cmp.getDirtyRegion();
if (dirty != null) {
wrapper.setClip(dirty.getX(), dirty.getY(), dirty.getSize().getWidth(), dirty.getSize().getHeight());
cmp.setDirtyRegion(null);
}
cmp.paintComponent(wrapper);
int cmpAbsX = cmp.getAbsoluteX() + cmp.getScrollX();
topX = Math.min(cmpAbsX, topX);
bottomX = Math.max(cmpAbsX + cmp.getWidth(), bottomX);
int cmpAbsY = cmp.getAbsoluteY() + cmp.getScrollY();
topY = Math.min(cmpAbsY, topY);
bottomY = Math.max(cmpAbsY + cmp.getHeight(), bottomY);
} else {
bottomX = getDisplayWidth();
bottomY = getDisplayHeight();
topX = 0;
topY = 0;
ani.paint(wrapper);
}
}
paintOverlay(wrapper);
flushGraphics(topX, topY, bottomX - topX, bottomY - topY);
}
}
/**
* This method is a callback from the edt before the edt enters to an idle
* state
* @param enter true before the edt sleeps and false when exits from the
* idle state
*/
public void edtIdle(boolean enter){
}
/**
* Flush the currently painted drawing onto the screen if using a double buffer
*
* @param x position of the dirty region
* @param y position of the dirty region
* @param width width of the dirty region
* @param height height of the dirty region
*/
public abstract void flushGraphics(int x, int y, int width, int height);
/**
* Flush the currently painted drawing onto the screen if using a double buffer
*/
public abstract void flushGraphics();
/**
* Returns a graphics object for use by the painting
*
* @return a graphics object, either recycled or new, this object will be
* used on the EDT
*/
protected Graphics getLWUITGraphics() {
return lwuitGraphics;
}
/**
* Installs the LWUIT graphics object into the implementation
*
* @param g graphics object for use by the implementation
*/
public void setLWUITGraphics(Graphics g) {
lwuitGraphics = g;
}
/**
* Installs the display lock allowing implementors to synchronize against the
* Display mutex, this method is invoked internally and should not be used.
*
* @param lock the mutex from display
*/
public void setDisplayLock(Object lock) {
displayLock = lock;
}
/**
* Returns a lock object which can be synchrnoized against, this lock is used
* by the EDT.
*
* @return a lock object
*/
public Object getDisplayLock() {
return displayLock;
}
/**
* Removes an entry from the paint queue if it exists, this is important for cases
* in which a component was repainted and immediately removed from its parent container
* afterwards. This happens sometimes in cases where a replace() operation changes
* a component to a new component that has an animation() the animation might have triggered
* a repaint before the removeComponent method was invoked
*
* @param cmp the component to
*/
public void cancelRepaint(Animation cmp) {
synchronized (displayLock) {
for (int iter = 0; iter < paintQueueFill; iter++) {
if (paintQueue[iter] == cmp) {
paintQueue[iter] = null;
return;
}
}
}
}
/**
* Invoked to add an element to the paintQueue
*
* @param cmp component or animation to push into the paint queue
*/
public void repaint(Animation cmp) {
synchronized (displayLock) {
for (int iter = 0; iter < paintQueueFill; iter++) {
Animation ani = paintQueue[iter];
if (ani == cmp) {
return;
}
}
// overcrowding the queue don't try to grow the array!
if (paintQueueFill >= paintQueue.length) {
System.out.println("Warning paint queue size exceeded, please watch the amount of repaint calls");
return;
}
paintQueue[paintQueueFill] = cmp;
paintQueueFill++;
displayLock.notify();
}
}
/**
* Extracts RGB data from the given native image and places it in the given array
*
* @param nativeImage native platform image object
* @param arr int array to store RGB data
* @param offset position within the array to start
* @param x x position within the image
* @param y y position within the image
* @param width width to extract
* @param height height to extract
*/
public abstract void getRGB(Object nativeImage, int[] arr, int offset, int x, int y, int width, int height);
/**
* Create a platform native image object from the given RGB data
*
* @param rgb ARGB data from which to create a platform image
* @param width width for the resulting image
* @param height height for the resulting image
* @return platform image object
*/
public abstract Object createImage(int[] rgb, int width, int height);
/**
* Creates a native image from a file in the system jar
*
* @param path within the jar
* @return native system image
* @throws java.io.IOException if thrown by loading
*/
public abstract Object createImage(String path) throws IOException;
/**
* Creates a native image from a given input stream
*
* @param i input stream from which to load the image
* @return native system image
* @throws java.io.IOException if thrown by loading
*/
public abstract Object createImage(InputStream i) throws IOException;
/**
* Creates a modifable native image that can return a graphics object
*
* @param width the width of the mutable image
* @param height the height of the mutable image
* @param fillColor the ARGB fill color, alpha may be ignored based on the value of
* isAlphaMutableImageSupported
* @return the native image
*/
public abstract Object createMutableImage(int width, int height, int fillColor);
/**
* Indicates whether mutable images respect alpha values when constructed
*
* @return true if mutable images can have an alpha value when initially created
*/
public boolean isAlphaMutableImageSupported() {
return false;
}
/**
* Create a nativate image from its compressed byte data
*
* @param bytes the byte array representing the image data
* @param offset offset within the byte array
* @param len the length for the image within the byte array
* @return a native image
*/
public abstract Object createImage(byte[] bytes, int offset, int len);
/**
* Returns the width of a native image
*
* @param i the native image
* @return the width of the native image
*/
public abstract int getImageWidth(Object i);
/**
* Returns the height of a native image
*
* @param i the native image
* @return the height of the native image
*/
public abstract int getImageHeight(Object i);
/**
* Scales a native image and returns the scaled version
*
* @param nativeImage image to scale
* @param width width of the resulting image
* @param height height of the resulting image
* @return scaled image instance
*/
public abstract Object scale(Object nativeImage, int width, int height);
private static int round(double d) {
double f = Math.floor(d);
double c = Math.ceil(d);
if (c - d < d - f) {
return (int) c;
}
return (int) f;
}
/**
* Returns an instance of this image rotated by the given number of degrees. By default 90 degree
* angle divisions are supported, anything else is implementation dependent. This method assumes
* a square image. Notice that it is inefficient in the current implementation to rotate to
* non-square angles,
* <p>E.g. rotating an image to 45, 90 and 135 degrees is inefficient. Use rotatate to 45, 90
* and then rotate the 45 to another 90 degrees to achieve the same effect with less memory.
*
* @param degrees A degree in right angle must be larger than 0 and up to 359 degrees
* @return new image instance with the closest possible rotation
*/
public Object rotate(Object image, int degrees) {
int width = getImageWidth(image);
int height = getImageHeight(image);
int[] arr = new int[width * height];
int[] dest = new int[arr.length];
getRGB(image, arr, 0, 0, 0, width, height);
int centerX = width / 2;
int centerY = height / 2;
double radians = Math.toRadians(-degrees);
double cosDeg = Math.cos(radians);
double sinDeg = Math.sin(radians);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int x2 = round(cosDeg * (x - centerX) - sinDeg * (y - centerY) + centerX);
int y2 = round(sinDeg * (x - centerX) + cosDeg * (y - centerY) + centerY);
if (!(x2 < 0 || y2 < 0 || x2 >= width || y2 >= height)) {
int destOffset = x2 + y2 * width;
if (destOffset >= 0 && destOffset < dest.length) {
dest[x + y * width] = arr[destOffset];
}
}
}
}
return createImage(dest, width, height);
}
/**
* Returns the number of softkeys on the device
*
* @return the number of softkey buttons on the device
*/
public abstract int getSoftkeyCount();
/**
* Returns the softkey keycode for the given softkey index
*
* @param index the index of the softkey
* @return the set of keycodes which can indicate the softkey, multiple keycodes
* might apply to the same functionality
*/
public abstract int[] getSoftkeyCode(int index);
/**
* Returns the keycode for the clear key
*
* @return the system key code for this device
*/
public abstract int getClearKeyCode();
/**
* Returns the keycode for the backspace key
*
* @return the system key code for this device
*/
public abstract int getBackspaceKeyCode();
/**
* Returns the keycode for the back key
*
* @return the system key code for this device
*/
public abstract int getBackKeyCode();
/**
* Returns the display game action for the given keyCode if applicable to match
* the contrct of LWUIT for the game action behavior
*
* @param keyCode the device keycode
* @return a game action or 0
*/
public abstract int getGameAction(int keyCode);
/**
* Returns a keycode which can be sent to getGameAction
*
* @param gameAction the game action
* @return key code matching the given game action
*/
public abstract int getKeyCode(int gameAction);
/**
* Returns true if the device will send touch events
*
* @return true if the device will send touch events
*/
public abstract boolean isTouchDevice();
/**
* This method is used internally to determine the actual current form
* it doesn't perform the logic of transitions etc. and shouldn't be invoked
* by developers
*
* @param f the current form
*/
public void setCurrentForm(Form f) {
currentForm = f;
}
/**
* Callback method allowing the implementation to confirm that it controls the
* view just before a new form is installed.
*/
public void confirmControlView() {
}
/**
* Returns the current form, this method is for internal use only and does not
* take transitions/menus into consideration
*
* @return The internal current form
*/
public Form getCurrentForm() {
return currentForm;
}
/**
* LWUIT can translate all coordinates and never requires a call to translate
* this works well for some devices which have hairy issues with translate.
* However for some platforms where translate can be leveraged with affine transforms
* this can be a problem. These platforms can choose to translate on their own
*
* @return true if the implementation is interested in receiving translate calls
* and handling them.
*/
public boolean isTranslationSupported() {
return false;
}
/**
* Translates the X/Y location for drawing on the underlying surface. Translation
* is incremental so the new value will be added to the current translation and
* in order to reset translation we have to invoke
* {@code translate(-getTranslateX(), -getTranslateY()) }
*
* @param graphics the graphics context
* @param x the x coordinate
* @param y the y coordinate
*/
public void translate(Object graphics, int x, int y) {
}
/**
* Returns the current x translate value
*
* @param graphics the graphics context
* @return the current x translate value
*/
public int getTranslateX(Object graphics) {
return 0;
}
/**
* Returns the current y translate value
*
* @param graphics the graphics context
* @return the current y translate value
*/
public int getTranslateY(Object graphics) {
return 0;
}
/**
* Returns the current color
*
* @param graphics the graphics context
* @return the RGB graphics color
*/
public abstract int getColor(Object graphics);
/**
* Sets the current rgb color while ignoring any potential alpha component within
* said color value.
*
* @param graphics the graphics context
* @param RGB the RGB value for the color.
*/
public abstract void setColor(Object graphics, int RGB);
/**
* Alpha value from 0-255 can be ignored for some operations
*
* @param graphics the graphics context
* @param alpha the alpha channel
*/
public abstract void setAlpha(Object graphics, int alpha);
/**
* Alpha value from 0-255 can be ignored for some operations
*
* @param graphics the graphics context
* @return the alpha channel
*/
public abstract int getAlpha(Object graphics);
/**
* Returns true if alpha can be applied for all elements globally and efficiently
* otherwise alpha should be ignored.
* Notice that fillRect MUST always support alpha regardless of the value of this
* variable!
*
* @return true if alpha support is natively implemented
*/
public boolean isAlphaGlobal() {
return false;
}
/**
* Indicates whether the underlying implementation allows for anti-aliasing in regular
* drawing operations
*
* @return false by default
*/
public boolean isAntiAliasingSupported() {
return false;
}
/**
* Indicates whether the underlying implementation allows for anti-aliased fonts
*
* @return false by default
*/
public boolean isAntiAliasedTextSupported() {
return false;
}
/**
* Toggles anti-aliasing mode for regular rendering operations
*
* @param graphics the graphics context
* @param a true to activate Anti-aliasing, false to disable it
*/
public void setAntiAliased(Object graphics, boolean a) {
}
/**
* Returns anti-aliasing mode for regular rendering operations
*
* @param graphics the graphics context
* @return true if Anti-aliasing is active, false otherwise
*/
public boolean isAntiAliased(Object graphics) {
return false;
}
/**
* Toggles anti-aliasing mode for font rendering operations
*
* @param graphics the graphics context
* @param a true to activate Anti-aliasing, false to disable it
*/
public void setAntiAliasedText(Object graphics, boolean a) {
}
/**
* Returns anti-aliasing mode for font rendering operations
*
* @param graphics the graphics context
* @return true if Anti-aliasing is active, false otherwise
*/
public boolean isAntiAliasedText(Object graphics) {
return false;
}
/**
* Installs a native font object
*
* @param graphics the graphics context
* @param font the native font object
*/
public abstract void setNativeFont(Object graphics, Object font);
/**
* Returns the internal clipping rectangle. This method must create a new
* rectangle object to prevent corruption by modification.
*
* @param graphics the graphics context
* @return the clipping rectangle.
*/
public Rectangle getClipRect(Object graphics) {
return new Rectangle(getClipX(graphics), getClipY(graphics), new Dimension(getClipWidth(graphics), getClipHeight(graphics)));
}
/**
* Returns the clipping coordinate
*
* @param graphics the graphics context
* @return the clipping coordinate
*/
public abstract int getClipX(Object graphics);
/**
* Returns the clipping coordinate
*
* @param graphics the graphics context
* @return the clipping coordinate
*/
public abstract int getClipY(Object graphics);
/**
* Returns the clipping coordinate
*
* @param graphics the graphics context
* @return the clipping coordinate
*/
public abstract int getClipWidth(Object graphics);
/**
* Returns the clipping coordinate
*
* @param graphics the graphics context
* @return the clipping coordinate
*/
public abstract int getClipHeight(Object graphics);
/**
* Installs a new clipping rectangle
*
* @param graphics the graphics context
* @param rect rectangle representing the new clipping area
*/
public void setClipRect(Object graphics, Rectangle rect) {
Dimension d = rect.getSize();
setClip(graphics, rect.getX(), rect.getY(), d.getWidth(), d.getHeight());
}
/**
* Installs a new clipping rectangle
*
* @param graphics the graphics context
* @param x coordinate
* @param y coordinate
* @param width size
* @param height size
* @param rect rectangle representing the new clipping area
*/
public abstract void setClip(Object graphics, int x, int y, int width, int height);
/**
* Changes the current clipping rectangle to subset the current clipping with
* the given clipping.
*
* @param graphics the graphics context
* @param rect rectangle representing the new clipping area
*/
public void clipRect(Object graphics, Rectangle rect) {
Dimension d = rect.getSize();
clipRect(graphics, rect.getX(), rect.getY(), d.getWidth(), d.getHeight());
}
/**
* Changes the current clipping rectangle to subset the current clipping with
* the given clipping.
*
* @param graphics the graphics context
* @param x coordinate
* @param y coordinate
* @param width size
* @param height size
* @param rect rectangle representing the new clipping area
*/
public abstract void clipRect(Object graphics, int x, int y, int width, int height);
/**
* Draws a line between the 2 X/Y coordinates
*
* @param graphics the graphics context
* @param x1 first x position
* @param y1 first y position
* @param x2 second x position
* @param y2 second y position
*/
public abstract void drawLine(Object graphics, int x1, int y1, int x2, int y2);
/**
* Fills the rectangle from the given position according to the width/height
* minus 1 pixel according to the convention in Java.
*
* @param graphics the graphics context
* @param x the x coordinate of the rectangle to be filled.
* @param y the y coordinate of the rectangle to be filled.
* @param width the width of the rectangle to be filled.
* @param height the height of the rectangle to be filled.
*/
public abstract void fillRect(Object graphics, int x, int y, int width, int height);
/**
* Draws a rectangle in the given coordinates
*
* @param graphics the graphics context
* @param x the x coordinate of the rectangle to be drawn.
* @param y the y coordinate of the rectangle to be drawn.
* @param width the width of the rectangle to be drawn.
* @param height the height of the rectangle to be drawn.
*/
public abstract void drawRect(Object graphics, int x, int y, int width, int height);
/**
* Draws a rounded corner rectangle in the given coordinates with the arcWidth/height
* matching the last two arguments respectively.
*
* @param graphics the graphics context
* @param x the x coordinate of the rectangle to be drawn.
* @param y the y coordinate of the rectangle to be drawn.
* @param width the width of the rectangle to be drawn.
* @param height the height of the rectangle to be drawn.
* @param arcWidth the horizontal diameter of the arc at the four corners.
* @param arcHeight the vertical diameter of the arc at the four corners.
*/
public abstract void drawRoundRect(Object graphics, int x, int y, int width, int height, int arcWidth, int arcHeight);
/**
* Fills a rounded rectangle in the same way as drawRoundRect
*
* @param graphics the graphics context
* @param x the x coordinate of the rectangle to be filled.
* @param y the y coordinate of the rectangle to be filled.
* @param width the width of the rectangle to be filled.
* @param height the height of the rectangle to be filled.
* @param arcWidth the horizontal diameter of the arc at the four corners.
* @param arcHeight the vertical diameter of the arc at the four corners.
* @see #drawRoundRect
*/
public abstract void fillRoundRect(Object graphics, int x, int y, int width, int height, int arcWidth, int arcHeight);
/**
* Fills a circular or elliptical arc based on the given angles and bounding
* box. The resulting arc begins at startAngle and extends for arcAngle
* degrees.
*
* @param graphics the graphics context
* @param x the x coordinate of the upper-left corner of the arc to be filled.
* @param y the y coordinate of the upper-left corner of the arc to be filled.
* @param width the width of the arc to be filled.
* @param height the height of the arc to be filled.
* @param startAngle the beginning angle.
* @param arcAngle the angular extent of the arc, relative to the start angle.
*/
public abstract void fillArc(Object graphics, int x, int y, int width, int height, int startAngle, int arcAngle);
/**
* Draws a circular or elliptical arc based on the given angles and bounding
* box
*
* @param graphics the graphics context
* @param x the x coordinate of the upper-left corner of the arc to be drawn.
* @param y the y coordinate of the upper-left corner of the arc to be drawn.
* @param width the width of the arc to be drawn.
* @param height the height of the arc to be drawn.
* @param startAngle the beginning angle.
* @param arcAngle the angular extent of the arc, relative to the start angle.
*/
public abstract void drawArc(Object graphics, int x, int y, int width, int height, int startAngle, int arcAngle);
/**
* Draw a string using the current font and color in the x,y coordinates. The font is drawn
* from the top position and not the baseline.
*
* @param graphics the graphics context
* @param str the string to be drawn.
* @param x the x coordinate.
* @param y the y coordinate.
*/
public abstract void drawString(Object graphics, String str, int x, int y);
/**
* Draws the image so its top left coordinate corresponds to x/y
*
* @param graphics the graphics context
* @param img the specified native image to be drawn
* @param x the x coordinate.
* @param y the y coordinate.
*/
public abstract void drawImage(Object graphics, Object img, int x, int y);
/**
* Draws the image so its top left coordinate corresponds to x/y
*
* @param graphics the graphics context
* @param img the specified native image to be drawn
* @param x the x coordinate.
* @param y the y coordinate.
* @param w the width
* @param h the height
*/
public void drawImage(Object graphics, Object img, int x, int y, int w, int h) {
}
/**
* Indicates if image scaling on the fly is supported by the platform, if not LWUIT will just scale the images on its own before drawing
*/
public boolean isScaledImageDrawingSupported() {
return false;
}
/**
* Draws a portion of the image
*
* @param nativeGraphics the graphics context
* @param img the specified native image to be drawn
* @param x the x coordinate.
* @param y the y coordinate.
* @param imageX location within the image to draw
* @param imageY location within the image to draw
* @param imageWidth size of the location within the image to draw
* @param imageHeight size of the location within the image to draw
*/
public void drawImageArea(Object nativeGraphics, Object img, int x, int y, int imageX, int imageY, int imageWidth, int imageHeight) {
int clipX = getClipX(nativeGraphics);
int clipY = getClipY(nativeGraphics);
int clipWidth = getClipWidth(nativeGraphics);
int clipHeight = getClipHeight(nativeGraphics);
clipRect(nativeGraphics, x, y, imageWidth, imageHeight);
if (getClipWidth(nativeGraphics) > 0 && getClipHeight(nativeGraphics) > 0) {
drawImage(nativeGraphics, img, x - imageX, y - imageY);
}
setClip(nativeGraphics, clipX, clipY, clipWidth, clipHeight);
}
/**
* Draws the image so its top left coordinate corresponds to x/y with a fast
* native rotation in a square angle which must be one of 0, 90, 180 or 270
*
* @param graphics the graphics context
* @param img the specified native image to be drawn
* @param x the x coordinate.
* @param y the y coordinate.
* @param degrees either 0, 90, 180 or 270 degree rotation for the image drawing
*/
public void drawImageRotated(Object graphics, Object img, int x, int y, int degrees) {
}
/**
* Indicates whether drawImageRotated is supported by the platform for FAST drawing,
* if not then its not worth calling the method which will be unimplemented!
*
* @return true if drawImageRotated will draw an image
*/
public boolean isRotationDrawingSupported() {
return false;
}
/**
* Draws a filled triangle with the given coordinates
*
* @param graphics the graphics context
* @param x1 the x coordinate of the first vertex of the triangle
* @param y1 the y coordinate of the first vertex of the triangle
* @param x2 the x coordinate of the second vertex of the triangle
* @param y2 the y coordinate of the second vertex of the triangle
* @param x3 the x coordinate of the third vertex of the triangle
* @param y3 the y coordinate of the third vertex of the triangle
*/
public void fillTriangle(Object graphics, int x1, int y1, int x2, int y2, int x3, int y3) {
fillPolygon(graphics, new int[]{x1, x2, x3}, new int[]{y1, y2, y3}, 3);
}
/**
* Draws the RGB values based on the MIDP API of a similar name. Renders a
* series of device-independent RGB+transparency values in a specified
* region. The values are stored in rgbData in a format with 24 bits of
* RGB and an eight-bit alpha value (0xAARRGGBB), with the first value
* stored at the specified offset. The scanlength specifies the relative
* offset within the array between the corresponding pixels of consecutive
* rows. Any value for scanlength is acceptable (even negative values)
* provided that all resulting references are within the bounds of the
* rgbData array. The ARGB data is rasterized horizontally from left to
* right within each row. The ARGB values are rendered in the region
* specified by x, y, width and height, and the operation is subject
* to the current clip region and translation for this Graphics object.
*
* @param graphics the graphics context
* @param rgbData an array of ARGB values in the format 0xAARRGGBB
* @param offset the array index of the first ARGB value
* @param x the horizontal location of the region to be rendered
* @param y the vertical location of the region to be rendered
* @param w the width of the region to be rendered
* @param h the height of the region to be rendered
* @param processAlpha true if rgbData has an alpha channel, false if
* all pixels are fully opaque
*/
public abstract void drawRGB(Object graphics, int[] rgbData, int offset, int x, int y, int w, int h, boolean processAlpha);
/**
* Returns the native graphics object on which all rendering operations occur
*
* @return a native graphics context
*/
public abstract Object getNativeGraphics();
/**
* Returns the native graphics object on the given native image occur
*
* @param image the native image on which the graphics will draw
* @return a native graphics context
*/
public abstract Object getNativeGraphics(Object image);
/**
* Return the width of the given characters in the given native font instance
*
* @param nativeFont the font for which the string width should be calculated
* @param ch array of characters
* @param offset characters offsets
* @param length characters length
* @return the width of the given characters in this font instance
*/
public abstract int charsWidth(Object nativeFont, char[] ch, int offset, int length);
/**
* Return the width of the given string in this font instance
*
* @param nativeFont the font for which the string width should be calculated
* @param str the given string *
* @return the width of the given string in this font instance
*/
public abstract int stringWidth(Object nativeFont, String str);
/**
* Return the width of the specific character when rendered alone
*
* @param nativeFont the font for which the string width should be calculated
* @param ch the specific character
* @return the width of the specific character when rendered alone
*/
public abstract int charWidth(Object nativeFont, char ch);
/**
* Return the total height of the font
*
* @param nativeFont the font for which the string width should be calculated
* @return the total height of the font
*/
public abstract int getHeight(Object nativeFont);
/**
* Return the global default font instance, if font is passed as null
* this font should be used
*
* @return the global default font instance
*/
public abstract Object getDefaultFont();
/**
* Optional operation returning the font face for the font
*
* @param nativeFont the font for which the string width should be calculated
* @return Optional operation returning the font face for system fonts
*/
public int getFace(Object nativeFont) {
return 0;
}
/**
* Optional operation returning the font size for system fonts
*
* @param nativeFont the font for which the string width should be calculated
* @return Optional operation returning the font size for system fonts
*/
public int getSize(Object nativeFont) {
return 0;
}
/**
* Optional operation returning the font style for system fonts
*
* @param nativeFont the font for which the string width should be calculated
* @return Optional operation returning the font style for system fonts
*/
public int getStyle(Object nativeFont) {
return 0;
}
/**
* Creates a new instance of a native font
*
* @param face the face of the font, can be one of FACE_SYSTEM,
* FACE_PROPORTIONAL, FACE_MONOSPACE.
* @param style the style of the font.
* The value is an OR'ed combination of STYLE_BOLD, STYLE_ITALIC, and
* STYLE_UNDERLINED; or the value is zero (STYLE_PLAIN).
* @param size the size of the font, can be one of SIZE_SMALL,
* SIZE_MEDIUM, SIZE_LARGE
* @return a native font object
*/
public abstract Object createFont(int face, int style, int size);
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param keyCode the key for the event
*/
protected void keyPressed(final int keyCode) {
Display.getInstance().keyPressed(keyCode);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param keyCode the key for the event
*/
protected void keyReleased(final int keyCode) {
Display.getInstance().keyReleased(keyCode);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerDragged(final int x, final int y) {
xPointerEvent[0] = x;
yPointerEvent[0] = y;
pointerDragged(xPointerEvent, yPointerEvent);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerPressed(final int x, final int y) {
xPointerEvent[0] = x;
yPointerEvent[0] = y;
pointerPressed(xPointerEvent, yPointerEvent);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerReleased(final int x, final int y) {
xPointerEvent[0] = x;
yPointerEvent[0] = y;
pointerReleased(xPointerEvent, yPointerEvent);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerHover(final int[] x, final int[] y) {
Display.getInstance().pointerHover(x, y);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerHoverReleased(final int[] x, final int[] y) {
Display.getInstance().pointerHoverReleased(x, y);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerHoverReleased(final int x, final int y) {
xPointerEvent[0] = x;
yPointerEvent[0] = y;
pointerHoverReleased(xPointerEvent, yPointerEvent);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerHoverPressed(final int[] x, final int[] y) {
Display.getInstance().pointerHoverPressed(x, y);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerHoverPressed(final int x, final int y) {
xPointerEvent[0] = x;
yPointerEvent[0] = y;
pointerHoverPressed(xPointerEvent, yPointerEvent);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerHover(final int x, final int y) {
xPointerEvent[0] = x;
yPointerEvent[0] = y;
pointerHover(xPointerEvent, yPointerEvent);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerDragged(final int[] x, final int[] y) {
if (hasDragStarted(x, y)) {
Display.getInstance().pointerDragged(x, y);
}
}
/**
* This method can be overriden by subclasses to indicate whether a drag
* event has started or whether the device is just sending out "noise".
* This method is invoked by pointer dragged to determine whether to propogate
* the actual pointer drag event to LWUIT.
*
* @param x the position of the current drag event
* @param y the position of the current drag event
* @return true if the drag should propogate into LWUIT
*/
protected boolean hasDragStarted(final int[] x, final int[] y) {
return hasDragStarted(x[0], y[0]);
}
/**
* This method can be overriden by subclasses to indicate whether a drag
* event has started or whether the device is just sending out "noise".
* This method is invoked by pointer dragged to determine whether to propogate
* the actual pointer drag event to LWUIT.
*
* @param x the position of the current drag event
* @param y the position of the current drag event
* @return true if the drag should propogate into LWUIT
*/
protected boolean hasDragStarted(final int x, final int y) {
if (dragActivationCounter == 0) {
dragActivationX = x;
dragActivationY = y;
dragActivationCounter++;
return false;
}
//send the drag events to the form only after latency of 7 drag events,
//most touch devices are too sensitive and send too many drag events.
//7 is just a latency const number that is pretty good for most devices
//this may be tuned for specific devices.
dragActivationCounter++;
if (dragActivationCounter > getDragAutoActivationThreshold()) {
return true;
}
// have we passed the motion threshold on the X axis?
if (((float) getDisplayWidth()) / 100.0f * ((float) getDragStartPercentage()) <=
Math.abs(dragActivationX - x)) {
dragActivationCounter = getDragAutoActivationThreshold() + 1;
return true;
}
// have we passed the motion threshold on the Y axis?
if (((float) getDisplayHeight()) / 100.0f * ((float) getDragStartPercentage()) <=
Math.abs(dragActivationY - y)) {
dragActivationCounter = getDragAutoActivationThreshold() + 1;
return true;
}
return false;
}
/**
* This method allows us to manipulate the drag started detection logic.
* If the pointer was dragged for more than this percentage of the display size it
* is safe to assume that a drag is in progress.
*
* @return motion percentage
*/
public int getDragStartPercentage() {
return dragStartPercentage;
}
/**
* This method allows us to manipulate the drag started detection logic.
* If the pointer was dragged for more than this percentage of the display size it
* is safe to assume that a drag is in progress.
*
* @param dragStartPercentage percentage of the screen required to initiate drag
*/
public void setDragStartPercentage(int dragStartPercentage) {
this.dragStartPercentage = dragStartPercentage;
}
/**
* This method allows subclasses to manipulate the drag started detection logic.
* If more than this number of drag events were delivered it is safe to assume a drag has started
* This number must be bigger than 0!
*
* @return number representing a minimum number of motion events to start a drag operation
*/
protected int getDragAutoActivationThreshold() {
return 7;
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerPressed(final int[] x, final int[] y) {
pointerPressedX = x[0];
pointerPressedY = y[0];
Display.getInstance().pointerPressed(x, y);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param x the position of the event
* @param y the position of the event
*/
protected void pointerReleased(final int[] x, final int[] y) {
// this is a special case designed to detect a "flick" event on some Samsung devices
// that send a pointerPressed/Released with widely differing X/Y values but don't send
// the pointerDrag events in between
if(dragActivationCounter == 0 && x[0] != pointerPressedX && y[0] != pointerPressedY) {
hasDragStarted(pointerPressedX, pointerPressedY);
if(hasDragStarted(x, y)) {
pointerDragged(pointerPressedX, pointerPressedY);
pointerDragged(x, y);
}
}
dragActivationCounter = 0;
Display.getInstance().pointerReleased(x, y);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param w the size of the screen
* @param h the size of the screen
*/
protected void sizeChanged(int w, int h) {
Display.getInstance().sizeChanged(w, h);
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param w the size of the screen
* @param h the size of the screen
*/
protected void hideNotify() {
Display.getInstance().hideNotify();
}
/**
* Subclasses should invoke this method, it delegates the event to the display and into
* LWUIT.
*
* @param w the size of the screen
* @param h the size of the screen
*/
protected void showNotify() {
Display.getInstance().showNotify();
}
private Object findCachedGradient(Hashtable cache, int startColor, int endColor, int x, int y, int width, int height, boolean horizontal, int centerX, int centerY, int size) {
if(cache != null) {
Enumeration e = cache.keys();
while(e.hasMoreElements()) {
int[] current = (int[])e.nextElement();
Object currentRef = cache.get(current);
if(currentRef == null) {
cache.remove(current);
e = cache.keys();
continue;
}
Object currentImage = extractHardRef(currentRef);
if(currentImage == null) {
cache.remove(current);
e = cache.keys();
continue;
}
if(current[0] == startColor &&
current[1] == endColor &&
current[2] == x &&
current[3] == y &&
current[5] == centerX &&
current[6] == centerY &&
current[7] == size &&
getImageWidth(currentImage) == width &&
getImageHeight(currentImage) == height) {
if((horizontal && current[4] == 1) || ((!horizontal) && current[4] == 0)) {
return currentImage;
}
}
}
}
return null;
}
private void storeCachedGradient(Object img, Hashtable cache, int startColor, int endColor, int x, int y, boolean horizontal, int centerX, int centerY, int size) {
int[] key;
if(horizontal) {
key = new int[] {startColor, endColor, x, y, 1, centerX, centerY, size};
} else {
key = new int[] {startColor, endColor, x, y, 0, centerX, centerY, size};
}
cache.put(key, createSoftWeakRef(img));
}
/**
* Draws a radial gradient in the given coordinates with the given colors,
* doesn't take alpha into consideration when drawing the gradient.
* Notice that a radial gradient will result in a circular shape, to create
* a square use fillRect or draw a larger shape and clip to the appropriate size.
*
* @param graphics the graphics context
* @param startColor the starting RGB color
* @param endColor the ending RGB color
* @param x the x coordinate
* @param y the y coordinate
* @param width the width of the region to be filled
* @param height the height of the region to be filled
* @param relativeX indicates the relative position of the gradient within the drawing region
* @param relativeY indicates the relative position of the gradient within the drawing region
* @param relativeSize indicates the relative size of the gradient within the drawing region
*/
public void fillRectRadialGradient(Object graphics, int startColor, int endColor, int x, int y, int width, int height, float relativeX, float relativeY, float relativeSize) {
int centerX = (int) (width * (1 - relativeX));
int centerY = (int) (height * (1 - relativeY));
int size = (int)(Math.min(width, height) * relativeSize);
int x2 = (int)(width / 2 - (size * relativeX));
int y2 = (int)(height / 2 - (size * relativeY));
boolean aa = isAntiAliased(graphics);
setAntiAliased(graphics, false);
if(cacheRadialGradients()) {
Object r = findCachedGradient(radialGradientCache, startColor, endColor, x, y, width, height, true, centerX, centerY, size);
if(r != null) {
drawImage(graphics, r, x, y);
} else {
r = createMutableImage(width, height, 0xffffffff);
Object imageGraphics = getNativeGraphics(r);
setColor(imageGraphics, endColor);
fillRect(imageGraphics, 0, 0, width, height);
fillRadialGradientImpl(imageGraphics, startColor, endColor, x2, y2, size, size);
drawImage(graphics, r, x, y);
if(radialGradientCache == null) {
radialGradientCache = new Hashtable();
}
storeCachedGradient(r, radialGradientCache, startColor, endColor, x, y, true, centerX, centerY, size);
}
} else {
setColor(graphics, endColor);
fillRect(graphics, x, y, width, height);
fillRadialGradientImpl(graphics, startColor, endColor, x + x2, y + y2, size, size);
}
if(aa) {
setAntiAliased(graphics, true);
}
}
/**
* Draws a radial gradient in the given coordinates with the given colors,
* doesn't take alpha into consideration when drawing the gradient.
* Notice that a radial gradient will result in a circular shape, to create
* a square use fillRect or draw a larger shape and clip to the appropriate size.
*
* @param graphics the graphics context
* @param startColor the starting RGB color
* @param endColor the ending RGB color
* @param x the x coordinate
* @param y the y coordinate
* @param width the width of the region to be filled
* @param height the height of the region to be filled
*/
public void fillRadialGradient(Object graphics, int startColor, int endColor, int x, int y, int width, int height) {
fillRadialGradientImpl(graphics, startColor, endColor, x, y, width, height);
}
private void fillRadialGradientImpl(Object graphics, int startColor, int endColor, int x, int y, int width, int height) {
boolean aa = isAntiAliased(graphics);
setAntiAliased(graphics, false);
int sourceR = startColor >> 16 & 0xff;
int sourceG = startColor >> 8 & 0xff;
int sourceB = startColor & 0xff;
int destR = endColor >> 16 & 0xff;
int destG = endColor >> 8 & 0xff;
int destB = endColor & 0xff;
int oldColor = getColor(graphics);
int originalHeight = height;
while (width > 0 && height > 0) {
updateGradientColor(graphics, sourceR, sourceG, sourceB, destR,
destG, destB, originalHeight, height);
fillArc(graphics, x, y, width, height, 0, 360);
x++;
y++;
width -= 2;
height -= 2;
}
setColor(graphics, oldColor);
if(aa) {
setAntiAliased(graphics, true);
}
}
private void updateGradientColor(Object nativeGraphics, int sourceR, int sourceG, int sourceB, int destR,
int destG, int destB, int distance, int offset) {
//int a = calculateGraidentChannel(sourceA, destA, distance, offset);
int r = calculateGraidentChannel(sourceR, destR, distance, offset);
int g = calculateGraidentChannel(sourceG, destG, distance, offset);
int b = calculateGraidentChannel(sourceB, destB, distance, offset);
int color = /*((a << 24) & 0xff000000) |*/ ((r << 16) & 0xff0000) |
((g << 8) & 0xff00) | (b & 0xff);
setColor(nativeGraphics, color);
}
/**
* Converts the color channel value according to the offest within the distance
*/
private int calculateGraidentChannel(int sourceChannel, int destChannel, int distance, int offset) {
if (sourceChannel == destChannel) {
return sourceChannel;
}
float ratio = ((float) offset) / ((float) distance);
int pos = (int) (Math.abs(sourceChannel - destChannel) * ratio);
if (sourceChannel > destChannel) {
return sourceChannel - pos;
} else {
return sourceChannel + pos;
}
}
/**
* Draws a linear gradient in the given coordinates with the given colors,
* doesn't take alpha into consideration when drawing the gradient
*
* @param graphics the graphics context
* @param startColor the starting RGB color
* @param endColor the ending RGB color
* @param x the x coordinate
* @param y the y coordinate
* @param width the width of the region to be filled
* @param height the height of the region to be filled
* @param horizontal indicating wheter it is a horizontal fill or vertical
*/
public void fillLinearGradient(Object graphics, int startColor, int endColor, int x, int y, int width, int height, boolean horizontal) {
boolean aa = isAntiAliased(graphics);
setAntiAliased(graphics, false);
if(cacheLinearGradients()) {
Object r = findCachedGradient(linearGradientCache, startColor, endColor, x, y, width, height, horizontal, 0, 0, 0);
if(r != null) {
drawImage(graphics, r, x, y);
} else {
r = createMutableImage(width, height, 0xffffffff);
fillLinearGradientImpl(getNativeGraphics(r), startColor, endColor, 0, 0, width, height, horizontal);
drawImage(graphics, r, x, y);
if(linearGradientCache == null) {
linearGradientCache = new Hashtable();
}
storeCachedGradient(r, linearGradientCache, startColor, endColor, x, y, horizontal, 0, 0, 0);
}
} else {
fillLinearGradientImpl(graphics, startColor, endColor, x, y, width, height, horizontal);
}
if(aa) {
setAntiAliased(graphics, true);
}
}
private void fillLinearGradientImpl(Object graphics, int startColor, int endColor, int x, int y, int width, int height, boolean horizontal) {
int sourceR = startColor >> 16 & 0xff;
int sourceG = startColor >> 8 & 0xff;
int sourceB = startColor & 0xff;
int destR = endColor >> 16 & 0xff;
int destG = endColor >> 8 & 0xff;
int destB = endColor & 0xff;
int oldColor = getColor(graphics);
if (horizontal) {
for (int iter = 0; iter < width; iter++) {
updateGradientColor(graphics, sourceR, sourceG, sourceB, destR,
destG, destB, width, iter);
drawLine(graphics, x + iter, y, x + iter, y + height);
}
} else {
for (int iter = 0; iter < height; iter++) {
updateGradientColor(graphics, sourceR, sourceG, sourceB, destR,
destG, destB, height, iter);
drawLine(graphics, x, y + iter, x + width, y + iter);
}
}
setColor(graphics, oldColor);
}
private boolean checkIntersection(Object g, int y0, int x1, int x2, int y1, int y2, int[] intersections, int intersectionsCount) {
if (y0 > y1 && y0 < y2 || y0 > y2 && y0 < y1) {
if (y1 == y2) {
drawLine(g, x1, y0, x2, y0);
return false;
}
intersections[intersectionsCount] = x1 + ((y0 - y1) * (x2 - x1)) / (y2 - y1);
return true;
}
return false;
}
private int markIntersectionEdge(Object g, int idx, int[] yPoints, int[] xPoints, int nPoints, int[] intersections, int intersectionsCount) {
intersections[intersectionsCount] = xPoints[idx];
if ((yPoints[idx] - yPoints[(idx + 1) % nPoints]) * (yPoints[idx] - yPoints[(idx + nPoints - 1) % nPoints]) > 0) {
intersections[intersectionsCount + 1] = xPoints[idx];
return 2;
}
//Check for special case horizontal line
if (yPoints[idx] == yPoints[(idx + 1) % nPoints]) {
drawLine(g, xPoints[idx], yPoints[idx], xPoints[(idx + 1) % nPoints], yPoints[(idx + 1) % nPoints]);
if ((yPoints[(idx + 1) % nPoints] - yPoints[(idx + 2) % nPoints]) * (yPoints[idx] - yPoints[(idx + nPoints - 1) % nPoints]) > 0) {
return 1;
} else {
intersections[intersectionsCount + 1] = xPoints[idx];
return 2;
}
}
return 1;
}
/**
* Fills a closed polygon defined by arrays of x and y coordinates.
* Each pair of (x, y) coordinates defines a point.
*
* @param graphics the graphics context
* @param xPoints - a an array of x coordinates.
* @param yPoints - a an array of y coordinates.
* @param nPoints - a the total number of points.
*/
public void fillPolygon(Object graphics, int[] xPoints, int[] yPoints, int nPoints) {
int[] intersections = new int[nPoints];
int intersectionsCount = 0;
int yMax = (int) yPoints[0];
int yMin = (int) yPoints[0];
for (int i = 0; i < nPoints; i++) {
yMax = Math.max(yMax, yPoints[i]);
yMin = Math.min(yMin, yPoints[i]);
}
// Loop through the rows of the image.
for (int row = yMin; row <= yMax; row++) {
intersectionsCount = 0;
for (int i = 1; i < nPoints; i++) {
if (checkIntersection(graphics, row, xPoints[i - 1], xPoints[i], yPoints[i - 1], yPoints[i], intersections, intersectionsCount)) {
intersectionsCount++;
}
}
if (checkIntersection(graphics, row, xPoints[nPoints - 1], xPoints[0], yPoints[nPoints - 1], yPoints[0], intersections, intersectionsCount)) {
intersectionsCount++;
}
for (int j = 0; j < nPoints; j++) {
if (row == yPoints[j]) {
intersectionsCount += markIntersectionEdge(graphics, j, yPoints, xPoints, nPoints, intersections, intersectionsCount);
}
}
int swap = 0;
for (int i = 0; i < intersectionsCount; i++) {
for (int j = i; j < intersectionsCount; j++) {
if (intersections[j] < intersections[i]) {
swap = intersections[i];
intersections[i] = intersections[j];
intersections[j] = swap;
}
}
}
for (int i = 1; i < intersectionsCount; i = i + 2) {
drawLine(graphics, intersections[i - 1], row, intersections[i], row);
}
}
}
/**
* Draws a closed polygon defined by arrays of x and y coordinates.
* Each pair of (x, y) coordinates defines a point.
*
* @param graphics the graphics context
* @param xPoints - a an array of x coordinates.
* @param yPoints - a an array of y coordinates.
* @param nPoints - a the total number of points.
*/
public void drawPolygon(Object graphics, int[] xPoints, int[] yPoints, int nPoints) {
for (int i = 1; i < nPoints; i++) {
drawLine(graphics, xPoints[i - 1], yPoints[i - 1], xPoints[i], yPoints[i]);
}
drawLine(graphics, xPoints[nPoints - 1], yPoints[nPoints - 1], xPoints[0], yPoints[0]);
}
/**
* Returns the type of the input device one of:
* KEYBOARD_TYPE_UNKNOWN, KEYBOARD_TYPE_NUMERIC, KEYBOARD_TYPE_QWERTY,
* KEYBOARD_TYPE_VIRTUAL, KEYBOARD_TYPE_HALF_QWERTY
*
* @return KEYBOARD_TYPE_UNKNOWN
*/
public int getKeyboardType() {
return Display.KEYBOARD_TYPE_UNKNOWN;
}
/**
* Indicates whether the device supports native in place editing in which case
* lightweight input logic shouldn't be used for input.
*
* @return false by default
*/
public boolean isNativeInputSupported() {
return false;
}
/**
* Indicates whether the device supports multi-touch events, this is only
* relevant when touch events are supported
*
* @return false by default
*/
public boolean isMultiTouch() {
return false;
}
/**
* Indicates whether the device has a double layer screen thus allowing two
* stages to touch events: click and hover. This is true for devices such
* as the storm but can also be true for a PC with a mouse pointer floating
* on top.
* <p>A click touch screen will also send pointer hover events to the underlying
* software and will only send the standard pointer events on click.
*
* @return false by default
*/
public boolean isClickTouchScreen() {
return false;
}
/**
* Returns true if indexed images should be used natively
*
* @return true if a native image should be used for indexed images
*/
public boolean isNativeIndexed() {
return false;
}
/**
* Creates a native image representing the indexed image
*
* @param image the indexed image
* @return a native version of the indexed image
*/
public Object createNativeIndexed(IndexedImage image) {
return null;
}
/**
* Create a video/media component
*
* @param player object responsible for playback lifecycle
* @return the video control
* @deprecated replaced by the new video component
*/
public Object createVideoComponent(Object player) {
return null;
}
/**
* Returns the video width
*
* @param videoControl the control for the video
* @return the width
* @deprecated replaced by the new video component
*/
public int getVideoWidth(Object videoControl) {
return 0;
}
/**
* Returns the video height
*
* @param videoControl the control for the video
* @return the height
* @deprecated replaced by the new video component
*/
public int getVideoHeight(Object videoControl) {
return 0;
}
/**
* Sets the video visibility
*
* @param vc video control instance
* @param visible whether the video is visible
* @deprecated replaced by the new video component
*/
public void setVideoVisible(Object vc, boolean visible) {
}
/**
* Starts the video
*
* @param player the player object
* @param videoControl the video control
* @deprecated replaced by the new video component
*/
public void startVideo(Object player, Object videoControl) {
}
/**
* Stop the video
*
* @param player the player object
* @param videoControl the video control
* @deprecated replaced by the new video component
*/
public void stopVideo(Object player, Object videoControl) {
}
/**
* Set the number of times the media should loop
*
* @param player the player object
* @param count the number of times the media should loop
* @deprecated replaced by the new video component
*/
public void setVideoLoopCount(Object player, int count) {
}
/**
* Return the duration of the media
*
* @param player the player object
* @return the duration of the media
* @deprecated replaced by the new video component
*/
public long getMediaTime(Object player) {
return 0;
}
/**
* "Jump" to a point in time within the media
*
* @param player the player object
* @param now the point in time to "Jump" to
* @return the media time in microseconds
* @deprecated replaced by the new video component
*/
public long setMediaTime(Object player, long now) {
return 0;
}
/**
* Toggles the fullscreen mode
*
* @param player the player object
* @param fullscreen true for fullscreen mode
* @deprecated replaced by the new video component
*/
public void setVideoFullScreen(Object player, boolean fullscreen) {
}
/**
* Paint the video for the media component
*
* @param cmp the media component
* @param fullScreen indicates whether this is fullscreen or not
* @param nativeGraphics the native graphics object
* @param video the native videoo control
* @param player the native player object
* @deprecated replaced by the version that accepts the video component instance
*/
public void paintVideo(Component cmp, boolean fullScreen, Object nativeGraphics, Object video,
Object player) {
}
/**
* Returns true if the image was opaque
*
* @param lwuitImage the lwuit image
* @param nativeImage the image object to test
* @return true if the image is opaque
*/
public boolean isOpaque(Image lwuitImage, Object nativeImage) {
int[] rgb = lwuitImage.getRGBCached();
for (int iter = 0; iter < rgb.length; iter++) {
if ((rgb[iter] & 0xff000000) != 0xff000000) {
return false;
}
}
return true;
}
/**
* Indicates whether the underlying implementation can draw using an affine
* transform hence methods such as rotate, scale and shear would work
*
* @return true if an affine transformation matrix is present
*/
public boolean isAffineSupported() {
return false;
}
/**
* Resets the affine transform to the default value
*
* @param nativeGraphics the native graphics object
*/
public void resetAffine(Object nativeGraphics) {
System.out.println("Affine unsupported");
}
/**
* Scales the coordinate system using the affine transform
*
* @param nativeGraphics the native graphics object
* @param scale factor for x
* @param scale factor for y
*/
public void scale(Object nativeGraphics, float x, float y) {
System.out.println("Affine unsupported");
}
/**
* Rotates the coordinate system around a radian angle using the affine transform
*
* @param angle the rotation angle in radians
* @param nativeGraphics the native graphics object
*/
public void rotate(Object nativeGraphics, float angle) {
System.out.println("Affine unsupported");
}
/**
* Shear the graphics coordinate system using the affine transform
*
* @param shear factor for x
* @param shear factor for y
* @param nativeGraphics the native graphics object
*/
public void shear(Object nativeGraphics, float x, float y) {
System.out.println("Affine unsupported");
}
/**
* Indicates whether the underlying platform supports creating an SVG Image
*
* @return true if the method create SVG image would return a valid image object
* from an SVG Input stream
*/
public boolean isSVGSupported() {
return false;
}
/**
* Creates an SVG Image from the given byte array data and the base URL
*
* @param baseURL URL which is used to resolve relative references within the SVG file
* @param data the conten of the SVG file
* @return a native image that can be used within the image object
* @throws IOException if resource lookup fail SVG is unsupported
*/
public Object createSVGImage(String baseURL, byte[] data) throws IOException {
throw new IOException("SVG is not supported by this implementation");
}
/**
* Returns a platform specific DOM object that can be manipulated by the user
* to change the SVG Image
*
* @param svgImage the underlying image object
* @return Platform dependent object, when JSR 226 is supported an SVGSVGElement might
* be returned.
*/
public Object getSVGDocument(Object svgImage) {
throw new RuntimeException("SVG is not supported by this implementation");
}
/**
* Callback to allow images animated by the underlying system to change their state
* e.g. for SVG or animated gif support. This method returns true if an animation
* state has changed requiring a repaint.
*
* @param nativeImage a native image used within the image object
* @param lastFrame the time the last frame of animation was shown
* @return true if a repaint is required since the image state changed, false otherwise
*/
public boolean animateImage(Object nativeImage, long lastFrame) {
return false;
}
/**
* Returns a list of the platform names ordered by priority, platform names are
* used to choose a font based on platform. Since a platform might support several
* layers for choice in narrowing platform font selection
*
* @return the platform names ordered according to priority.
*/
public String[] getFontPlatformNames() {
return new String[]{"MIDP", "MIDP2"};
}
/**
* Loads the truetype font from the input stream without closing the stream,
* this method should return the native font.
*
* @param stream from which to load the font
* @return the native font created from the stream
* @throws IOException will be thrown in case of an io error
*/
public Object loadTrueTypeFont(InputStream stream) throws IOException {
throw new IOException("Unsupported operation");
}
/**
* Returns true if the system supports dynamically loading truetype fonts from
* a stream.
*
* @return true if the system supports dynamically loading truetype fonts from
* a stream.
*/
public boolean isTrueTypeSupported() {
return false;
}
/**
* Loads a native font based on a lookup for a font name and attributes. Font lookup
* values can be separated by commas and thus allow fallback if the primary font
* isn't supported by the platform.
*
* @param lookup string describing the font
* @return the native font object
*/
public Object loadNativeFont(String lookup) {
return null;
}
/**
* Indicates whether loading a font by a string is supported by the platform
*
* @return true if the platform supports font lookup
*/
public boolean isLookupFontSupported() {
return false;
}
/**
* Minimizes the current application if minimization is supported by the platform (may fail).
* Returns false if minimization failed.
*
* @return false if minimization failed true if it succeeded or seems to be successful
*/
public boolean minimizeApplication() {
return false;
}
/**
* Restore the minimized application if minimization is supported by the platform
*/
public void restoreMinimizedApplication() {
}
/**
* Indicates whether an application is minimized
*
* @return true if the application is minimized
*/
public boolean isMinimized() {
return false;
}
/**
* Indicates whether the implementation is interested in caching radial gradients for
* drawing.
*
* @return true to activate radial gradient caching
*/
protected boolean cacheRadialGradients() {
return true;
}
/**
* Indicates whether the implementation is interested in caching linear gradients for
* drawing.
*
* @return true to activate linear gradient caching
*/
protected boolean cacheLinearGradients() {
return true;
}
/**
* Indicates the default status to apply to the 3rd softbutton variable
*
* @return true if the 3rd softbutton should be set as true
* @see com.sun.lwuit.Display#isThirdSoftButton()
* @see com.sun.lwuit.Display#setThirdSoftButton()
*/
public boolean isThirdSoftButton() {
return false;
}
/**
* Indicates how many drag points are used to calculate dragging speed
*
* @return the size of points to calculate the speed
*/
public int getDragPathLength(){
return 10;
}
/**
* Indicates what drag points are valid for the drag speed calculation.
* Points that are older then the current time - the path time are ignored
*
* @return the relevance time per point
*/
public int getDragPathTime(){
return 200;
}
/**
* This method returns the dragging speed based on the latest dragged
* events
* @param points array of locations
* @param dragPathTime the time difference between each point
* @param dragPathOffset the offset in the arrays
* @param dragPathLength
*/
public float getDragSpeed(float[] points, long[] dragPathTime,
int dragPathOffset, int dragPathLength){
long now = System.currentTimeMillis();
final long tooold = now - getDragPathTime();
int offset = dragPathOffset - dragPathLength;
if (offset < 0) {
offset = getDragPathLength() + offset;
}
long old = 0;
float oldPoint = 0;
float speed = 0;
long timediff;
float diff;
float velocity;
float f = dragPathLength;
while (dragPathLength > 0) {
if (dragPathTime[offset] > tooold) {
if (old == 0) {
old = dragPathTime[offset];
oldPoint = points[offset];
}
timediff = now - old;
diff = points[offset] - oldPoint;
if (timediff > 0) {
velocity = (diff / timediff) * 1.5f;
speed += velocity;
}
}
dragPathLength--;
offset++;
if (offset >= getDragPathLength()) {
offset = 0;
}
}
f = Math.max(1, f);
return -speed / f;
}
/**
* Indicates whether LWUIT should consider the bidi RTL algorithm
* when drawing text or navigating with the text field cursor.
*
* @return true if the bidi algorithm should be considered
*/
public boolean isBidiAlgorithm() {
return bidi;
}
/**
* Indicates whether LWUIT should consider the bidi RTL algorithm
* when drawing text or navigating with the text field cursor.
*
* @param activate set to true to activate the bidi algorithm, false to
* disable it
*/
public void setBidiAlgorithm(boolean activate) {
bidi = activate;
}
/**
* Converts the given string from logical bidi layout to visual bidi layout so
* it can be rendered properly on the screen. This method is only necessary
* for devices/platforms that don't have "built in" bidi support such as
* Sony Ericsson devices.
* See <a href="http://www.w3.org/International/articles/inline-bidi-markup/#visual">this</a>
* for more on visual vs. logical ordering.
*
*
* @param s a "logical" string with RTL characters
* @return a "visual" renderable string
*/
public String convertBidiLogicalToVisual(String s) {
if (bidi) {
if (s.length() >= 2) {
char[] c = s.toCharArray();
swapBidiChars(c, 0, s.length(), -1);
return new String(c);
}
}
return s;
}
/**
* Returns the index of the given char within the source string, the actual
* index isn't necessarily the same when bidi is involved
* See <a href="http://www.w3.org/International/articles/inline-bidi-markup/#visual">this</a>
* for more on visual vs. logical ordering.
*
* @param source the string in which we are looking for the position
* @param index the "logical" location of the cursor
* @return the "visual" location of the cursor
*/
public int getCharLocation(String source, int index) {
if (bidi) {
return swapBidiChars(source.toCharArray(), 0, source.length(), index);
}
return index;
}
private boolean isWhitespace(char c) {
return c == ' ' || (c == '\n') || (c == '\t') || (c == 10) || (c == 13);
}
/**
* Returns true if the given character is an RTL character or a space
* character
*
* @param c character to test
* @return true if bidi is active and this is a
*/
public boolean isRTLOrWhitespace(char c) {
if (bidi) {
return isRTL(c) || isWhitespace(c);
}
return false;
}
/**
* Returns true if the given character is an RTL character
*
* @param c character to test
* @return true if the charcter is an RTL character
*/
public boolean isRTL(char c) {
return (c >= RTL_RANGE_BEGIN && c <= RTL_RANGE_END);
}
private final int swapBidiChars(char[] chars, int ixStart, int len, int index) {
int destIndex = -1;
int ixEnd = ixStart + len;
int ix0, ix1;
ix0 = ix1 = ixStart;
boolean doSwap = false;
for (int i1 = ixStart; i1 < ixEnd; i1++) {
if (isRTL(chars[i1])) {
doSwap = true;
break;
}
}
if (doSwap) {
while (ix0 < ixEnd) {
if ((ix1 = scanSecond(chars, ix0, ixEnd)) < 0) {
break;
} else {
ix0 = ix1;
ix1 = scanBackFirst(chars, ix0, ixEnd);
// swap
for (int iy0 = ix0, iy1 = ix1 - 1; iy0 < iy1; iy0++, iy1--) {
char tmp = chars[iy0];
chars[iy0] = chars[iy1];
chars[iy1] = tmp;
if (index == iy1) {
//System.out.println("IY: Found char: new index="+iy0);
destIndex = iy0;
index = iy0;
}
}
ix0 = ix1;
}
}
}
if (doSwap) {
// swap the line
for (ix0 = ixStart, ix1 = ixEnd - 1; ix0 <= ix1; ix0++, ix1--) {
char ch0 = chars[ix0];
char ch1 = chars[ix1];
chars[ix0] = ch1;
chars[ix1] = ch0;
if (index == ix0) {
destIndex = ix1;
} else if (index == ix1) {
destIndex = ix0;
}
}
}
return destIndex;
}
private boolean isRTLBreak(char ch1) {
return ch1 == ')' || ch1 == ']' || ch1 == '}' || ch1 == '(' || ch1 == '[' || ch1 == '{';
}
private boolean isLTR(char c) {
return !isRTL(c) && !isRTLBreak(c);
}
private final int scanSecond(char[] chars, int ixStart, int ixEnd) {
int ixFound = -1;
for (int ix = ixStart; ixFound < 0 && ix < ixEnd; ix++) {
if (!isRTLOrWhitespace(chars[ix])) {
ixFound = ix;
}
}
return ixFound;
}
private final int scanBackFirst(char[] chars, int ixStart, int ixEnd) {
int ix, ixFound = ixEnd;
for (ix = ixStart + 1; ix < ixEnd; ix++) {
if (isRTL(chars[ix]) || isRTLBreak(chars[ix])) {
ixFound = ix;
break;
}
}
for (ix = ixFound - 1; ix >= ixStart; ix--) {
if (isLTR(chars[ix]) && !isWhitespace(chars[ix])) {
ixFound = ix + 1;
break;
}
}
return ixFound;
}
/**
* This method is essentially equivalent to cls.getResourceAsStream(String)
* however some platforms might define unique ways in which to load resources
* within the implementation.
*
* @param cls class to load the resource from
* @param resource relative/absolute URL based on the Java convention
* @return input stream for the resource or null if not found
*/
public InputStream getResourceAsStream(Class cls, String resource) {
return cls.getResourceAsStream(resource);
}
/**
* Animations should return true to allow the native image animation to update
*
* @param nativeImage underlying native imae
* @return true if this is an animation
*/
public boolean isAnimation(Object nativeImage) {
return false;
}
/**
* Creates a peer component for the given lightweight component
*
* @param nativeComponent a platform specific "native component"
* @return a LWUIT peer component that can be manipulated just like any other
* LWUIT component but would internally encapsulate the given native peer
*/
public PeerComponent createNativePeer(Object nativeComponent) {
throw new IllegalArgumentException(nativeComponent.getClass().getName());
}
/**
* Create a video component
*
* @param uri the platform specific location for the sound
* @return a VideoComponent that can be used to control the playback of the
* video
* @throws java.io.IOException if the allocation fails
*/
public VideoComponent createVideoPeer(String url) throws IOException {
throw new IllegalArgumentException("not supported");
}
/**
* Create a video component
*
* @param stream the stream containing the media data
* @param mimeType the type of the data in the stream
* @return a VideoComponent that can be used to control the playback of the
* video
* @throws java.io.IOException if the allocation fails
*/
public VideoComponent createVideoPeer(InputStream stream, String type) throws IOException {
throw new IllegalArgumentException("not supported");
}
/**
* Toggles the fullscreen mode
*
* @param v video component
* @param fullscreen true for fullscreen mode
*/
public void setVideoFullScreen(VideoComponent v, boolean fullscreen) {
}
/**
* Shows a native Form/Canvas or some other heavyweight native screen
*
* @param nativeFullScreenPeer the native screen peer
*/
public void showNativeScreen(Object nativeFullScreenPeer) {
}
/**
* Places the following commands on the native menu system
*
* @param commands the LWUIT commands to use
*/
public void setNativeCommands(Vector commands) {
}
/**
* Exits the application...
*/
public void exitApplication() {
}
/**
* Returns the property from the underlying platform deployment or the default
* value if no deployment values are supported. This is equivalent to the
* getAppProperty from the jad file.
* <p>The implementation should be responsible for the following keys to return
* reasonable valid values for the application:
* <ol>
* <li>AppName
* <li>UserAgent - ideally although not required
* <li>AppVersion
* <li>Platform - Similar to microedition.platform
* </ol>
*
* @param key the key of the property
* @param defaultValue a default return value
* @return the value of the property
*/
public String getProperty(String key, String defaultValue) {
return defaultValue;
}
/**
* Executes the given URL on the native platform
*
* @param url the url to execute
*/
public void execute(String url) {
}
/**
* Returns one of the density variables appropriate for this device, notice that
* density doesn't alwyas correspond to resolution and an implementation might
* decide to change the density based on DPI constraints.
*
* @return one of the DENSITY constants of Display
*/
public int getDeviceDensity() {
int d = getDisplayHeight() * getDisplayWidth();
if(isTablet()) {
// tablets have lower density and allow fitting more details in the screen despite a high resolution
if(d >= 1440*720) {
return Display.DENSITY_HIGH;
}
return Display.DENSITY_MEDIUM;
}
if(d <= 176*220) {
return Display.DENSITY_VERY_LOW;
}
if(d <= 240*320) {
return Display.DENSITY_LOW;
}
if(d <= 360*480) {
return Display.DENSITY_MEDIUM;
}
if(d <= 480*854) {
return Display.DENSITY_HIGH;
}
if(d <= 1440*720) {
return Display.DENSITY_VERY_HIGH;
}
return Display.DENSITY_HD;
}
/**
* Plays a builtin device sound matching the given identifier, implementations
* and themes can offer additional identifiers to the ones that are already built
* in.
*
* @param soundIdentifier the sound identifier which can match one of the
* common constants in this class or be a user/implementation defined sound
*/
public void playBuiltinSound(String soundIdentifier) {
playUserSound(soundIdentifier);
}
/**
* Plays a sound defined by the user
*
* @param soundIdentifier the sound identifier which can match one of the
* common constants in this class or be a user/implementation defined sound
* @return true if a user sound exists and was sent to playback
*/
protected boolean playUserSound(String soundIdentifier) {
Object sound = builtinSounds.get(soundIdentifier);
if(sound == null) {
return false;
}
playAudio(sound);
return true;
}
/**
* This method allows implementations to store sound objects natively e.g.
* in files, byte arrays whatever
*
* @param data native data object
*/
protected void playNativeBuiltinSound(Object data) {
}
/**
* Converts a sound object to a form which will be easy for the implementation
* to play later on. E.g. a byte array or a file/file name and return an object
* that will allow playNativeBuiltinSound() to use
*
* @param i stream containing a sound file
* @return native playback object
* @throws java.io.IOException thrown by the stream
*/
protected Object convertBuiltinSound(InputStream i) throws IOException {
ByteArrayOutputStream b = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int size = i.read(buffer);
while(size > -1) {
b.write(buffer, 0, size);
size = i.read(buffer);
}
b.close();
i.close();
return b.toByteArray();
}
/**
* Installs a replacement sound as the builtin sound responsible for the given
* sound identifier (this will override the system sound if such a sound exists).
*
* @param soundIdentifier the sound string passed to playBuiltinSound
* @param data an input stream containing platform specific audio file, its usually safe
* to assume that wav/mp3 would be supported.
* @throws IOException if the stream throws an exception
*/
public void installBuiltinSound(String soundIdentifier, InputStream data) throws IOException {
builtinSounds.put(soundIdentifier, convertBuiltinSound(data));
}
/**
* Indicates whether a user installed or system sound is available
*
* @param soundIdentifier the sound string passed to playBuiltinSound
* @return true if a sound of this given type is avilable
*/
public boolean isBuiltinSoundAvailable(String soundIdentifier) {
return builtinSounds.containsKey(soundIdentifier);
}
/**
* Allows muting/unmuting the builtin sounds easily
*
* @param enabled indicates whether the sound is muted
*/
public void setBuiltinSoundsEnabled(boolean enabled) {
builtinSoundEnabled = enabled;
}
/**
* Allows muting/unmuting the builtin sounds easily
*
* @return true if the sound is *not* muted
*/
public boolean isBuiltinSoundsEnabled() {
return builtinSoundEnabled;
}
/**
* Plays the sound in the given URI which is partially platform specific.
*
* @param uri the platform specific location for the sound
* @param onCompletion invoked when the audio file finishes playing, may be null
* @return a handle that can be used to control the playback of the audio
* @throws java.io.IOException if the URI access fails
*/
public Object createAudio(String uri, Runnable onCompletion) throws IOException {
return null;
}
/**
* Plays the sound in the given stream
*
* @param stream the stream containing the media data
* @param mimeType the type of the data in the stream
* @param onCompletion invoked when the audio file finishes playing, may be null
* @return a handle that can be used to control the playback of the audio
* @throws java.io.IOException if the URI access fails
*/
public Object createAudio(InputStream stream, String mimeType, Runnable onCompletion) throws IOException {
return null;
}
/**
* Starts playing the audio file
*
* @param handle the handle object returned by create audio
*/
public void playAudio(Object handle) {
}
/**
* Pauses the playback of the audio file
*
* @param handle the handle object returned by create audio
*/
public void pauseAudio(Object handle) {
}
/**
* Returns the time in seconds in the audio file
*
* @param handle the handle object returned by play audio
* @return time in milli seconds
*/
public int getAudioTime(Object handle) {
return -1;
}
/**
* Sets the position in the audio file
*
* @param handle the handle object returned by play audio
* @param time in milli seconds
*/
public void setAudioTime(Object handle, int time) {
}
/**
* Returns the length in milli seconds of the audio file
*
* @param handle the handle object returned by play audio
* @return time in milli seconds
*/
public int getAudioDuration(Object handle) {
return -1;
}
/**
* Stops the audio playback and cleans up the resources related to it immediately.
*
* @param handle the playback handle
*/
public void cleanupAudio(Object handle) {
}
/**
* Sets the media playback volume in percentage
*
* @param vol the volume for media playback
*/
public void setVolume(int vol) {
}
/**
* Returns the media playback volume in percentage
*
* @return the volume percentage
*/
public int getVolume() {
return -1;
}
/**
* Creates a soft/weak reference to an object that allows it to be collected
* yet caches it. This method is in the porting layer since CLDC only includes
* weak references while some platforms include nothing at all and some include
* the superior soft references.
*
* @param o object to cache
* @return a caching object or null if caching isn't supported
*/
public Object createSoftWeakRef(Object o) {
return new WeakReference(o);
}
/**
* Extracts the hard reference from the soft/weak reference given
*
* @param o the reference returned by createSoftWeakRef
* @return the original object submitted or null
*/
public Object extractHardRef(Object o) {
WeakReference w = (WeakReference)o;
if(w != null) {
return w.get();
}
return null;
}
/**
* This method notifies the implementation about the chosen commands
* behavior
* @param commandBehavior see Display.COMMAND_BEHAVIOR...
*/
public void notifyCommandBehavior(int commandBehavior){
}
/**
* Indicates if the implemenetation has a native underlying theme
*
* @return true if the implementation has a native theme available
*/
public boolean hasNativeTheme() {
return false;
}
/**
* Installs the native theme, this is only applicable if hasNativeTheme() returned true. Notice that this method
* might replace the DefaultLookAndFeel instance and the default transitions.
*/
public void installNativeTheme() {
throw new RuntimeException();
}
/**
* Performs a clipboard copy operation, if the native clipboard is supported by the implementation it would be used
*
* @param obj object to copy, while this can be any arbitrary object it is recommended that only Strings or LWUIT
* image objects be used to copy
*/
public void copyToClipboard(Object obj) {
lightweightClipboard = obj;
}
/**
* Returns the current content of the clipboard
*
* @return can be any object or null see copyToClipboard
*/
public Object getPasteDataFromClipboard() {
return lightweightClipboard;
}
/**
* Returns true if the device is currently in portrait mode
*
* @return true if the device is in portrait mode
*/
public boolean isPortrait() {
return getDisplayWidth() < getDisplayHeight();
}
/**
* Returns true if the device allows forcing the orientation via code, feature phones do not allow this
* although some include a jad property allowing for this feature
*
* @return true if lockOrientation would work
*/
public boolean canForceOrientation() {
return false;
}
/**
* On devices that return true for canForceOrientation() this method can lock the device orientation
* either to portrait or landscape mode
*
* @param portrait true to lock to portrait mode, false to lock to landscape mode
*/
public void lockOrientation(boolean portrait) {
}
/**
* An implementation can return true if it supports embedding a native browser widget
*
* @return true if the implementation supports embedding a native browser widget
*/
public boolean isNativeBrowserComponentSupported() {
return false;
}
/**
* If the implementation supports the creation of a browser component it should be returned in this
* method
*
* @param browserComponent instance of the browser component thru which events should be fired
* @return an instance of the native browser peer or null
*/
public PeerComponent createBrowserComponent(Object browserComponent) {
return null;
}
/**
* This method allows customizing the properties of a web view in various ways including platform specific settings.
* When a property isn't supported by a specific platform it is just ignored.
*
* @param browserPeer browser instance
* @param key see the documentation with the LWUIT Implementation for further details
* @param value see the documentation with the LWUIT Implementation for further details
*/
public void setBrowserProperty(PeerComponent browserPeer, String key, Object value) {
}
/**
* The page title
* @param browserPeer browser instance
* @return the title
*/
public String getBrowserTitle(PeerComponent browserPeer) {
return null;
}
/**
* The page URL
* @param browserPeer browser instance
* @return the URL
*/
public String getBrowserURL(PeerComponent browserPeer) {
return null;
}
/**
* Sets the page URL, jar: URL's must be supported by the implementation
* @param browserPeer browser instance
* @param url the URL
*/
public void setBrowserURL(PeerComponent browserPeer, String url) {
// load from jar:// URL's
try {
InputStream i = Display.getInstance().getResourceAsStream(getClass(), url.substring(6));
if(i == null) {
System.out.println("Local resource not found: " + url);
return;
}
byte[] buffer = new byte[4096];
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int size = i.read(buffer);
while(size > -1) {
bo.write(buffer, 0, size);
size = i.read(buffer);
}
i.close();
bo.close();
String htmlText = new String(bo.toByteArray(), "UTF-8");
String baseUrl = url.substring(0, url.lastIndexOf('/'));
setBrowserPage(browserPeer, htmlText, baseUrl);
return;
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Reload the current page
* @param browserPeer browser instance
*/
public void browserReload(PeerComponent browserPeer) {
}
/**
* Indicates whether back is currently available
* @param browserPeer browser instance
* @return true if back should work
*/
public boolean browserHasBack(PeerComponent browserPeer) {
return false;
}
/**
* Indicates whether forward is currently available
* @param browserPeer browser instance
* @return true if forward should work
*/
public boolean browserHasForward(PeerComponent browserPeer) {
return false;
}
/**
* Navigates back in the history
* @param browserPeer browser instance
*/
public void browserBack(PeerComponent browserPeer) {
}
/**
* Navigates forward in the history
* @param browserPeer browser instance
*/
public void browserForward(PeerComponent browserPeer) {
}
/**
* Clears navigation history
* @param browserPeer browser instance
*/
public void browserClearHistory(PeerComponent browserPeer) {
}
/**
* Shows the given HTML in the native viewer
*
* @param browserPeer browser instance
* @param html HTML web page
* @param baseUrl base URL to associate with the HTML
*/
public void setBrowserPage(PeerComponent browserPeer, String html, String baseUrl) {
}
/**
* Executes the given JavaScript string within the current context
*
* @param browserPeer browser instance
* @param javaScript the JavaScript string
*/
public void browserExecute(PeerComponent browserPeer, String javaScript) {
setBrowserURL(browserPeer, "javascript:(" + javaScript + ")()");
}
/**
* Allows exposing the given object to JavaScript code so the JavaScript code can invoke methods
* and access fields on the given object. Notice that on RIM devices which don't support reflection
* this object must implement the propriatery Scriptable interface
* http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/script/Scriptable.html
*
* @param browserPeer browser instance
* @param o the object to invoke, notice all public fields and methods would be exposed to JavaScript
* @param name the name to expose within JavaScript
*/
public void browserExposeInJavaScript(PeerComponent browserPeer, Object o, String name) {
}
/**
* Converts the dips count to pixels, dips are roughly 1mm in length. This is a very rough estimate and not
* to be relied upon
*
* @param dipCount the dips that we will convert to pixels
* @param horizontal indicates pixels in the horizontal plane
* @return value in pixels
*/
public int convertToPixels(int dipCount, boolean horizontal) {
switch(getDeviceDensity()) {
case Display.DENSITY_VERY_LOW:
return dipCount;
case Display.DENSITY_LOW:
return dipCount * 2;
case Display.DENSITY_MEDIUM:
return dipCount * 5;
case Display.DENSITY_HIGH:
return dipCount * 10;
case Display.DENSITY_VERY_HIGH:
return dipCount * 14;
case Display.DENSITY_HD:
return dipCount * 20;
}
return dipCount;
}
/**
* Indicates whether the device is a tablet, notice that this is often a guess
*
* @return true if the device is assumed to be a tablet
*/
public boolean isTablet() {
return false;
}
}