--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_qt/src/javax/microedition/lcdui/CustomItem.java Tue Aug 31 15:09:22 2010 +0300
@@ -0,0 +1,630 @@
+/*
+* Copyright (c) 2009 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 javax.microedition.lcdui.EventDispatcher.LCDUIEvent;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * Implementation of LCDUI abstract <code>CustomItem</code> class.
+ */
+public abstract class CustomItem extends Item
+{
+ protected static final int NONE = 0;
+ protected static final int TRAVERSE_HORIZONTAL = 1;
+ protected static final int TRAVERSE_VERTICAL = 2;
+
+ protected static final int KEY_PRESS = 4;
+ protected static final int KEY_RELEASE = 8;
+ protected static final int KEY_REPEAT = 0x10;
+
+ protected static final int POINTER_PRESS = 0x20;
+ protected static final int POINTER_RELEASE = 0x40;
+ protected static final int POINTER_DRAG = 0x80;
+
+ /**
+ * If CustomItem is changed, reasons for Re-layouting.
+ */
+ static final int UPDATE_REASON_REPAINT = UPDATE_ITEM_MAX << 1;
+
+
+ private boolean cleanupNeeded;
+ private int contentWidth;
+ private int contentHeight;
+
+ private boolean repaintPending;
+ private int repaintX1;
+ private int repaintY1;
+ private int repaintX2;
+ private int repaintY2;
+ private Object repaintLock;
+ private Object cleanupLock;
+ private Object resizeLock;
+
+ // Flag for passing info between UI thread
+ // and Dispatcher thread
+ private boolean widgetDisposed;
+
+ private com.nokia.mj.impl.rt.support.Finalizer finalizer;
+
+ // Graphics command buffer for this instance
+ Buffer graphicsBuffer;
+ Graphics customItemGraphics;
+
+ CustomItemLayouter layouter;
+
+
+
+ /**
+ * Constructor.
+ *
+ * @param label Label of CustomItem.
+ */
+ protected CustomItem(String label)
+ {
+ finalizer = ((finalizer != null) ? finalizer
+ : new com.nokia.mj.impl.rt.support.Finalizer()
+ {
+ public void finalizeImpl()
+ {
+ if(finalizer != null)
+ {
+ finalizer = null;
+ if(!ESWTUIThreadRunner.isDisposed())
+ {
+ dispose();
+ }
+ }
+ }
+ });
+ repaintLock = new Object();
+ cleanupLock = new Object();
+ resizeLock = new Object();
+ setLabel(label);
+ }
+
+ /**
+ * Get the game action associated with the key code.
+ *
+ * @param keyCode key code
+ * @return game action bound to the key
+ */
+ public int getGameAction(int keyCode)
+ {
+ return KeyTable.getGameAction(keyCode);
+ }
+
+ /**
+ * Get the interaction modes available on this device.
+ *
+ * @return bitmask of available interaction modes.
+ */
+ protected final int getInteractionModes()
+ {
+ // TODO: check final interaction modes
+ return TRAVERSE_HORIZONTAL | TRAVERSE_VERTICAL
+ | KEY_PRESS | KEY_RELEASE
+ | POINTER_PRESS | POINTER_DRAG | POINTER_RELEASE;
+ }
+
+ /**
+ * Invalidates CustomItem.
+ */
+ protected final void invalidate()
+ {
+ updateParent(UPDATE_SIZE_CHANGED);
+ }
+
+ /**
+ * Requests repainting of CustomItem.
+ */
+ protected final void repaint()
+ {
+ repaint(0, 0, contentWidth, contentHeight);
+ }
+
+ /**
+ * Requests repainting of the specified area in CustomItem.
+ *
+ * @param aX
+ * @param aY
+ * @param aWidth
+ * @param aHeight
+ */
+ protected final void repaint(int aX, int aY, int aWidth, int aHeight)
+ {
+ Rectangle rect = new Rectangle(aX, aY, aWidth, aHeight);
+ // From here it goes to updateItem()
+ updateParent(UPDATE_REASON_REPAINT, rect);
+ }
+
+ /**
+ * Callback method for traverse-in event.
+ *
+ * @param direction
+ * @param viewportWidth
+ * @param viewportHeight
+ * @param visRect if the CustomItem supports internal traversal and keeps
+ * focus, visRect represents the visible rectangle (focused area)
+ * @return true if internal traversal has occurred, false otherwise
+ */
+ protected boolean traverse(int direction, int viewportWidth,
+ int viewportHeight, int[] visRect)
+ {
+ return false;
+ }
+
+ /**
+ * Callback method for traverse-out event.
+ */
+ protected void traverseOut()
+ {
+ }
+
+ /**
+ * Callback method for key pressed event.
+ */
+ protected void keyPressed(int aKeyCode)
+ {
+ }
+
+ /**
+ * Callback method for key released event.
+ */
+ protected void keyReleased(int aKeyCode)
+ {
+ }
+
+ /**
+ * Callback method for key repeated event.
+ */
+ protected void keyRepeated(int aKeyCode)
+ {
+ }
+
+ /**
+ * Callback method for pointer pressed event.
+ */
+ protected void pointerPressed(int aX, int aY)
+ {
+ }
+
+ /**
+ * Callback method for pointer released event.
+ */
+ protected void pointerReleased(int aX, int aY)
+ {
+ }
+
+ /**
+ * Callback method for pointer dragged event.
+ */
+ protected void pointerDragged(int aX, int aY)
+ {
+ }
+
+ /**
+ * Callback method when item gets shown.
+ */
+ protected void showNotify()
+ {
+ }
+
+ /**
+ * Callback method when item gets hidden.
+ */
+ protected void hideNotify()
+ {
+ }
+
+ /**
+ * Callback method for content area size change event.
+ */
+ protected void sizeChanged(int aWidth, int aHeight)
+ {
+ }
+
+ /**
+ * Abstract method.
+ *
+ * @param graphics
+ * @param aWidth
+ * @param aHeight
+ */
+ protected abstract void paint(Graphics graphics, int aWidth, int aHeight);
+
+ /**
+ * Should return the minimum width of the content area.
+ */
+ protected abstract int getMinContentWidth();
+
+ /**
+ * Should return the minimum height of the content area.
+ */
+ protected abstract int getMinContentHeight();
+
+ /**
+ * Should return the preferred width of the content area.
+ */
+ protected abstract int getPrefContentWidth(int aHeight);
+
+ /**
+ * Should return the preferred height of the content area.
+ */
+ protected abstract int getPrefContentHeight(int aWidth);
+
+ /**
+ * Calculates minimum size of this item.
+ *
+ * @return Minimum size.
+ */
+ Point calculateMinimumSize()
+ {
+ return CustomItemLayouter.calculateMinimumBounds(this);
+ }
+
+ /**
+ * Calculates preferred size of this item.
+ *
+ * @return Preferred size.
+ */
+ Point calculatePreferredSize()
+ {
+ return CustomItemLayouter.calculatePreferredBounds(this);
+ }
+
+ /**
+ * Set the size of the content area.
+ *
+ * @param newWidth
+ * @param newHeight
+ */
+ void internalHandleSizeChanged(int newWidth, int newHeight)
+ {
+ if(contentWidth != newWidth || contentHeight != newHeight)
+ {
+ synchronized(resizeLock)
+ {
+ contentWidth = newWidth;
+ contentHeight = newHeight;
+ }
+ EventDispatcher eventDispatcher = EventDispatcher.instance();
+ LCDUIEvent event = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_SIZECHANGED, layouter.formLayouter.getForm());
+ event.item = this;
+ eventDispatcher.postEvent(event);
+ synchronized(cleanupLock)
+ {
+ cleanupNeeded = true;
+ }
+ repaint();
+ }
+ }
+
+ /*
+ * Note that if you call this and getContentHeight() from a non-UI thread
+ * then it must be made sure size doesn't change between the calls.
+ */
+ int getContentWidth()
+ {
+ return contentWidth;
+ }
+
+ /*
+ * Note that if you call this and getContentHeight() from a non-UI thread
+ * then it must be made sure size doesn't change between the calls.
+ */
+ int getContentHeight()
+ {
+ return contentHeight;
+ }
+
+ boolean isFocusable()
+ {
+ return true;
+ }
+
+ /*
+ * This gets called when Form contents are modified.
+ */
+ void setParent(Screen parent)
+ {
+ super.setParent(parent);
+ if(parent == null)
+ {
+ // Item was removed from a Form
+ layouter = null;
+ }
+ else
+ {
+ // Item was added to a Form
+ layouter = ((CustomItemLayouter)((Form)parent).getFormLayouter().getItemLayouter(this));
+ }
+ }
+
+ 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 size at any time. Must make sure it doesn't
+ // change between reading the width and the height.
+ final int w;
+ final int h;
+ synchronized(resizeLock)
+ {
+ w = contentWidth;
+ h = contentHeight;
+ }
+
+ // 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;
+ }
+
+ /*
+ * Note the control passed as a parameter can change. It must not be stored
+ * to the CustomItem. Adding and removing Form item is blocked for the
+ * duration of the call.
+ */
+ void updateItem(Rectangle rect, Control control)
+ {
+ // 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(rect.x, rect.y, rect.width, rect.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(!repaintPending)
+ {
+ EventDispatcher eventDispatcher = EventDispatcher.instance();
+ LCDUIEvent event = eventDispatcher.newEvent(
+ LCDUIEvent.CUSTOMITEM_PAINT_MIDLET_REQUEST, layouter.formLayouter.getForm());
+ event.widget = control;
+ event.item = this;
+ eventDispatcher.postEvent(event);
+ repaintPending = true;
+ }
+ }
+ }
+ }
+
+ /*
+ * Dispatcher thread thread calls. Operations changing Form content through
+ * public API are blocked for the duration of the method call, however form
+ * internal layout may modify items or even delete some. It has been checked
+ * that the eSWT widget has not been disposed and that the CustomItem has not
+ * been removed from the Form.
+ */
+ void doCallback(final LCDUIEvent event)
+ {
+ switch(event.type)
+ {
+ case LCDUIEvent.CUSTOMITEM_PAINT_MIDLET_REQUEST:
+ case LCDUIEvent.CUSTOMITEM_PAINT_NATIVE_REQUEST:
+ doPaintCallback(event);
+ break;
+ case LCDUIEvent.CUSTOMITEM_HIDENOTIFY:
+ hideNotify();
+ break;
+ case LCDUIEvent.CUSTOMITEM_SHOWNOTIFY:
+ showNotify();
+ break;
+ case LCDUIEvent.CUSTOMITEM_KEYPRESSED:
+ keyPressed(event.keyCode);
+ break;
+ case LCDUIEvent.CUSTOMITEM_KEYREPEATED:
+ keyRepeated(event.keyCode);
+ break;
+ case LCDUIEvent.CUSTOMITEM_KEYRELEASED:
+ keyReleased(event.keyCode);
+ break;
+ case LCDUIEvent.CUSTOMITEM_POINTERDRAGGED:
+ pointerDragged(event.x, event.y);
+ break;
+ case LCDUIEvent.CUSTOMITEM_POINTERPRESSED:
+ pointerPressed(event.x, event.y);
+ break;
+ case LCDUIEvent.CUSTOMITEM_POINTERRELEASED:
+ pointerReleased(event.x, event.y);
+ break;
+ case LCDUIEvent.CUSTOMITEM_SIZECHANGED:
+ sizeChanged(event.width, event.height);
+ break;
+ default:
+ super.doCallback(event);
+ }
+ }
+
+ /*
+ * Dispatcher thread calls.
+ */
+ void doPaintCallback(final LCDUIEvent event)
+ {
+ // 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.CUSTOMITEM_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.
+ repaintPending = 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;
+ }
+ }
+
+ // Instantiate GraphicsBuffer and Graphics if needed
+ // or just update the bounds
+ widgetDisposed = false;
+ final CustomItem self = this;
+ ESWTUIThreadRunner.safeSyncExec(new Runnable()
+ {
+ public void run()
+ {
+ // Checking the widget state here ensures that
+ // it is not disposed during this method execution
+ // as we are in UI thread
+ if(event.widget.isDisposed())
+ {
+ widgetDisposed = true;
+ return;
+ }
+ if(customItemGraphics == null)
+ {
+ graphicsBuffer = Buffer.createInstance(self, (Control)event.widget);
+ customItemGraphics = graphicsBuffer.getGraphics();
+ customItemGraphics.setSyncStrategy(Graphics.SYNC_LEAVE_SURFACE_SESSION_OPEN);
+ }
+ else
+ {
+ graphicsBuffer.setControlBounds((Control)event.widget);
+ }
+ }
+ });
+
+ // Quit if widget was already disposed
+ if(widgetDisposed)
+ {
+ return;
+ }
+
+ // Clean the background if dirty, buffer the operations.
+ synchronized(cleanupLock)
+ {
+ if(cleanupNeeded && layouter.noBackground)
+ {
+ // Must be made sure that size doesn't change between reading
+ // the width and the height.
+ int contentWidth, contentHeight;
+ synchronized(resizeLock)
+ {
+ contentWidth = this.contentWidth;
+ contentHeight = this.contentHeight;
+ }
+
+ customItemGraphics.setClip(0, 0, contentWidth, contentHeight);
+ customItemGraphics.cleanBackground(new Rectangle(0, 0, contentWidth, contentHeight));
+ cleanupNeeded = false;
+ }
+ }
+
+ // Clip must define the invalid area
+ customItemGraphics.reset();
+ customItemGraphics.setClip(redrawNowX, redrawNowY, redrawNowW, redrawNowH);
+
+ // The callback
+ paint(customItemGraphics, contentWidth, contentHeight);
+
+ // Wait until the UI thread is available. Then in the UI thread
+ // synchronously send a paint event.
+ ESWTUIThreadRunner.safeSyncExec(new Runnable()
+ {
+ public void run()
+ {
+ if(event.widget.isDisposed())
+ {
+ return;
+ }
+ graphicsBuffer.sync();
+ graphicsBuffer.blitToDisplay(null, event.widget);
+ }
+ });
+ }
+
+ /**
+ * Disposes this instance
+ */
+ private void dispose()
+ {
+ ESWTUIThreadRunner.safeSyncExec(new Runnable()
+ {
+ public void run()
+ {
+ if(graphicsBuffer != null)
+ {
+ graphicsBuffer.dispose();
+ graphicsBuffer = null;
+ }
+ }
+ });
+ }
+}