javauis/lcdui_qt/src/javax/microedition/lcdui/ItemLayouter.java
branchRCL_3
changeset 65 ae942d28ec0e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_qt/src/javax/microedition/lcdui/ItemLayouter.java	Tue Aug 31 15:09:22 2010 +0300
@@ -0,0 +1,744 @@
+/*
+* 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 org.eclipse.ercp.swt.mobile.CaptionedControl;
+import org.eclipse.ercp.swt.mobile.MobileShell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.*;
+
+import com.nokia.mj.impl.nokialcdui.ItemControlStateChangeListener;
+
+/**
+ * Abstract base class for Item layouters.
+ */
+abstract class ItemLayouter
+{
+
+    /**
+     * Key name for paint listener.
+     */
+    private static final String FOCUS_LISTENER = "FocusListener";
+
+    protected static final String MIN_TEXT = "...";
+
+    protected FormLayouter formLayouter;
+
+    protected Composite formComposite;
+
+    private static Label staticLabel;
+    private static CaptionedControl staticCC;
+    private static MobileShell staticShell;
+
+    private static Point captionedTrim;
+
+    protected static int formWidth;
+    protected static int formHeigh;
+
+    /**
+     * Gets static singleton off-screen Shell which can be used by item
+     * layouters when creating temporary Controls.
+     *
+     * @return Static Shell. Never null.
+     */
+    static MobileShell eswtGetStaticShell()
+    {
+        if(staticShell == null)
+        {
+            staticShell = new MobileShell(ESWTUIThreadRunner.getInstance()
+                                          .getDisplay(), SWT.SYSTEM_MODAL | SWT.VERTICAL);
+            staticShell.getVerticalBar().setVisible(true);
+            formWidth = staticShell.getClientArea().width;
+            formHeigh = staticShell.getClientArea().height;
+        }
+        return staticShell;
+    }
+
+    /**
+     * Gets static singleton off-screen Label control.
+     */
+    private static Label eswtGetStaticLabel()
+    {
+        if(staticLabel == null)
+        {
+            staticLabel = new Label(eswtGetStaticShell(), SWT.NONE);
+        }
+        return staticLabel;
+    }
+
+    /**
+     * Gets static singleton off-screen Captioned control.
+     */
+    private static CaptionedControl eswtGetStaticCC()
+    {
+        if(staticCC == null)
+        {
+            staticCC = new CaptionedControl(eswtGetStaticShell(), SWT.VERTICAL);
+        }
+        return staticCC;
+    }
+
+    /**
+     * The static "layouting" shell's size is updated.
+     */
+    static void eswtUpdateStaticShellSize(int width, int height)
+    {
+        if(staticShell != null)
+        {
+            staticShell.setBounds(staticShell.computeTrim(0, 0, width, height));
+            formWidth = width;
+            formHeigh = height;
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param aFormLayouter FormLayouter used for layouting.
+     */
+    ItemLayouter(FormLayouter aFormLayouter)
+    {
+        formLayouter = aFormLayouter;
+        formComposite = formLayouter.getForm().getFormComposite();
+        ESWTUIThreadRunner.syncExec(new Runnable()
+        {
+            public void run()
+            {
+                ItemLayouter.eswtGetStaticShell();
+            }
+        });
+    }
+
+    /**
+     * Label alignment directive.
+     */
+    int eswtGetLabelAlignmentDirective()
+    {
+        return formLayouter.getLanguageSpecificLayoutDirective();
+    }
+
+    /**
+     * Layout Item in a row.
+     *
+     * @param row current Row
+     * @param item Item to layout
+     */
+    void eswtLayoutItem(Row row, Item item)
+    {
+    	LayoutObject lo = getLayoutObject(item);
+        formLayouter.eswtAddNewLayoutObject(lo);
+        if(item instanceof CustomItem)
+        {
+            ItemControlStateChangeListener listener = item.getItemControlStateChangeListener();
+            if(null != listener)
+            {
+                listener.notifyControlAvailable(lo.getControl(),item);
+                lo.getControl().addListener(SWT.Dispose, new EventListener(item));
+            }
+        }
+    }
+
+    /**
+     * Creates LayoutObject for the given Item.
+     *
+     * @param item Item to layout
+     * @return LayoutObject
+     */
+    LayoutObject getLayoutObject(Item item)
+    {
+    	LayoutObject lo = formLayouter.getLayoutObject(item);
+    	if(lo == null)
+    	{
+        	lo = new LayoutObject(item, eswtGetCaptionedControl(item));
+    	}
+		return lo;
+    }
+
+    /**
+     * Wraps this item's control in the necessary composites.<br>
+     * Based on the item, the result of this method can be:
+     * <li> specific Control
+     * <li> labeled CaptionedControl (containing the specific Control) <br>
+     * <br>
+     * The method will set the size of these using the eswtCaptionedResize
+     * method.
+     *
+     * @param item Item to be layouted
+     */
+    final Control eswtGetCaptionedControl(Item item)
+    {
+        CaptionedControl captioned = new CaptionedControl(formComposite, SWT.VERTICAL);
+        if(item.hasLabel())
+        {
+	        captioned.setText(item.getLabel());
+		}
+        eswtGetControl(captioned, item);
+        eswtCaptionedResize(item, captioned, item.getLayoutWidth(), item.getLayoutHeight());
+        return captioned;
+    }
+
+    /**
+     * This abstract method creates the eSWT control.
+     *
+     * @param parent where to create
+     * @param item on which it is based
+     * @return Control
+     */
+    abstract Control eswtGetControl(Composite parent, Item item);
+
+    /**
+     * Update size of an LayoutObject.
+     */
+    final void eswtResizeObject(LayoutObject lo)
+    {
+        Item item = lo.getOwningItem();
+        eswtCaptionedResize(item, lo.getControl(), item.getLayoutWidth(), item.getLayoutHeight());
+        lo.eswtUpdateSize();
+    }
+
+    /**
+     * Set the size of a LayoutObject.
+     *
+     * @param lo LayoutObject
+     * @param width
+     * @param height
+     */
+    final void eswtResizeObject(LayoutObject lo, int width, int height)
+    {
+        eswtCaptionedResize(lo.getOwningItem(), lo.getControl(), width, height);
+        lo.eswtUpdateSize();
+    }
+
+    final void eswtCaptionedResize(Item item, Control control, int width, int height)
+    {
+        if(control instanceof CaptionedControl)
+        {
+            CaptionedControl cc = (CaptionedControl) control;
+            cc.setSize(width, height);
+            Rectangle ccArea = cc.getClientArea();
+            eswtResizeControl(item, eswtFindSpecificControl(item, control),
+                              ccArea.width, ccArea.height);
+        }
+        else
+        {
+            eswtResizeControl(item, eswtFindSpecificControl(item, control),
+                              width, height);
+        }
+    }
+
+    /**
+     * Should be implemented in sub-classes where resizing might happen.
+     *
+     * @param item Item
+     * @param control layouted Control
+     * @param width item width.
+     * @param height item height
+     */
+    void eswtResizeControl(Item item, Control control, int width, int height)
+    {
+    	if(control != null)
+    	{
+        	control.setSize(width, height);
+    	}
+    }
+
+    /**
+     * Add listeners to layouted control.
+     *
+     * @param item Item
+     * @param lo LayoutObject
+     */
+    void eswtAddListeners(Item item, LayoutObject lo)
+    {
+        lo.eswtAddSelectionListenerForCommands();
+        Control specific = eswtFindSpecificControl(item, lo.getControl());
+        if(specific != null)
+        {
+            eswtAddSpecificListeners(item, specific);
+        }
+        else
+        {
+            Logger.warning(this + "::eswtAddListeners didnt find control for " + item);
+        }
+    }
+
+    /**
+     * Add listeners to Layouter specific control.
+     *
+     * @param item Item
+     * @param control specific Control
+     */
+    void eswtAddSpecificListeners(Item item, Control control)
+    {
+        if(item.isFocusable())
+        {
+            ItemFocusListener ifl = new ItemFocusListener(item);
+            control.addFocusListener(ifl);
+            control.setData(FOCUS_LISTENER, ifl);
+        }
+    }
+
+    /**
+     * Remove listeners from layouted control.
+     *
+     * @param item Item
+     * @param lo LayoutObject
+     */
+    void eswtRemoveListeners(Item item, LayoutObject lo)
+    {
+        lo.eswtRemoveSelectionListenerForCommands();
+        Control specific = eswtFindSpecificControl(item, lo.getControl());
+        if(specific != null)
+        {
+            eswtRemoveSpecificListeners(item, specific);
+        }
+        else
+        {
+            Logger.warning(this + "::eswtRemoveListeners didnt find control for " + item);
+        }
+    }
+
+    /**
+     * Remove listeners from Layouter specific control.
+     *
+     * @param item Item
+     * @param control specific Control
+     */
+    void eswtRemoveSpecificListeners(Item item, Control control)
+    {
+        if(item.isFocusable())
+        {
+            ItemFocusListener ifl = (ItemFocusListener) control
+                                    .getData(FOCUS_LISTENER);
+            if(ifl != null)
+            {
+                control.removeFocusListener(ifl);
+                control.setData(FOCUS_LISTENER, null);
+            }
+        }
+    }
+
+    /**
+     * Update control of an Item.
+     *
+     * @param item Item to update
+     * @param reason reason of update
+     * @param param optional parameter
+     */
+    final void updateItem(final Item item, final Control control,
+                          final int reason, final Object param)
+    {
+        ESWTUIThreadRunner.syncExec(new Runnable()
+        {
+            public void run()
+            {
+                if(control != null)
+                {
+                    if(!control.isDisposed())
+                    {
+                        eswtUpdateItem(item, control, reason, param);
+                    }
+                    else
+                    {
+                        Logger.warning(ItemLayouter.this
+                                       + "::updateItem found a disposed widget for " + item);
+                    }
+                }
+                else
+                {
+                    Logger.warning(ItemLayouter.this
+                                   + "::updateItem didnt find control for " + item);
+                }
+            }
+        });
+    }
+
+    /**
+     * This abstract method updates the eSWT control.
+     *
+     * @param item Item to update
+     * @param control layouted eSWT control
+     * @param reason reason of update
+     * @param param optional parameter
+     */
+    abstract void eswtUpdateItem(Item item, Control control, int reason,
+                                 Object param);
+
+    /**
+     * Finds the Layouter specific eSWT control in the eSWT Composite tree.
+     *
+     * @param item Item
+     * @param control eSWT control
+     * @return a specific control or null if not found
+     */
+    final Control eswtFindSpecificControl(Item item, Control control)
+    {
+        Control ret = null;
+        if(eswtIsSpecificControl(item, control))
+        {
+            ret = control;
+        }
+        else if(control instanceof Composite)
+        {
+            Control[] children = ((Composite) control).getChildren();
+            for(int i = 0; i < children.length; i++)
+            {
+                Control result = eswtFindSpecificControl(item, children[i]);
+                if(result != null)
+                {
+                    ret = result;
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Returns the unlocked preferred size needed to display an Item.
+     * Subclasses may overwrite this. By default returns (0,0).
+     *
+     * @param item Item.
+     * @return Preferred area needed to display Item. x is width
+     *      and y is height.
+     */
+    static Point calculatePreferredBounds(Item item)
+    {
+        return new Point(0, 0);
+    }
+
+    /**
+     * Returns if this eSWT control is Layouter specific.
+     *
+     * @param item Item
+     * @param control eSWT control
+     * @return true if this is Layouter specific
+     */
+    abstract boolean eswtIsSpecificControl(Item item, Control control);
+
+    /**
+     * Gets the first control of an Item.
+     *
+     * @param item Item
+     * @return layouted Control
+     */
+    Control eswtGetFirstControl(Item item)
+    {
+        LayoutObject lo = formLayouter.getFirstLayoutObjectOfItem(item);
+        if(lo != null)
+        {
+            return lo.getControl();
+        }
+        return null;
+    }
+
+    /**
+     * Gets the first layouter specific control of an Item.
+     *
+     * @param item Item
+     * @return layouted specific Control
+     */
+    Control eswtGetFirstSpecificControl(Item item)
+    {
+        LayoutObject lo = formLayouter.getFirstLayoutObjectOfItem(item);
+        if(lo != null)
+        {
+            Control control = lo.getControl();
+            if(control != null)
+            {
+                return eswtFindSpecificControl(item, control);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Offers a key event to be consumed by the control.<br>
+     * If the key is not consumed (default) then it's used for scrolling.
+     *
+     * @param item Item
+     * @param key eSWT key code
+     * @param type eSWT key type
+     * @return if the key was consumed or not
+     */
+    boolean eswtOfferKeyEvent(Item item, int key, int type)
+    {
+        if(type == SWT.KeyDown)
+        {
+            return eswtOfferKeyPressed(item, key);
+        }
+        else if(type == SWT.KeyUp)
+        {
+            return eswtOfferKeyReleased(item, key);
+        }
+        else
+        {
+            return eswtOfferKeyRepeated(item, key);
+        }
+    }
+
+    boolean eswtOfferKeyPressed(Item item, int key)
+    {
+        // Do not consume these by default
+        return false;
+    }
+
+    boolean eswtOfferKeyRepeated(Item item, int key)
+    {
+        // Do not consume these by default
+        return false;
+    }
+
+    boolean eswtOfferKeyReleased(Item item, int key)
+    {
+        // Do not consume these by default
+        return false;
+    }
+
+    /**
+     * Processing for item when it gets focus.
+     *
+     * @param item
+     * @param dir
+     */
+    void eswtFocusGained(Item item, int dir)
+    {
+        Logger.method(item, "focusGained", String.valueOf(dir));
+        item.internalSetFocused(true);
+    }
+
+    /**
+     * Processing for item when it looses focus.
+     *
+     * @param item item which looses focus.
+     */
+    void eswtFocusLost(Item item)
+    {
+        Logger.method(item, "focusLost");
+        item.internalSetFocused(false);
+    }
+
+    final void eswtHandleVisibilityChange(Item item, boolean visible)
+    {
+        if(item.isVisible() != visible)
+        {
+            item.internalSetVisible(visible);
+            if(visible)
+            {
+                eswtHandleShow(item);
+            }
+            else
+            {
+                eswtHandleHide(item);
+            }
+        }
+    }
+
+    /**
+     * Special processing of Item when it becomes visible.
+     *
+     * @param item which becomes visible.
+     */
+    void eswtHandleShow(Item item)
+    {
+        // Implementation not needed. Subclasses may override.
+    }
+
+    /**
+     * Special processing for item which becomes not visible due to scrolling.
+     *
+     * @param item which becomes hidden.
+     */
+    void eswtHandleHide(Item item)
+    {
+        // Implementation not needed. Subclasses may override.
+    }
+
+    static void applyMinMargins(Item item, Point size)
+    {
+        if(item.hasLabel())
+        {
+            applyCaptionedTrim(MIN_TEXT, size);
+        }
+        size.x = Math.min(size.x, formWidth);
+    }
+
+    static void applyPrefMargins(Item item, Point size)
+    {
+        if(item.hasLabel())
+        {
+            applyCaptionedTrim(item.getLabel(), size);
+        }
+        size.x = Math.min(size.x, formWidth);
+    }
+
+
+    static final void applyCaptionedTrim(final String text, Point size)
+    {
+        final Point localSize = size;
+        ESWTUIThreadRunner.syncExec(new Runnable()
+        {
+            public void run()
+            {
+                CaptionedControl cc = eswtGetStaticCC();
+                cc.setText(text);
+                Rectangle rect = cc.computeTrim(0, 0, localSize.x, localSize.y);
+                captionedTrim = new Point(rect.width, rect.height);
+            }
+        });
+        size.x = captionedTrim.x;
+        size.y = captionedTrim.y;
+    }
+
+    /**
+     * Applies the label size to the unlocked preferred area needed to display
+     * an Item.
+     *
+     * @param size
+     *            Point where the Label's size is added.
+     * @param item
+     *            Item containing the label.
+     */
+    static Point getLabelSize(final String labelStr)
+    {
+        final Point size = new Point(0, 0);
+        ESWTUIThreadRunner.syncExec(new Runnable()
+        {
+            public void run()
+            {
+                Label label = eswtGetStaticLabel();
+                label.setText(labelStr);
+                Point temp = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+                size.x = Math.min(temp.x, formWidth);
+                size.y = temp.y;
+            }
+        });
+        return size;
+    }
+
+    /**
+     * Calculate X position based on horizontal layout.
+     *
+     * @param owningWidth owning area width
+     * @param objectWidth object's width
+     * @param horizontalLayout horizontal layout
+     * @return x-position of object
+     */
+    static final int getXLocation(int owningWidth, int objectWidth,
+                                  int horizontalLayout)
+    {
+        switch(horizontalLayout)
+        {
+        case Item.LAYOUT_RIGHT:
+            return owningWidth - objectWidth;
+        case Item.LAYOUT_CENTER:
+            return (owningWidth - objectWidth) / 2;
+        case Item.LAYOUT_LEFT:
+        default:
+            return 0;
+        }
+    }
+
+    /**
+     * Calculate Y position based on vertical layout.
+     *
+     * @param owningHeight owning area height
+     * @param objectHeight object's height
+     * @param verticalLayout vertical layout
+     * @return y-position of object
+     */
+    static final int getYLocation(int owningHeight, int objectHeight,
+                                  int verticalLayout)
+    {
+        switch(verticalLayout)
+        {
+        case Item.LAYOUT_VCENTER:
+            return (owningHeight - objectHeight) / 2;
+        case Item.LAYOUT_TOP:
+            return 0;
+        case Item.LAYOUT_BOTTOM:
+            return owningHeight - objectHeight;
+        default:
+            return owningHeight - objectHeight;
+        }
+    }
+
+    /**
+     * Gets maximum width of an item. The maximum width is same for all items
+     * and it's the width of form's content area.
+     *
+     * @param item Item which maximum width is returned. The width is same for
+     *            all items but this parameter is useful because method could
+     *            use item's parent to calculate the width.
+     * @return Maximum width of an item.
+     */
+    static int getMaximumItemWidth(final Item item)
+    {
+        if(item != null && item.hasLabel())
+        {
+            Point temp = new Point(0, 0);
+            applyCaptionedTrim("", temp);
+            return formWidth - temp.x;
+        }
+        return formWidth;
+    }
+
+    /**
+     * Item focus Listener reacts on eSWT focusGained event.
+     */
+    class ItemFocusListener implements FocusListener
+    {
+
+        private Item item;
+
+        ItemFocusListener(Item item)
+        {
+            this.item = item;
+        }
+
+        public void focusGained(FocusEvent focusEvent)
+        {
+            if(!item.isFocused())
+            {
+                // Logger.method(item, "focusGained");
+                formLayouter.eswtSetCurrentSelectedItem(item);
+            }
+        }
+
+        public void focusLost(FocusEvent fe)
+        {
+            // Logger.method(item, "focusLost");
+        }
+    }
+
+    class EventListener implements Listener
+    {
+        Item itm;
+        EventListener(Item item)
+        {
+            itm = item;
+        }
+        public void handleEvent(Event e)
+        {
+            (itm.getItemControlStateChangeListener()).notifyControlDisposed(itm);
+        }
+    }
+}