javauis/lcdui_qt/src/javax/microedition/lcdui/Canvas.java
branchRCL_3
changeset 65 ae942d28ec0e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_qt/src/javax/microedition/lcdui/Canvas.java	Tue Aug 31 15:09:22 2010 +0300
@@ -0,0 +1,1553 @@
+/*
+* Copyright (c) 2009, 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+package javax.microedition.lcdui;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.Timer;
+import java.util.TimerTask;
+import javax.microedition.lcdui.game.GameCanvas;
+import javax.microedition.lcdui.EventDispatcher.LCDUIEvent;
+import org.eclipse.ercp.swt.mobile.MobileShell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.qt.graphics.WindowSurface;
+import org.eclipse.swt.internal.extension.MobileShellExtension;
+import org.eclipse.swt.internal.qt.SymbianWindowVisibilityListener;
+
+
+/**
+ * The abstract <code>Canvas</code> class is designed to handle low-level
+ * graphical operations as well as low-level key and pointer events. Canvas is
+ * derived from <code>Displayable</code> so the application can switch between
+ * different <code>Canvases</code> together with other
+ * <code>Displayables</code>.
+ * <p>
+ * <b>Canvas</b> the abstract method <code>paint(Graphics g)</code> designed
+ * to be implemented by the developer.
+ */
+public abstract class Canvas extends Displayable
+{
+
+    /**
+     *  Constant for <code>UP</code> game action.
+     */
+    public static final int UP = 1;
+
+    /**
+     * Constant for <code>DOWN</code> game action.
+     */
+    public static final int DOWN = 6;
+
+    /**
+     * Constant for <code>LEFT</code> game action.
+     */
+    public static final int LEFT = 2;
+
+    /**
+     * Constant for <code>RIGHT</code> game action.
+     */
+    public static final int RIGHT = 5;
+
+    /**
+     * Constant for <code>FIRE</code> game action.
+     */
+    public static final int FIRE = 8;
+
+    /**
+     * Constant for general "<code>A</code>" game action.
+     */
+    public static final int GAME_A = 9;
+
+    /**
+     * Constant for general "<code>B</code>" game action.
+     */
+    public static final int GAME_B = 10;
+
+    /**
+     * Constant for general "<code>C</code>" game action.
+     */
+    public static final int GAME_C = 11;
+
+    /**
+     * Constant for general "<code>D</code>" game action.
+     */
+    public static final int GAME_D = 12;
+
+    /**
+     * Key code for <code>0</code> key.
+     */
+    public static final int KEY_NUM0 = 48;
+
+    /**
+     * Key code for <code>1</code> key.
+     */
+    public static final int KEY_NUM1 = 49;
+
+    /**
+     * Key code for <code>2</code> key.
+     */
+    public static final int KEY_NUM2 = 50;
+
+    /**
+     * Key code for <code>3</code> key.
+     */
+    public static final int KEY_NUM3 = 51;
+
+    /**
+     * Key code for <code>4</code> key.
+     */
+    public static final int KEY_NUM4 = 52;
+
+    /**
+     * Key code for <code>5</code> key.
+     */
+    public static final int KEY_NUM5 = 53;
+
+    /**
+     * Key code for <code>6</code> key.
+     */
+    public static final int KEY_NUM6 = 54;
+
+    /**
+     * Key code for <code>7</code> key.
+     */
+    public static final int KEY_NUM7 = 55;
+
+    /**
+     * Key code for <code>8</code> key.
+     */
+    public static final int KEY_NUM8 = 56;
+
+    /**
+     * Key code for <code>9</code> key.
+     */
+    public static final int KEY_NUM9 = 57;
+
+    /**
+     * Key code for <code>*</code> key.
+     */
+    public static final int KEY_STAR = 42;
+
+    /**
+     * Key code for <code>#</code> key.
+     */
+    public static final int KEY_POUND = 35;
+
+
+    private static final int GAME_CANVAS = 1;
+    private static final int NO_BACKGROUND = 1 << 1;
+    private static final int FULLSCREEN_MODE = 1 << 2;
+
+    private static final int DISABLE_TAPDETECTION = 1 << 3;
+    private static final int SUPPRESS_GAMEKEYS = 1 << 4;
+    private static final int SUPPRESS_DRAGEVENT = 1 << 5;
+    private static final int CLEANUP_NEEDED = 1 << 6;
+    private static final int REPAINT_PENDING = 1 << 7;
+    private static final int SELECTIONKEY_COMPATIBILITY = 1 << 8;
+
+    private static final int CURRENTLY_VISIBLE = 1 << 9;
+
+
+    // Listeners for various events.
+    private org.eclipse.swt.events.PaintListener paintListener =
+        new CanvasShellPaintListener();
+
+    private CanvasShellMouseListener mouseListener =
+        new CanvasShellMouseListener();
+
+    private CanvasShellVisibilityListener shellVisibilityListener =
+        new CanvasShellVisibilityListener();
+
+    // Canvas Graphics object passed to paint(Graphics g)
+    private Graphics canvasGraphics;
+
+    // Graphics object for transferring return values
+    // from UI thread
+    private Graphics tempGraphics;
+
+    // Graphics command buffer for this instance
+    Buffer graphicsBuffer;
+
+    //On Screen Keypad
+    //private Composite keypadComposite;
+    private CanvasKeypad onScreenkeypad;
+    private static CanvasKeypad sharedKeypad;
+
+    // Vector of flags that a certain key was pressed but was not released.
+    // Used to implement keyRepeated since eSWT does not support
+    // key repeat events.
+    private Vector keysPressed;
+    private int gameKeyState;
+
+    private static int objectCount;
+    private static Shell sharedShell;
+    private Shell mShell;
+    private Composite canvasComp;
+    private Label tickerLabel;
+
+    private int mode;
+    private Object modeLock;
+    private Object cleanupLock;
+    private Object repaintLock;
+    private Object flushLock;
+
+    private Timer timer = new Timer();
+    private CanvasTimerTask timerTask;
+
+    private static final int DEFAULT_TIMEOUT = 500;
+    private static final int DEFAULT_TWIPS = 200;
+
+    private int pointerDownX;
+    private int pointerDownY;
+    private int twips;
+    private int timeout;
+
+    private int repaintX1;
+    private int repaintY1;
+    private int repaintX2;
+    private int repaintY2;
+
+
+    /**
+     * Constructs <code>Canvas</code> object.
+     */
+    public Canvas()
+    {
+        super(null);
+        synchronized(this)
+        {
+            objectCount++;
+        }
+
+        modeLock = new Object();
+        repaintLock = new Object();
+        cleanupLock = new Object();
+        flushLock = new Object();
+        setMode(GAME_CANVAS, this instanceof GameCanvas);
+        construct();
+        keysPressed = new Vector();
+    }
+
+    /**
+     * Disposes this instance
+     * Called when finalizer is destroying this instance.
+     */
+    void dispose()
+    {
+        ESWTUIThreadRunner.update(getClass().getName(), -1);
+        ESWTUIThreadRunner.safeSyncExec(new Runnable()
+        {
+            public void run()
+            {
+                if(graphicsBuffer != null)
+                {
+                    graphicsBuffer.dispose();
+                    graphicsBuffer = null;
+                }
+
+                synchronized(this)
+                {
+                    objectCount--;
+
+                    if((objectCount == 0) || isMode(GAME_CANVAS))
+                    {
+                        mShell.dispose();
+                        sharedShell = null;
+                        sharedKeypad = null;
+                    }
+                    else
+                    {
+                        Ticker ticker = getTicker();
+                        if (ticker != null)
+                        {
+                            ticker.removeLabel(tickerLabel);
+                        }
+                        if(tickerLabel != null)
+                        {
+                            tickerLabel.dispose();
+                        }
+
+                        canvasComp.dispose();
+                    }
+                }
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see Displayable#eswtConstructShell(int)
+     */
+    Shell eswtConstructShell(int style)
+    {
+        if(isMode(GAME_CANVAS))
+        {
+            mShell = super.eswtConstructShell(style);
+        }
+        else
+        {
+            if(sharedShell == null)
+            {
+                sharedShell = super.eswtConstructShell(style);
+            }
+            mShell = sharedShell;
+        }
+
+        // Give the Shell the maximized size already before it becomes visible
+        // so that it will return the correct size.
+        mShell.setBounds(org.eclipse.swt.widgets.Display.getCurrent().getClientArea());
+
+        // Make the Shell maximized. On Symbian it's automatically maximized
+        // so this has no effect but on other platforms explicit maximizing
+        // might be needed.
+        mShell.setMaximized(true);
+
+        return mShell;
+    }
+
+    /**
+     * Sets ticker. If ticker is added already to other canvas(es),
+     * it continues running from position where it was. Otherwise
+     * it will start running from beginning when this method returns.
+     *
+     * @param newTicker New ticker. If null, current ticker is removed.
+     */
+    public void setTicker(Ticker newTicker)
+    {
+        super.setTicker(newTicker);
+
+        ESWTUIThreadRunner.syncExec(new Runnable()
+        {
+            public void run()
+            {
+                tickerLabel.setVisible(isMode(CURRENTLY_VISIBLE));
+            }
+        });
+    }
+
+    /**
+     * Creates singleton Label instance used by Ticker.
+     * Creates tickerLabel on shell and sets the visibility of the same.
+     */
+    Label getTickerLabel()
+    {
+        tickerLabel = super.getTickerLabel();
+
+        if (!isMode(CURRENTLY_VISIBLE))
+        {
+            ESWTUIThreadRunner.syncExec(new Runnable()
+            {
+                public void run()
+                {
+                    tickerLabel.setVisible(false);
+                }
+            });
+        }
+
+        return tickerLabel;
+    }
+
+    /* (non-Javadoc)
+     * @see Displayable#eswtConstructContent(int)
+     */
+    Composite eswtConstructContent(int style)
+    {
+        // Get JAD attribute
+        setMode(NO_BACKGROUND, !JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_UI_ENHANCEMENT,
+                                                JadAttributeUtil.VALUE_CANVAS_HAS_BACKGROUND));
+        if(isMode(NO_BACKGROUND))
+        {
+            style |= SWT.NO_BACKGROUND;
+        }
+
+        // Get JAD attribute for S60 Selection Key Compatibility
+        setMode(SELECTIONKEY_COMPATIBILITY, JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_S60_SELECTION_KEY_COMPATIBILITY,
+                                    JadAttributeUtil.VALUE_TRUE));
+
+        // Get JAD attribute for MIDlet Tap Detection
+        String tapAttr = JadAttributeUtil.getValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_TAP_DETECTION_OPTIONS);
+        if(tapAttr != null)
+        {
+            if(tapAttr.indexOf(',') == -1)
+            {
+                setDefaultTapValues();
+            }
+            else
+            {
+                String twipString = tapAttr.substring(0, tapAttr.indexOf(',')).trim();
+                String timeoutString = tapAttr.substring(tapAttr.indexOf(',') + 1, tapAttr.length()).trim();
+
+                try
+                {
+                    twips = Integer.parseInt(twipString);
+                    timeout = Integer.parseInt(timeoutString);
+
+                    // Check for Negative Values
+                    if((twips < 0) && (timeout < 0))
+                    {
+                        setDefaultTapValues();
+                    }
+
+                    if((twips == 0)  && (timeout == 0))
+                    {
+                        setMode(DISABLE_TAPDETECTION, true);
+                    }
+
+                    // if any one of the value is zero, set defaults
+                    if(!((twips != 0) && (timeout != 0)))
+                    {
+                        setDefaultTapValues();
+                    }
+                }
+                catch(NumberFormatException e)
+                {
+                    // Alpha Numeric Values of Timeouts and Timeout
+                    setDefaultTapValues();
+                }
+            }
+        }
+        else
+        {
+            setDefaultTapValues();
+        }
+
+        canvasComp = super.eswtConstructContent(style);
+        canvasComp.setVisible(false);
+
+        createOnScreenKeypad();
+        return canvasComp;
+    }
+
+    void eswtInitGraphics() {
+        // create graphics buffer
+       graphicsBuffer = Buffer.createInstance(this, canvasComp);
+    }
+
+    /**
+     * Creates OSK(OnScreenKeypad), shared Keypad will be created for Canvas,
+     * seperate OSK will be created for each GameCanvas.
+     */
+    CanvasKeypad createOnScreenKeypad()
+    {
+        // Read the on screen keypad settings from the jad attribute
+        String oskAttr = JadAttributeUtil
+                         .getValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_ON_SCREEN_KEYPAD);
+        if(oskAttr != null
+                && (!oskAttr.equalsIgnoreCase(JadAttributeUtil.VALUE_NO))
+                && ((oskAttr
+                     .equalsIgnoreCase(JadAttributeUtil.VALUE_GAMEACTIONS)) || (oskAttr
+                             .equalsIgnoreCase(JadAttributeUtil.VALUE_NAVIGATIONKEYS))))
+        {
+            // On screen keypad is required, On devices without keyboard it can
+            // be either navigation keys or navigation and game keys
+
+            if(isMode(GAME_CANVAS))
+            {
+                onScreenkeypad = new CanvasKeypad(this, oskAttr);
+                return onScreenkeypad;
+            }
+
+            if(sharedKeypad == null)
+            {
+                sharedKeypad = new CanvasKeypad(this, oskAttr);
+            }
+            onScreenkeypad = sharedKeypad;
+            return onScreenkeypad;
+        }
+
+        return null;
+    }
+
+    Rectangle eswtLayoutShellContent()
+    {
+        Rectangle shellArea = mShell.getClientArea();
+        int oskHeight = (onScreenkeypad != null ? onScreenkeypad.getHeight() : 0);
+        int tickerHeight = (tickerLabel != null ? tickerLabel.getBounds().height : 0);
+
+        canvasComp.setBounds(0, tickerHeight,
+                              shellArea.width, shellArea.height - tickerHeight - oskHeight);
+
+        canvasComp.setFocus();
+        return canvasComp.getClientArea();
+    }
+
+    /* (non-Javadoc)
+     * @see Displayable#eswtHandleShowCurrentEvent()
+     */
+    void eswtHandleShowCurrentEvent()
+    {
+        setMode(CURRENTLY_VISIBLE, true);
+        eswtSetTitle();
+        ((MobileShell) mShell).setFullScreenMode(isMode(FULLSCREEN_MODE));
+        if(onScreenkeypad != null)
+        {
+            if(!isMode(GAME_CANVAS))
+            {
+                onScreenkeypad.setCurrentCanvas(this);
+            }
+            onScreenkeypad.setFullScreenMode(isMode(FULLSCREEN_MODE));
+        }
+        canvasComp.setVisible(true);
+        if(tickerLabel != null)
+        {
+            tickerLabel.setVisible(!isMode(FULLSCREEN_MODE));
+        }
+        addCommands();
+        super.eswtHandleShowCurrentEvent();
+        getContentComp().addPaintListener(paintListener);
+        getContentComp().addMouseListener(mouseListener);
+        getContentComp().addMouseMoveListener(mouseListener);
+        ((MobileShellExtension)getShell()).addSymbianWindowVisibilityListener(shellVisibilityListener);
+    }
+
+    /* (non-Javadoc)
+     * @see Displayable#eswtHandleHideCurrentEvent()
+     */
+    void eswtHandleHideCurrentEvent()
+    {
+        setMode(CURRENTLY_VISIBLE, false);
+        canvasComp.setVisible(false);
+        if(tickerLabel != null)
+        {
+            tickerLabel.setVisible(false);
+        }
+        removeCommands();
+        super.eswtHandleHideCurrentEvent();
+        getContentComp().removePaintListener(paintListener);
+        getContentComp().removeMouseListener(mouseListener);
+        getContentComp().removeMouseMoveListener(mouseListener);
+        ((MobileShellExtension)getShell()).removeSymbianWindowVisibilityListener(shellVisibilityListener);
+    }
+
+    /**
+     * eSWT callback to add a Command.
+     */
+    void eswtAddCommand(Command cmd) {
+        if (isMode(CURRENTLY_VISIBLE)) {
+            cmd.eswtAddESWTCommand(mShell, false);
+        }
+        if (eswtIsShown()) {
+            cmd.eswtAddCommandSelectionListener(mShell, getCommandListener());
+        }
+    }
+
+    /**
+     * Adds the commands to this Canvas.
+     * Adds all the commands of displayable to the shell.
+     */
+    void addCommands()
+    {
+        Command cmd = null;
+        for (Enumeration e = getCommands().elements(); e.hasMoreElements();)
+        {
+            cmd = (Command) e.nextElement();
+            final Command finalCommand = cmd;
+            finalCommand.eswtAddESWTCommand(mShell, false);
+        }
+    }
+
+    /**
+     * Removes the commands from this Canvas.
+     * Removes all the commands of displayable from the shell.
+     */
+    void removeCommands()
+    {
+        Command cmd = null;
+        for (Enumeration e = getCommands().elements(); e.hasMoreElements();)
+        {
+            cmd = (Command) e.nextElement();
+            final Command finalCommand = cmd;
+            finalCommand.eswtRemoveESWTCommand(mShell);
+        }
+    }
+
+    /**
+     * Issues the request to repaint the whole Canvas.
+     */
+    public void repaint()
+    {
+        repaint(0, 0, getWidth(), getHeight());
+    }
+
+    /**
+     * Issues the request to repaint the specified area of the Canvas. The
+     * request is processed asynchronously.
+     *
+     * @param x - left bound of the rectangle to redraw.
+     * @param y - top bound of the rectangle to redraw.
+     * @param width - width of the rectangle to redraw.
+     * @param height - height of the rectangle to redraw.
+     */
+    public void repaint(int x, int y, int width, int height)
+    {
+        // Paint callback event is posted without any invalid area info.
+        // Invalid area info is kept in the member variables. Only one event
+        // per Canvas is added to the queue. If there are more repaint() calls
+        // before the already posted event has been served those are merged
+        // to the invalid area in the member variables without posting a new
+        // event.
+        synchronized(repaintLock)
+        {
+            if(invalidate(x, y, width, height))
+            {
+                // Note that repaintPending doesn't mean that there's a repaint
+                // event in the queue. It means that the invalid area is going
+                // to be repainted and there's no need to add a new event.
+                // It's possible that the repaint event has already been
+                // removed from the queue but repaintPending is still true. In
+                // that case it's currently being processed and we can still
+                // add to the invalid area.
+                if(!isMode(REPAINT_PENDING))
+                {
+                    EventDispatcher eventDispatcher = EventDispatcher.instance();
+                    LCDUIEvent event = eventDispatcher.newEvent(
+                                           LCDUIEvent.CANVAS_PAINT_MIDLET_REQUEST, this);
+                    event.widget = getContentComp();
+                    eventDispatcher.postEvent(event);
+                    setMode(REPAINT_PENDING, true);
+                }
+            }
+        }
+    }
+
+    /**
+     * Switches the Canvas between full screen and non-full screen modes.
+     *
+     * @param mode - true switches the Canvas to the full-screen mode.
+     */
+    public void setFullScreenMode(boolean aMode)
+    {
+        setMode(FULLSCREEN_MODE, aMode);
+        ESWTUIThreadRunner.syncExec(new Runnable()
+        {
+            public void run()
+            {
+                if(tickerLabel != null)
+                {
+                    if(isMode(FULLSCREEN_MODE))
+                    {
+                        tickerLabel.setBounds(Integer.MIN_VALUE, 0, 0, 0);
+                    }
+                    else
+                    {
+                        tickerLabel.pack();
+                        tickerLabel.setLocation(Integer.MIN_VALUE, 0);
+                    }
+
+                    if(isMode(CURRENTLY_VISIBLE))
+                    {
+                        tickerLabel.setVisible(!isMode(FULLSCREEN_MODE));
+                    }
+                }
+
+                if(isMode(CURRENTLY_VISIBLE))
+                {
+                    ((MobileShell)mShell).setFullScreenMode(isMode(FULLSCREEN_MODE));
+                    //set the CanvasKeypad to the required mode
+                    if(onScreenkeypad != null)
+                    {
+                        onScreenkeypad.setFullScreenMode(isMode(FULLSCREEN_MODE));
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Processes all the issued paint requests.
+     */
+    public final void serviceRepaints()
+    {
+        EventDispatcher.instance().serviceRepaints(this);
+    }
+
+    /**
+     * Checks if the Canvas supports pointer dragging events.
+     *
+     * @return true if the Canvas supports pointer dragging, false otherwise.
+     */
+    public boolean hasPointerMotionEvents()
+    {
+        return true;
+    }
+
+    /**
+     * Checks if the Canvas supports pointer events.
+     *
+     * @return true if the Canvas supports pointer events, false otherwise.
+     */
+    public boolean hasPointerEvents()
+    {
+        return true;
+    }
+
+    /**
+     * Is the Canvas double buffered.
+     *
+     * @return true if the Canvas is double buffered, false otherwise.
+     */
+    public boolean isDoubleBuffered()
+    {
+        return true;
+    }
+
+    /**
+     * Queries if the Canvas supports key repeat events.
+     *
+     * @return true if Canvas supports key repeat events, false otherwise.
+     */
+    public boolean hasRepeatEvents()
+    {
+        return true;
+    }
+
+    /**
+     * Returns game action for a specified key code.
+     *
+     * @param keyCode Key code to map to game action.
+     * @return Game action for a a specified key code or
+     *         IllegalArgumentException.
+     */
+    public int getGameAction(int keyCode)
+    {
+        return KeyTable.getGameAction(keyCode);
+    }
+
+    /**
+     * Returns the key code specific for a certain game action.
+     *
+     * @param gameAction - game action to be mapped to the key code.
+     * @return Key code that is mapped to the specified game action.
+     */
+    public int getKeyCode(int gameAction)
+    {
+        return KeyTable.getKeyCode(gameAction);
+    }
+
+    /**
+     * Returns the key name specific for a certain key code.
+     *
+     * @param keyCode - key name to get the name of.
+     * @return String that contains textual name of the key specified by the key
+     *         code.
+     */
+    public String getKeyName(int keyCode)
+    {
+        return KeyTable.getKeyName(keyCode);
+    }
+
+    /**
+     * Callback to be implemented by the application to render the
+     * <code>Canvas</code>. The clip region of <code>Graphics</code> object
+     * specifies the area that needs to be updated. All the region has to be
+     * updated in <code>paint()</code> method.
+     * <p>
+     * Application should not take into account the source where the
+     * <code>paint()</code> request came from.
+     * <p>
+     * Any interaction with Graphics object outside of <code>paint()</code>
+     * method is undefined.
+     * <p>
+     * The application should never call <code>paint()</code> explicitly, it is
+     * called by the framework.
+     *
+     * @param g - the <code>Graphics</code> object to be used for graphical
+     *            operations on the <code>Canvas.</code>
+     */
+    protected abstract void paint(Graphics g);
+
+    /**
+     * Callback to signal a key press.
+     *
+     * @param keyCode - key code of the key pressed.
+     */
+    protected void keyPressed(int keyCode)
+    {
+    }
+
+    /**
+     * Callback to signal a key release.
+     *
+     * @param keyCode - key code of the key released.
+     */
+    protected void keyReleased(int keyCode)
+    {
+    }
+
+    /**
+     * Callback to signal a key repeat. Happens when the key is pressed down for
+     * a long time.
+     *
+     * @param keyCode - key code of the key repeated.
+     */
+    protected void keyRepeated(int keyCode)
+    {
+    }
+
+    /**
+     * Callback to signal pointer press event.
+     *
+     * @param x - X-coordinate of the tap point.
+     * @param y - Y-coordinate of the tap point.
+     */
+    protected void pointerPressed(int x, int y)
+    {
+    }
+
+    /**
+     * Callback to signal pointer release event.
+     *
+     * @param x - X-coordinate of the pointer release point.
+     * @param y - Y-coordinate of the pointer release point.
+     */
+    protected void pointerReleased(int x, int y)
+    {
+    }
+
+    /**
+     * Callback to signal pointer drag event.
+     *
+     * @param x - X-coordinate of the pointer drag point.
+     * @param y - Y-coordinate of the pointer drag point.
+     */
+    protected void pointerDragged(int x, int y)
+    {
+    }
+
+    /**
+     * Callback similar to the one in <code>Displayable</code>. Additional
+     * case for <code>sizeChanged</code> to be called in <code>Canvas</code>
+     * is switching between full-screen mode by calling
+     * <code>setFullScreenMode()</code>.
+     */
+    protected void sizeChanged(int w, int h)
+    {
+    }
+
+    /**
+     * showNotify() is the callback called before the Canvas is shown on the
+     * screen.
+     */
+    protected void showNotify()
+    {
+    }
+
+    /**
+     * hideNotify() is the callback called after the Canvas is removed from the
+     * screen.
+     */
+    protected void hideNotify()
+    {
+    }
+
+    /**
+     * Init GameCanvas frame buffer.
+     *
+     * @param supressKeys suppress game keys
+     * @return frame buffer
+     */
+    final void initGameCanvas(boolean suppressKeys)
+    {
+        setMode(SUPPRESS_GAMEKEYS, suppressKeys);
+    }
+
+    /**
+     * Gets composite that contains Canvas content.
+     *
+     * @return Composite.
+     */
+    Composite getContentComp()
+    {
+        return canvasComp;
+    }
+
+    /**
+     * Get game canvas frame buffer graphics.
+     */
+    final Graphics getGameBufferGraphics()
+    {
+        tempGraphics = null;
+        ESWTUIThreadRunner.safeSyncExec(new Runnable()
+        {
+            public void run()
+            {
+                tempGraphics =  graphicsBuffer.getGraphics();
+            }
+        });
+        return tempGraphics;
+    }
+
+    CanvasKeypad getCanvasKeypad()
+    {
+        return onScreenkeypad;
+    }
+
+    boolean IsFullScreenMode()
+    {
+        return isMode(FULLSCREEN_MODE);
+    }
+
+    /**
+     * Return game key states for GameCanvas.
+     *
+     * @return game key states.
+     */
+    final int getGameKeyStates()
+    {
+        int ret = gameKeyState;
+        gameKeyState = 0;
+        return ret;
+    }
+
+    void renderGraphics(final Graphics g)
+    {
+        // Implementation missing. GameCanvas.paint() should by default render
+        // the the off-screen buffer at (0,0). Rendering of the buffer must be
+        // subject to the clip region and origin translation of the Graphics
+        // object.
+    }
+
+    /**
+     * Flushes the frameBuffer to the display.
+     *
+     * @param x
+     * @param y
+     * @param width
+     * @param height
+     */
+    void flushGameBuffer(final int x, final int y, final int width,
+                         final int height)
+    {
+        // This is serialized with the
+        // paint callback processing
+        synchronized(flushLock)
+        {
+            synchronized(graphicsBuffer)
+            {
+                 ESWTUIThreadRunner.safeSyncExec(new Runnable()
+                {
+                    public void run()
+                    {
+                        graphicsBuffer.sync();
+                        graphicsBuffer.blitToDisplay(null, getContentComp());
+                    }
+                });
+            }
+        }
+    }
+
+    /**
+     * Called by ShellListener when shell gets activated.
+     */
+    void handleShellActivatedEvent()
+    {
+        super.handleShellActivatedEvent();
+
+        // reset the game key state
+        gameKeyState = 0;
+
+        synchronized(cleanupLock)
+        {
+            setMode(CLEANUP_NEEDED, true);
+        }
+
+        LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_SHOWNOTIFY, this);
+        EventDispatcher.instance().postEvent(event);
+    }
+
+    /**
+     * Called by ShellListener when shell gets de-activated.
+     */
+    void handleShellDeActivatedEvent()
+    {
+        super.handleShellDeActivatedEvent();
+        LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_HIDENOTIFY, this);
+        EventDispatcher.instance().postEvent(event);
+    }
+
+    /*
+     * UI thread calls
+     */
+    void eswtHandleResizeEvent(int width, int height)
+    {
+        super.eswtHandleResizeEvent(width, height);
+        // update new bounds to graphicsBuffer
+        // this call must not be synchronized as we
+        // cannot use locking in UI thread
+        graphicsBuffer.setControlBounds(getContentComp());
+        synchronized(cleanupLock)
+        {
+            setMode(CLEANUP_NEEDED, true);
+        }
+    }
+
+    /*
+     * UI thread calls
+     */
+    void eswtHandleEvent(Event e)
+    {
+        super.eswtHandleEvent(e);
+        if(e.type == SWT.KeyDown)
+        {
+            doKeyPressed(e.keyCode);
+        }
+        else if(e.type == SWT.KeyUp)
+        {
+            doKeyReleased(e.keyCode);
+        }
+    }
+
+    private void setMode(final int aMode, boolean value)
+    {
+        synchronized(modeLock)
+        {
+            if(value)
+            {
+                mode |= aMode;
+            }
+            else
+            {
+                mode &= ~aMode;
+            }
+        }
+    }
+
+    private boolean isMode(final int aMode)
+    {
+        return ((mode & aMode) != 0);
+    }
+
+    /*
+     * UI thread calls. Paint listener of the eSWT widget.
+     */
+    class CanvasShellPaintListener implements PaintListener
+    {
+        public void paintControl(PaintEvent pe)
+        {
+            // Check if we got here from buffer flush
+            if(graphicsBuffer.isPaintingActive())
+            {
+                graphicsBuffer.blitToDisplay(pe.gc.getGCData().internalGc, null);
+            }
+            else
+            {
+                // Native toolkit is requesting an update of an area that has
+                // become invalid. Can't do anything here because the contents
+                // need to be queried from the MIDlet in another thread by
+                // a paint callback. For this a paint callback event is posted.
+                // For a moment the native toolkit thinks that the area has
+                // been validated when in truth it will be painted later after
+                // the paint callback has been executed.
+                EventDispatcher eventDispatcher = EventDispatcher.instance();
+                LCDUIEvent event = eventDispatcher.newEvent(
+                                       LCDUIEvent.CANVAS_PAINT_NATIVE_REQUEST,
+                                       javax.microedition.lcdui.Canvas.this);
+                event.x = pe.x;
+                event.y = pe.y;
+                event.width = pe.width;
+                event.height = pe.height;
+                event.widget = pe.widget;
+                eventDispatcher.postEvent(event);
+            }
+        }
+    }
+
+    /*
+     * Dispatcher thread or the serviceRepaints()-thread calls.
+     */
+    final void doCallback(LCDUIEvent event)
+    {
+        switch(event.type)
+        {
+        case LCDUIEvent.CANVAS_PAINT_MIDLET_REQUEST: // fall through
+        case LCDUIEvent.CANVAS_PAINT_NATIVE_REQUEST:
+            doPaintCallback(event);
+            break;
+        case LCDUIEvent.CANVAS_HIDENOTIFY:
+            hideNotify();
+            break;
+        case LCDUIEvent.CANVAS_KEYPRESSED:
+            keyPressed(event.keyCode);
+            break;
+        case LCDUIEvent.CANVAS_KEYREPEATED:
+            keyRepeated(event.keyCode);
+            break;
+        case LCDUIEvent.CANVAS_KEYRELEASED:
+            keyReleased(event.keyCode);
+            break;
+        case LCDUIEvent.CANVAS_POINTERDRAGGED:
+            pointerDragged(event.x, event.y);
+            break;
+        case LCDUIEvent.CANVAS_POINTERPRESSED:
+            pointerPressed(event.x, event.y);
+            break;
+        case LCDUIEvent.CANVAS_POINTERRELEASED:
+            pointerReleased(event.x, event.y);
+            break;
+        case LCDUIEvent.CANVAS_SHOWNOTIFY:
+            showNotify();
+            break;
+        default:
+            super.doCallback(event);
+            break;
+        }
+    }
+
+    /*
+     * Dispatcher thread or the serviceRepaints()-thread calls.
+     */
+    private final void doPaintCallback(final LCDUIEvent event)
+    {
+        synchronized(flushLock)
+        {
+            // It's possible that this Canvas is sent to background
+            // right after the visibility is checked here, however
+            // it is okay as in such case we just do one extra paint
+            // callback. The visibility change cannot be synchronized with
+            // this method, since it would expose implementation to deadlock
+            if(!isMode(CURRENTLY_VISIBLE))
+            {
+                return;
+            }
+
+            // Decide the area going to be painted by the callback.
+            final int redrawNowX;
+            final int redrawNowY;
+            final int redrawNowW;
+            final int redrawNowH;
+            // Before this thread obtains the repaintLock any repaint() calls
+            // will still be adding to the invalid area that is going to be
+            // painted by this callback.
+            synchronized(repaintLock)
+            {
+                if(event.type == LCDUIEvent.CANVAS_PAINT_NATIVE_REQUEST)
+                {
+                    // Merge with possibly existing repaint() requests
+                    invalidate(event.x, event.y, event.width, event.height);
+                }
+                else
+                {
+                    // Need to add a new event to the queue in subsequent repaint()
+                    // calls.
+                    setMode(REPAINT_PENDING, false);
+                }
+
+                // Store the current area to be painted
+                redrawNowX = repaintX1;
+                redrawNowY = repaintY1;
+                redrawNowW = repaintX2-repaintX1;
+                redrawNowH = repaintY2-repaintY1;
+
+                // After releasing the lock the repaint() calls will start with
+                // new invalid area.
+                repaintX1 = repaintX2 = repaintY1 = repaintY2 = 0;
+
+                // Don't do the callback if there's nothing to paint
+                if(!((redrawNowW > 0) && (redrawNowH > 0)))
+                {
+                    return;
+                }
+            }
+
+            // Create instance of Graphics if not created yet
+            if(canvasGraphics == null)
+            {
+                canvasGraphics = graphicsBuffer.getGraphics();
+                canvasGraphics.setSyncStrategy(Graphics.SYNC_LEAVE_SURFACE_SESSION_OPEN);
+            }
+
+            // Clean the background if dirty, buffer the operations.
+            synchronized(cleanupLock)
+            {
+                if(isMode(CLEANUP_NEEDED) && isMode(NO_BACKGROUND))
+                {
+                    // UI thread can change the contentArea object reference at
+                    // any time. Store the object reference locally to ensure it
+                    // points to the same rectangle all the time.
+                    Rectangle contentArea = getContentArea();
+
+                    canvasGraphics.setClip(contentArea.x, contentArea.y,
+                                           contentArea.width, contentArea.height);
+                    canvasGraphics.cleanBackground(contentArea);
+                    setMode(CLEANUP_NEEDED, false);
+                }
+            }
+
+            // Clip must define the invalid area
+            canvasGraphics.reset();
+            canvasGraphics.setClip(redrawNowX, redrawNowY, redrawNowW, redrawNowH);
+
+            // The callback
+            paint(canvasGraphics);
+
+            // Blit frame to display
+            synchronized(graphicsBuffer)
+            {
+                ESWTUIThreadRunner.safeSyncExec(new Runnable()
+                {
+                     public void run()
+                    {
+                        if(event.widget.isDisposed())
+                        {
+                            return;
+                        }
+                        graphicsBuffer.sync();
+                        graphicsBuffer.blitToDisplay(null, event.widget);
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * UI thread calls to flush the command buffer of a graphics context.
+     */
+    private final void doBufferFlush(PaintEvent event, Graphics graphics)
+    {
+        //  event.gc.getGCData().internalGc.render(graphics.getCommandBuffer());
+    }
+
+    /*
+     * UI thread calls.
+     */
+    void doKeyPressed(int keyCode)
+    {
+        Logger.method(this, "doKeyPressed", String.valueOf(keyCode));
+        boolean sendCallback = false;
+
+        if(!(updateGameKeyState(keyCode, true) && isMode(SUPPRESS_GAMEKEYS)))
+        {
+            if(isMode(SELECTIONKEY_COMPATIBILITY) && (keyCode == -5))
+            {
+                if(isMode(FULLSCREEN_MODE))
+                {
+                    if(!((getNumCommands() > 0) && hasCommandListener()))
+                    {
+                        sendCallback = true;
+                    }
+                    else
+                    {
+                        sendCallback = false;
+                    }
+                }
+                else
+                {
+                    sendCallback = true;
+                }
+            }
+            else if((!isMode(SELECTIONKEY_COMPATIBILITY)) && (keyCode == -5))
+            {
+                sendCallback = false;
+            }
+            else
+            {
+                sendCallback = true;
+            }
+        }
+        else
+        {
+            sendCallback = false;
+        }
+
+        if(sendCallback == true)
+        {
+            LCDUIEvent event;
+
+            if(keysPressed.contains(new Integer(keyCode)))
+            {
+                event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_KEYREPEATED, this);
+            }
+            else
+            {
+                event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_KEYPRESSED, this);
+            }
+            event.keyCode = keyCode;
+            EventDispatcher.instance().postEvent(event);
+
+        }
+    }
+
+    /*
+     * UI thread calls.
+     */
+    void doKeyReleased(int keyCode)
+    {
+        Logger.method(this, "doKeyReleased", String.valueOf(keyCode));
+        boolean sendCallback = false;
+        if(!(updateGameKeyState(keyCode, true) && isMode(SUPPRESS_GAMEKEYS)))
+        {
+            if(isMode(SELECTIONKEY_COMPATIBILITY) && (keyCode == -5))
+            {
+                if(isMode(FULLSCREEN_MODE))
+                {
+                    if(!((getNumCommands() > 0) && hasCommandListener()))
+                    {
+                        sendCallback = true;
+                    }
+                    else
+                    {
+                        sendCallback = false;
+                    }
+                }
+                else
+                {
+                    sendCallback = true;
+                }
+            }
+            else if((!isMode(SELECTIONKEY_COMPATIBILITY)) && (keyCode == -5))
+            {
+                sendCallback = false;
+            }
+            else
+            {
+                sendCallback = true;
+            }
+        }
+        else
+        {
+            sendCallback = false;
+        }
+
+        if(sendCallback == true)
+        {
+
+            LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_KEYRELEASED, this);
+            event.keyCode = keyCode;
+            EventDispatcher.instance().postEvent(event);
+
+        }
+    }
+
+    /**
+     * Updates game key states and returns if the key was a game key.
+     */
+    private boolean updateGameKeyState(int keyCode, boolean addKeyState)
+    {
+        // Ignore key repeat events
+        if(ESWTUIThreadRunner.getKeyRepeatCount() > 1)
+        {
+            return true;
+        }
+        try
+        {
+            int gameAction = KeyTable.getGameAction(keyCode);
+            if(addKeyState)
+            {
+                // set bitfield
+                gameKeyState |= (1 << gameAction);
+            }
+            return true;
+        }
+        catch(IllegalArgumentException iae)
+        {
+            return false;
+        }
+    }
+
+    private void setDefaultTapValues()
+    {
+        twips = DEFAULT_TWIPS;
+        timeout = DEFAULT_TIMEOUT;
+    }
+
+    class CanvasShellMouseListener implements
+        org.eclipse.swt.events.MouseListener,
+        org.eclipse.swt.events.MouseMoveListener
+    {
+
+        public void mouseDoubleClick(MouseEvent arg0)
+        {
+        }
+
+        public void mouseDown(MouseEvent event)
+        {
+            LCDUIEvent e = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_POINTERPRESSED,
+                           javax.microedition.lcdui.Canvas.this);
+            e.x = event.x;
+            e.y = event.y;
+            EventDispatcher.instance().postEvent(e);
+
+            if(!isMode(DISABLE_TAPDETECTION))
+            {
+                // Supress Drag events
+                setMode(SUPPRESS_DRAGEVENT, true);
+
+                pointerDownX = event.x;
+                pointerDownY = event.y;
+
+                // Create and Schedule Timer
+                timerTask = new CanvasTimerTask();
+                timer.schedule(timerTask, timeout);
+            }
+        }
+
+        public void mouseUp(MouseEvent event)
+        {
+            int pointerUpX = event.x;
+            int pointerUpY = event.y;
+
+            if(!isMode(DISABLE_TAPDETECTION))
+            {
+                if(timerTask != null)
+                {
+                    timerTask.cancel();
+                    timerTask = null;
+                }
+
+                // If Timer not expired and Mouseup is withing rectangle assign
+                // PointercDown to Pinter Up
+                if(isMode(SUPPRESS_DRAGEVENT) && checkWithinRect(event.x, event.y))
+                {
+                    pointerUpX = pointerDownX;
+                    pointerUpY = pointerDownY;
+                    setMode(SUPPRESS_DRAGEVENT, false);
+                }
+            }
+
+            LCDUIEvent e = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_POINTERRELEASED,
+                           javax.microedition.lcdui.Canvas.this);
+            e.x = pointerUpX;
+            e.y = pointerUpY;
+            EventDispatcher.instance().postEvent(e);
+        }
+
+        public void mouseMove(MouseEvent event)
+        {
+            // Check for timeout expiration and if PointerUp falls outside the rectangle
+            if(isMode(DISABLE_TAPDETECTION) || (!isMode(SUPPRESS_DRAGEVENT)) || !checkWithinRect(event.x, event.y))
+            {
+                LCDUIEvent e = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_POINTERDRAGGED,
+                               javax.microedition.lcdui.Canvas.this);
+                e.x = event.x;
+                e.y = event.y;
+                EventDispatcher.instance().postEvent(e);
+            }
+        }
+
+        boolean checkWithinRect(int x, int y)
+        {
+            // Get pixel per inch
+            Point P = Display.getCurrent().getDPI();
+
+            float xPxielwidth  = (twips * P.x) / 1440;
+            float yPixelHeight = (twips * P.y) / 1440;
+
+            int RightX = pointerDownX + (int) xPxielwidth;
+
+            // If the rectange width falls outside the canvas area
+            if(RightX > getWidth())
+            {
+                RightX = getWidth();
+            }
+
+            int LeftX = pointerDownX - (int) xPxielwidth;
+
+            // If the rectange width falls outside the canvas area
+            if(LeftX < 0)
+                LeftX = 0;
+
+            int TopY = pointerDownY - (int) yPixelHeight;
+
+            // If the rectange height falls outside the canvas area
+            if(TopY < 0)
+                TopY = 0;
+
+            int DownY = pointerDownY + (int) yPixelHeight;
+
+            // If the rectange heightfalls outside the canvas area.
+            if(DownY > getHeight())
+                DownY = getHeight();
+
+            // Find the PointerUp is within rectange
+            if((x >= LeftX) && (x <= RightX))
+            {
+                if((y >= TopY) && (y <= DownY))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    private boolean invalidate(int x, int y, int width, int height)
+    {
+        // Regularize bounds
+        final int x1 = x;
+        final int y1 = y;
+        final int x2 = x + width;
+        final int y2 = y + height;
+
+        // Union the current and new damaged rects
+        final boolean valid = ((repaintX2 - repaintX1) <= 0) && ((repaintY2 - repaintY1) <= 0);
+        if(!valid)
+        {
+            repaintX1 = Math.min(repaintX1, x1);
+            repaintY1 = Math.min(repaintY1, y1);
+            repaintX2 = Math.max(repaintX2, x2);
+            repaintY2 = Math.max(repaintY2, y2);
+        }
+        else
+        {
+            repaintX1 = x1;
+            repaintY1 = y1;
+            repaintX2 = x2;
+            repaintY2 = y2;
+        }
+
+        // UI thread can change the the contentArea object reference at
+        // any time. Store the object reference locally to ensure it
+        // points to the same rectangle all the time.
+        Rectangle contentArea = getContentArea();
+        if(contentArea == null) return valid;
+        final int w = contentArea.width;
+        final int h = contentArea.height;
+
+        // Clip to bounds
+        repaintX1 = repaintX1 > 0 ? repaintX1 : 0;
+        repaintY1 = repaintY1 > 0 ? repaintY1 : 0;
+        repaintX2 = repaintX2 < w ? repaintX2 : w;
+        repaintY2 = repaintY2 < h ? repaintY2 : h;
+
+        return valid;
+    }
+
+    class CanvasTimerTask extends TimerTask
+    {
+
+        public void run()
+        {
+            setMode(SUPPRESS_DRAGEVENT, false);
+        }
+    }
+
+    class CanvasShellVisibilityListener implements SymbianWindowVisibilityListener
+    {
+        public void handleSymbianWindowVisibilityChange(Widget widget, boolean visible) {
+            if (javax.microedition.lcdui.Canvas.this.getShell() == widget && graphicsBuffer != null)
+            {
+                WindowSurface surface = graphicsBuffer.getWindowSurface();
+                if (surface != null)
+                    surface.handleSymbianWindowVisibilityChange(visible);
+            }
+        }
+    }
+}