javauis/lcdui_qt/src/javax/microedition/lcdui/FormLayouter.java
author hgs
Fri, 15 Oct 2010 12:29:39 +0300
changeset 80 d6dafc5d983f
parent 57 59b3b4473dc8
permissions -rw-r--r--
v2.2.19_1

/*
* 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 java.util.NoSuchElementException;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

/**
 * FormLayouter implements form layout algorithm.
 */
class FormLayouter
{

    /**
     * Form instance (not owned).
     */
    private Form form;

    /**
     * Form's scrolled composite (not owned).
     */
    protected ScrolledComposite formComposite;

    /**
     * Controls vector.
     */
    private Vector layoutObjects = new Vector();

    /**
     * Rows vector.
     */
    private Vector rows = new Vector();

    /**
     * Form's current horizontal alignment
     */
    private int currentHLayoutDirective;

    private ImageItemLayouter imIL;

    private StringItemLayouter sIL;

    private GaugeLayouter gL;

    private TextFieldLayouter tfL;

    private DateFieldLayouter dfL;

    private ChoiceGroupLayouter cgL;

    private CustomItemLayouter ciL;

    private SpacerLayouter sL;

    private int vPosition;

    private Item deferredScrollToItem;

    private boolean isCurrent;

    private Control itemMainControl;

    private static final int NO_DIRECTION = -1;

    private Item currentSelectedItem;

    private int direction = NO_DIRECTION;

    private boolean mousePressed;

    private LayoutObject currentlyUnderMouse;


    /**
     * Constructor.
     *
     * @param form Form to perform layout on.
     */
    FormLayouter(Form form)
    {
        this.form = form;
        formComposite = form.getFormComposite();
        imIL = new ImageItemLayouter(this);
        sIL = new StringItemLayouter(this);
        gL = new GaugeLayouter(this);
        dfL = new DateFieldLayouter(this);
        tfL = new TextFieldLayouter(this);
        cgL = new ChoiceGroupLayouter(this);
        ciL = new CustomItemLayouter(this);
        sL = new SpacerLayouter(this);
    }

    /**
     * Dispose and cleanup layouted items.
     */
    void dispose()
    {
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtClearRows(0, null);
    			removeAllLayoutObjects();
				rows = null;
				layoutObjects = null;
            }
        });
    }

    /**
     * Log out a control with all its children at the given indentation level.
     */
    void logControl(int level, Control control)
    {
        if(control != null)
        {
            String s2 = control.toString();
            s2 = s2.substring(0, s2.indexOf('}') - 1);
            String s3 = control.getBounds().toString();
            s3 = s3.substring(s3.indexOf('{'));
            Logger.verbose(Logger.indent(s2 + s3, level * 2));
            if(control instanceof Composite)
            {
                Control[] arr = ((Composite) control).getChildren();
                for(int i = 0; i < arr.length; i++)
                {
                    logControl(level + 1, arr[i]);
                }
            }
        }
    }

    /**
     * Log out all layoutobjects for each item.
     */
    void logFormLayout()
    {
        Logger.verbose(form + " ---------------------------------------------");
        LayoutObject lo = null;
        Item item = null;
        int line = -1;
        while((lo = getNextLayoutObjectOfItem(lo, null)) != null)
        {
            if(lo.getOwningItem() != item)
            {
                item = lo.getOwningItem();
                Logger.verbose(item.toString());
            }
            if(lo.getRowIdx() != line)
            {
                line = lo.getRowIdx();
            }
            Logger.verbose(Logger.indent("Row:" + lo.getRowIdx(), 2));
            logControl(2, lo.getControl());
        }
        Logger.verbose(form + " ---------------------------------------------");
    }

    /**
     * Called when Form is about to be shown.<br>
     * NOTE: this is called from eSWT UI-thread
     */
    void handleShowCurrentEvent()
    {
        Logger.method(this, "handleShowCurrentEvent");
        isCurrent = true;

        // restore our scrolling position (eSWT resets it to 0 by default)
        eswtSetScrollingPosition(vPosition, true);

        Item item = null;
        LayoutObject lo = null;
        while((lo = getNextLayoutObjectOfItem(lo, null)) != null)
        {
            if(lo.getOwningItem() != item)
            {
                // item border
                item = lo.getOwningItem();
                getItemLayouter(item).eswtAddListeners(item, lo);
            }
        }

		eswtApplyCurrentFocus();	
    }

    /**
     * Called when Form is about to be hidden.<br>
     * NOTE: this is called from eSWT UI-thread
     */
    void handleHideCurrentEvent()
    {
        Logger.method(this, "handleHideCurrentEvent");
        isCurrent = false;

        Item item = null;
        LayoutObject lo = null;
        while((lo = getNextLayoutObjectOfItem(lo, null)) != null)
        {
            if(lo.getOwningItem() != item)
            {
                // item border
                item = lo.getOwningItem();
                getItemLayouter(item).eswtRemoveListeners(item, lo);
                getItemLayouter(item).eswtHandleVisibilityChange(item, false);
            }
        }

		direction = NO_DIRECTION;
    }

    /**
     * Called when Form is beeing resized.<br>
     * NOTE: this is called from eSWT UI-thread
     */
    void handleResizeEvent(int width, int height)
    {
        // Logger.method(this, "handleResizeEvent");
        int numitems = getItemCount();
        for(int i = 0; i < numitems; i++)
        {
            getItem(i).invalidateCachedSizes();
        }
        ItemLayouter.eswtUpdateStaticShellSize(width, height);
    }

    /**
     * Do form layout according to startIndex.
     *
     * @param startItem - index of item which need to be layouted.
     */
    void layoutForm(final int startIndex)
    {
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtLayoutForm(startIndex);
            }
        });
    }

    /**
     * eSWT callback for doLayout().
     */
    void eswtLayoutForm(int startIndex)
    {
        int numItems = getItemCount();
        Logger.method(this, "eswtLayoutForm", startIndex + " / " + numItems);

        eswtUpdateFormComposite(false);
        if(numItems > 0)
        {
            Item previousItem = null;
            int startRowIndex = 0;

            if(startIndex > 0)
            {
                // Find row with previous item.
                previousItem = getItem(startIndex - 1);
                Row prevItemRow = getLastRowOfItem(previousItem);
                if(prevItemRow != null)
                {
                    startRowIndex = rows.indexOf(prevItemRow);
                }
            }

            // Clear rows starting from item - partial re-layouting
            eswtClearRows(startRowIndex, previousItem);
            // Layout items
            for(int i = startIndex; i < numItems; i++)
            {
                eswtLayoutItem(getItem(i));
            }
            // Update last row
            eswtUpdateRow(getLastRow());
        }
        else
        {
            eswtClearRows(0, null);
        }
        // check if we need to scroll to a particular item
        if(deferredScrollToItem != null)
        {
            eswtSetCurrentItem(deferredScrollToItem);
            deferredScrollToItem = null;
        }
        eswtUpdateFormComposite(true);
        eswtHandleVisibilityChanges();

        if(Logger.isLogVerbose())
        {
            logFormLayout();
        }

		// clear invalid selected item
		eswtCheckCurrentSelectedItem();
		
		if(currentSelectedItem != null
				&& (currentSelectedItem.isFocusable()))
		{
			eswtApplyCurrentFocus();
		}
		else
		{
			// If there's no item currently selected try to find first
			// focusable item and set it current (if found):
			Item found = eswtGetNextFocusableItem(
							 getItem(startIndex - 1), SWT.ARROW_RIGHT);
			if(found != null)
			{
				eswtSetCurrentSelectedItem(found, NO_DIRECTION);
			}
			else
			{
				eswtApplyCurrentFocus();
			}
		}		
    }

    /**
     * Returns if the parameter is a eSWT directional key code.
     *
     * @param keyCode key code
     */
    private boolean isDirectionKey(int keyCode)
    {
        return (keyCode == SWT.ARROW_DOWN || keyCode == SWT.ARROW_UP
                || keyCode == SWT.ARROW_LEFT || keyCode == SWT.ARROW_RIGHT);
    }

    /**
     * Handler for key events.<br>
     * The implementation moves focus and/or scrolls the form when
     * needed. The method is called by the Form.
     *
     * @param keyCode eSWT key code.
     * @param keyType eSWT key type.
     */
    final void handleKeyEvent(int keyCode, int keyType)
    {
        Logger.method(this, "handleKeyEvent", currentSelectedItem,
                      String.valueOf(keyCode), String.valueOf(keyType));

        boolean isDirectionalKey = isDirectionKey(keyCode);
        if(keyType == SWT.KeyDown && isDirectionalKey)
        {
            eswtCheckCurrentSelectedItem();
        }

        if(currentSelectedItem != null)
        {
            if(getItemLayouter(currentSelectedItem).eswtOfferKeyEvent(
                        currentSelectedItem, keyCode, keyType))
            {
                // if the key has been consumed
                return;
            }
        }

        // scrolling/focus traverse only happens on directional key's down event
        if(keyType == SWT.KeyDown && isDirectionalKey)
        {
            // try to find next focusable item
            Item next = eswtGetNextFocusableItem(currentSelectedItem, keyCode);

            // if no visible & focusable item was found to transfer focus
            if(next == currentSelectedItem)
            {
                // try to scroll a bit
                eswtSetScrollingPosition(getNextScrollingPosition(keyCode),
                                         true);
                // find next focusable after scrolling
                next = eswtGetNextFocusableItem(currentSelectedItem, keyCode);
            }

            if(next != currentSelectedItem)
            {
                //textfield always have to be fully visible when focused.
                if(next instanceof TextField)
                {
                    eswtScrollToItem(next);
                }
                eswtSetCurrentSelectedItem(next, keyCode);
            }
        }
    }

    /**
     * Handler for pointer events.<br>
     * The method is called by the Form.
     *
     * @param x coordinate relative to scrolledComposite
     * @param y coordinate relative to scrolledComposite
     * @param type event type: SWT.MouseDown, SWT.MouseMove, SWT.MouseUp
     */
    final void handlePointerEvent(int x, int y, int type)
    {
        Logger.method(this, "handlePointerEvent", String.valueOf(x),
                      String.valueOf(y), String.valueOf(type));

        // TODO: change when DirectUI style arrives.
        /*
        Item item;
        if (type == SWT.MouseMove) {
            if (currentlyUnderMouse == null
                    || !currentlyUnderMouse.contains(x, y)) {
                if (currentlyUnderMouse != null) {
                    //currentlyUnderMouse.getControl().setCapture(false);
                }
                item = eswtFindItemUnderMouse(x, y);
                if (item != null && item != currentSelectedItem
                        && item.isFocusable()) {
                    setCurrentItem(item);
                    item.internalSetFocused(true);
                    eswtSetCurrentSelectedItem(item);
                    //following method causes all mouse events delivered to it

                    currentlyUnderMouse.getControl().setCapture(true);
                    Logger.warning("seting capture to:" + item);
                }
            }
            int currentVPosition = getVPosition();
            boolean isMouseDirectionUp = false;
            boolean doScrolling = false;
            int localY = y;

            if (y <= currentVPosition) {
                localY = Math.max(0, y);
                eswtSetScrollingPosition(localY, true);
                isMouseDirectionUp = true;
                doScrolling = true;
            }
            else if (y > (currentVPosition + getFormHeight())) {
                //check for maxVPosition
                if (y > (eswtGetMaxVPosition() + getFormHeight())) {
                    localY = eswtGetMaxVPosition() + getFormHeight();
                }
                else {
                    localY = y;
                }
                currentVPosition = localY - getFormHeight();
                eswtSetScrollingPosition(currentVPosition, true);

                isMouseDirectionUp = false;
                doScrolling = true;
            }
            if (mousePressed && doScrolling) {
                resetEventTimer(isMouseDirectionUp, localY);
            }
        }
        else
        if (type == SWT.MouseDown) {
            mousePressed = true;
            item = eswtFindItemUnderMouse(x, y);
            if (item != null && item != currentSelectedItem
                    && item.isFocusable() && getForm().getShell() ==
                        getForm().getShell().getDisplay().getActiveShell()) {
                //eswtScrollToItem(item);
                //following method causes all mouse events delivered to it

                //currentlyUnderMouse.getControl().setCapture(true);
            }
        }
        else if (type == SWT.MouseUp) {
            mousePressed = false;
            if (currentlyUnderMouse != null) {
                //currentlyUnderMouse.getControl().setCapture(false);
            }
        }*/
    }

    /**
     * Find item at the specified point.
     *
     * @param x coordinate.
     * @param y coordinate.
     * @return Item.
     */
    Item eswtFindItemUnderMouse(int x, int y)
    {
        Row itemRow;
        for(int i = 0; i < getRowCount(); i++)
        {
            itemRow = getRow(i);
            if(itemRow.getYShift() <= y && y <= itemRow.getBottomPosition())
            {
                LayoutObject lo;
                for(int j = 0; j < itemRow.size(); j++)
                {
                    lo = itemRow.getLayoutObject(j);
                    if(lo.contains(x, y))
                    {
                        Logger.info("Item under mouse: "
                                    + lo.getOwningItem());
                        currentlyUnderMouse = lo;
                        return lo.getOwningItem();
                    }
                }
                break;
            }
        }
        return null;
    }

    /**
     * Gets next (or nearest) focusable item.
     *
     * @param fromItem Item where to start to search the next focusable item.
     * @param dir Search direction, one of the arrow key constants defined
     *      in class SWT.
     *
     * @return Nearest focusable item or null if no item found.
     */
    final Item eswtGetNextFocusableItem(Item fromItem, int dir)
    {
        Item nextItem = fromItem;

        switch(dir)
        {
        case SWT.ARROW_RIGHT:
        {
            LayoutObject obj = getLastLayoutObjectOfItem(fromItem);
            while((obj = getNextLayoutObjectOfItem(obj, null)) != null)
            {
                Item owner = obj.getOwningItem();
                if(owner != null && owner != fromItem
                        && owner.isFocusable()
                        && isPartiallyVisible(obj, Config.DFI_VISIBILITY_PERCENT))
                {
                    nextItem = owner;
                    break;
                }
            }
            break;
        }

        case SWT.ARROW_LEFT:
        {
            LayoutObject obj = getFirstLayoutObjectOfItem(fromItem);
            while((obj = getPrevLayoutObjectOfItem(obj, null)) != null)
            {
                Item owner = obj.getOwningItem();
                if(owner != null && owner != fromItem
                        && owner.isFocusable()
                        && isPartiallyVisible(obj, Config.DFI_VISIBILITY_PERCENT))
                {
                    nextItem = owner;
                    break;
                }
            }
            break;
        }

        case SWT.ARROW_DOWN:
        {
            int minDist = Integer.MAX_VALUE;
            LayoutObject start = getLastLayoutObjectOfItem(fromItem);
            LayoutObject obj = start;
            while((obj = getNextLayoutObjectOfItem(obj, null)) != null)
            {
                Item owner = obj.getOwningItem();
                if(owner != null && owner != fromItem
                        && owner.isFocusable() && obj.isBelow(start)
                        && isPartiallyVisible(obj, Config.DFI_VISIBILITY_PERCENT))
                {
                    int dist = obj.distanceTo(start);
                    if(dist < minDist)
                    {
                        minDist = dist;
                        nextItem = owner;
                    }
                }
            }
            break;
        }

        case SWT.ARROW_UP:
        {
            int minDist = Integer.MAX_VALUE;
            LayoutObject start = getFirstLayoutObjectOfItem(fromItem);
            LayoutObject obj = start;
            while((obj = getPrevLayoutObjectOfItem(obj, null)) != null)
            {
                Item owner = obj.getOwningItem();
                if(owner != null && owner != fromItem
                        && owner.isFocusable() && obj.isAbove(start)
                        && isPartiallyVisible(obj, Config.DFI_VISIBILITY_PERCENT))
                {
                    int dist = obj.distanceTo(start);
                    if(dist < minDist)
                    {
                        minDist = dist;
                        nextItem = owner;
                    }
                }
            }
            break;
        }

        default:
        }

        return nextItem;
    }

    /**
     * Check if the currentSelectedItem is valid and visible. If not then it
     * sets it to null.
     */
    final void eswtCheckCurrentSelectedItem()
    {
        if(currentSelectedItem != null)
        {
            if(currentSelectedItem.getParent() != getForm()
                    || !currentSelectedItem.isVisible())
            {
                // we need to find another
                Logger.method(this, "eswtCheckCurrentSelectedItem");
                eswtSetCurrentSelectedItem(null, NO_DIRECTION);
            }
        }
    }

    /**
     * Sets currentSelectedItem and sets focus to it.<br>
     * If one of form's items is already selected when this method is called,
     * removes focus from old item and then moves focus to new one.
     *
     * @param item Item to set as current selected. If null, nothing happens.
     * @param dir Direction which is delivered to layouter.
     */
    void eswtSetCurrentSelectedItem(Item item, int dir)
    {
        if(currentSelectedItem != item)
        {
            Logger.info(this + "::SelectedItem: "
                        + currentSelectedItem + " --(" + dir + ")--> " + item);

            // Save direction
            direction = dir;
            // Remove focus from currentSelectedItem and notify its Layouter.
            if(currentSelectedItem != null)
            {
                getItemLayouter(currentSelectedItem).eswtFocusLost(
                    currentSelectedItem);
            }

            // Set new currentSelectedItem, must be focusable or null
            currentSelectedItem = item;

            // Set focus to currentSelectedItem and notify its Layouter.
            if(currentSelectedItem != null)
            {
                getItemLayouter(currentSelectedItem).eswtFocusGained(
                    currentSelectedItem, dir);
            }

            // Apply eSWT focus to currentSelectedItem's control
            eswtApplyCurrentFocus();
        }
    }

    /**
     * Sets currentSelectedItem and sets focus to it.<br>
     * If one of form's items is already selected when this method is called,
     * removes focus from old item and then moves focus to new one.
     *
     * @param item Item to set as current selected. If null, nothing happens.
     * @param dir Direction which is delivered to layouter.
     */
    void eswtSetCurrentSelectedItem(Item item)
    {
        if(currentSelectedItem != item)
        {
            Logger.info(this + "::SelectedItem: "
                        + currentSelectedItem + " ---> " + item);

            // Remove focus from currentSelectedItem and notify its Layouter.
            if(currentSelectedItem != null)
            {
                getItemLayouter(currentSelectedItem).eswtFocusLost(
                    currentSelectedItem);
            }

            // Set new currentSelectedItem, must be focusable or null
            currentSelectedItem = item;

            // Set focus to currentSelectedItem and notify its Layouter.
            if(currentSelectedItem != null)
            {
                getItemLayouter(currentSelectedItem).eswtFocusGained(
                    currentSelectedItem, NO_DIRECTION);
            }

            // Apply eSWT focus to currentSelectedItem's control
            //eswtApplyCurrentFocus();
        }
    }

    /**
     * Sets focus to currentSelectedItem's control if its partially visible.
     * Otherwise it sets dummy focus to form's composite.<br>
     * <br>
     * Note that this method applies focus only to eSWT control. Item focus
     * update and layouter notifications are handled in method
     * <code>eswtSetCurrentSelectedItem()</code>.<br>
     * If currentSelectedItem is null or form is not shown, this method has no
     * effect.
     */
    void eswtApplyCurrentFocus()
    {
        if(isFormCurrent())
        {
            // if any of the Item's LayoutObjects is visible
            if(isItemPartiallyVisible(currentSelectedItem))
            {
                Logger.method(this, "ApplyFocus", currentSelectedItem);
                eswtSetFocusToFirstControl(currentSelectedItem);
            }
            else
            {
                Logger.method(this, "ApplyFocus", "dummy");
                formComposite.forceFocus();
            }
        }
    }

    /**
     * If the Item is valid and it is layouted, then sets the Item's first
     * LayoutObject focused.
     *
     * @param item an item which first LayoutObject is set focused.
     */
    void eswtSetFocusToFirstControl(Item item)
    {
        if(item != null && item.isFocusable())
        {
            LayoutObject lo = getFirstLayoutObjectOfItem(item);
            if(lo != null)
            {
                lo.getControl().forceFocus();
            }
        }
    }

    /**
     * Gets Current selected item.
     *
     * @return Current selected item. May also return null.
     */
    Item getCurrentSelectedItem()
    {
        return currentSelectedItem;
    }

    /**
     * Get the direction of scrolling.
     *
     * @return direction of scrolling.
     */
    int getDirection()
    {
        return direction;
    }

    /**
     * Set focus to an item if it is focusable and scroll form to make it
     * visible if it is not.
     *
     * @param item Item to set as current item.
     */
    void setCurrentItem(final Item item)
    {
        Logger.method(this, "setCurrentItem", item);
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtSetCurrentItem(item);
            }
        });
    }

    /**
     * eSWT callback for setCurrentItem.
     */
    boolean eswtSetCurrentItem(Item item)
    {
        if(item != null)
        {
            if(isItemLayouted(item))
            {
                eswtScrollToItem(item);
                deferredScrollToItem = null;

				if(item.isFocusable())
				{
					eswtSetCurrentSelectedItem(item, NO_DIRECTION);
					Logger.info("eswtSetCurrentItem" + item);
				}
                return true;
            }
            else
            {
                deferredScrollToItem = item;
            }
        }
        return false;
    }

    void eswtScrollToItem(Item item)
    {
        if(item != null)
        {
            int pos = getItemBottomPosition(item) - getFormHeight();
            if(!isItemPartiallyVisible(item))
            {
                eswtSetScrollingPosition(pos, true);
            }
            else if(item instanceof TextField && !isItemFullyVisible(item))
            {
                eswtSetScrollingPosition(pos, true);
            }
        }
    }

    void eswtScrolltoRegion(int yTop, int yBottom, int swtDir)
    {
        if(yTop < vPosition || yBottom > vPosition + getFormHeight())
        {
            // if the region is somewhat outside the screen
            if(swtDir == SWT.ARROW_DOWN || swtDir == SWT.ARROW_RIGHT)
            {
                // align to top
                eswtSetScrollingPosition(yTop, true);
            }
            else
            {
                // align to bottom
                eswtSetScrollingPosition(yBottom - getFormHeight(), true);
            }
        }
    }

    void eswtScrollIfNeeded(final int top, final int bottom)
    {
        if(bottom > vPosition + getFormHeight())
        {
            eswtSetScrollingPosition(bottom - (getFormHeight() / 2), true);
        }
        else if(top < vPosition)
        {
            eswtSetScrollingPosition(bottom - (getFormHeight() / 2), true);
        }
    }

    /**
     * Get control's position relative to composite.
     */
    void getControlPositionOnComposite(Control control, Point location)
    {
        if(control != formComposite)
        {
            Point rel = control.getLocation();
            location.x += rel.x;
            location.y += rel.y;
            getControlPositionOnComposite(control.getParent(), location);
        }
    }

    /**
     * eSWT specific calls to do update ScrolledComposite.
     */
    private void eswtUpdateFormComposite(boolean show)
    {
        if(getRowCount() > 0)
        {
            if(show)
            {
                formComposite.updateScrollbar(getLastRow().getBottomPosition());
                formComposite.pack();
            }
        }
        // Could happen if changing item from very tall to very short.
        // so we have to update VPosition
        if(getVPosition() > eswtGetMaxVPosition())
        {
            eswtSetScrollingPosition(eswtGetMaxVPosition(), false);
        }

        formComposite.setRedraw(show);
        formComposite.setVisible(show);
    }

    /**
     * Clean all form rows starting from startIndex.
     *
     * @param startIndex Start row from which to clean.
     * @param keepItem - item in a startRow which shouldn't be recreated.
     */
    private void eswtClearRows(int startIndex, Item keepItem)
    {
        Logger.method(this, "clearRows", String.valueOf(startIndex), keepItem);
        Row row = null;
        for(int i = (getRowCount() - 1); i >= startIndex; i--)
        {
            row = getRow(i);
            if(row.cleanRow(keepItem))
            {
                break;
            }
            else
            {
                rows.removeElement(row);
            }
        }

        // one row always should be available.
        if((getRowCount() == 0))
        {
            // rows.addElement(tempRow);
            currentHLayoutDirective = Item.LAYOUT_DEFAULT;
            Row newRow = new Row(ItemLayouter.getMaximumItemWidth(null),
                                 getCurrentHLayoutDirective());
            rows.addElement(newRow);
        }
    }

    /**
     * Update Row's internal layout. Handles LAYOUT_2 related post-processing.
     *
     * @param row Row
     */
    private void eswtUpdateRow(Row row)
    {
        if(row != null)
        {
            //Logger.verbose("updateRow start: " + row);
            int numShrink = row.getNumLayoutObjects(Item.LAYOUT_SHRINK);
            int numExpand = row.getNumLayoutObjects(Item.LAYOUT_EXPAND);
            //Logger.verbose("shrink: " + numShrink + " expand: " + numExpand);

            int vMask = Item.LAYOUT_VSHRINK | Item.LAYOUT_VEXPAND;
            // Expand items vertically with VSHRINK or VEXPAND layout directive
            LayoutObject lo = null;
            int maxHeight = row.getRowHeight(vMask);
            while((lo = row.getNextLayoutObject(lo, vMask)) != null)
            {
                if(lo.getOwningItem().hasLayout(Item.LAYOUT_VSHRINK))
                {
                    int pref = lo.getOwningItem().getPreferredHeight();
                    getItemLayouter(lo.getOwningItem()).eswtResizeObject(lo,
                            lo.getWidth(), Math.min(pref, maxHeight));
                }
                else if(lo.getOwningItem().hasLayout(Item.LAYOUT_VEXPAND))
                {
                    getItemLayouter(lo.getOwningItem()).eswtResizeObject(lo,
                            lo.getWidth(), maxHeight);
                }
            }

            // Expand items with SHRINK layout directive
            if(numShrink > 0)
            {
                // Get extra space before shrink and expand
                int offset = row.getFreeSpace() / numShrink;
                // Logger.verbose("shrinkOffset: " + offset);
                if(offset >= 0)
                {
                    while((lo = row.getNextLayoutObject(lo, Item.LAYOUT_SHRINK)) != null)
                    {
                        int pref = lo.getOwningItem().getPreferredWidth();
                        int min = lo.getOwningItem().getMinimumWidth();
                        int itemWidth = Math.min(pref, min + offset);
                        getItemLayouter(lo.getOwningItem()).eswtResizeObject(lo,
                                itemWidth, lo.getHeight());
                    }
                }
            }

            // Expand items with EXPAND layout directive
            if(numExpand > 0)
            {
                // Get extra space after shrink but before expand
                int offset = row.getFreeSpace(Item.LAYOUT_EXPAND) / numExpand;
                if(offset >= 0)
                {
                    // Logger.verbose("expandOffset: " + offset);
                    while((lo = row.getNextLayoutObject(lo, Item.LAYOUT_EXPAND)) != null)
                    {
                        int pref = lo.getOwningItem().getPreferredWidth();
                        getItemLayouter(lo.getOwningItem()).eswtResizeObject(lo,
                                pref + offset, lo.getHeight());
                    }
                }
            }

            //if (numShrink > 0 || numExpand > 0) {
            row.updateRowInternals();
            //}

            row.updateRowLayout(form.getLeftRightLanguage());
            // Logger.verbose("updateRow: " + row);
        }
    }

    /**
     * Add a new Row.
     */
    private Row eswtAddNewRow()
    {
        Row lastRow = getLastRow();
        eswtUpdateRow(lastRow);
        int yShift = (lastRow == null ? 0 : lastRow.getBottomPosition());
        // create new Row
        Row newRow = new Row(ItemLayouter.getMaximumItemWidth(null),
                             getCurrentHLayoutDirective());
        newRow.setYShift(yShift);
        rows.addElement(newRow);
        return newRow;
    }

    /**
     * Add a LayoutObject to the last Row.
     *
     * @param layoutObject the layout object
     */
    void eswtAddNewLayoutObject(LayoutObject layoutObject)
    {
    	if(!layoutObjects.contains(layoutObject))
    	{
    		layoutObjects.addElement(layoutObject);
    	}
        Row lastRow = getLastRow();
        // check if the current Row is full
        if(!lastRow.isEmpty()
                && lastRow.getFreeSpace() < layoutObject.getWidth())
        {
            lastRow = eswtAddNewRow();
        }
        lastRow.eswtAddLayoutObject(layoutObject);
        layoutObject.setRowIdx(getRowCount() - 1);
    }

    /**
     * Optionally add a new Row and adds a LayoutObject.
     *
     * @param layoutObject the layout object
     * @param newRow adds a new row if true. If false, adds new row only if
     *            there's no space for layoutObject in current row.
     */
    void eswtAddNewLayoutObject(LayoutObject layoutObject, boolean newRow)
    {
        if(newRow)
        {
            eswtAddNewRow();
        }
        eswtAddNewLayoutObject(layoutObject);
    }

    /**
     * Layout item in a row, if needed new row is added.
     *
     * @param row - where to startLayout.
     * @param item - Item to Layout
     */
    private void eswtLayoutItem(Item item)
    {
        Row lastRow = getLastRow();
        boolean hlChange = setCurrentHLayoutDirective(item.internalGetLayout());
        if(hlChange || getItemNewLineBefore(item))
        {
            // newline directive or horizontal layout changed
            if(lastRow.isEmpty())
            {
                // if the current/last row is empty - use that
                lastRow.setRowHLayout(getCurrentHLayoutDirective());
            }
            else
            {
                eswtAddNewRow();
            }
        }

        // Use the specific layouter to layout item in the last row
        getItemLayouter(item).eswtLayoutItem(getLastRow(), item);

        if(form.eswtIsShown())
        {
            LayoutObject lo = getFirstLayoutObjectOfItem(item);
            if(lo != null)
            {
                getItemLayouter(item).eswtAddListeners(item, lo);
            }
        }

        if(getItemNewLineAfter(item))
        {
            eswtAddNewRow();
        }
    }

    /**
     * Set Form's Layout directive. if it differ from current set flag
     * startFromNewLine = true;
     *
     * @param newLayoutDirective
     * @return true if a layout change has occured
     */
    private boolean setCurrentHLayoutDirective(int newLayoutDirective)
    {
        int newHLayoutDirective = Item.getHorizontalLayout(newLayoutDirective);
        if((newHLayoutDirective != currentHLayoutDirective)
                && (newHLayoutDirective != Item.LAYOUT_DEFAULT))
        {
            currentHLayoutDirective = newHLayoutDirective;
            return true;
        }
        return false;
    }

    /**
     * Get Form current Layout directive.
     *
     * @return current Layout directive for Form.
     */
    private int getCurrentHLayoutDirective()
    {
        if(currentHLayoutDirective == Item.LAYOUT_DEFAULT)
        {
            return getLanguageSpecificLayoutDirective();
        }
        return currentHLayoutDirective;
    }

    /**
     * Returns language specific layout directive.
     *
     * @return LAYOUT_LEFT or LAYOUT_RIGHT.
     */
    int getLanguageSpecificLayoutDirective()
    {
        if(form.getLeftRightLanguage())
        {
            return Item.LAYOUT_LEFT;
        }
        else
        {
            return Item.LAYOUT_RIGHT;
        }
    }

    private boolean getItemNewLineBefore(Item item)
    {
        return ((item.internalGetLayout() & Item.LAYOUT_NEWLINE_BEFORE) != 0);
    }

    private boolean getItemNewLineAfter(Item item)
    {
        return ((item.internalGetLayout() & Item.LAYOUT_NEWLINE_AFTER) != 0);
    }

    boolean isItemLayouted(Item item)
    {
        return (getFirstLayoutObjectOfItem(item) != null);
    }

    /**
     * Returns if the form is shown.
     */
    boolean isFormCurrent()
    {
        return isCurrent;
    }

    /**
     * Returns if the region is partially visible.
     *
     * @param viewTop viewPort's top position
     * @param viewBottom viewPort's bottom position
     * @return true if visible
     */
    boolean isPartiallyVisible(int yTop, int yBottom)
    {
        int vBottomPosition = vPosition + getFormHeight();
        if((vPosition <= yTop && vBottomPosition <= yTop)
                || (vPosition >= yBottom && vBottomPosition >= yBottom))
        {
            return false;
        }
        return true;
    }

    /**
     * Returns if at least the region's given percentage is visible.
     */
    boolean isPartiallyVisible(int yTop, int yBottom, int minPercent)
    {
        int visPercent = getVisibilityPercent(yTop, yBottom);
        if(visPercent > minPercent)
        {
            return true;
        }
        return false;
    }

    /**
     * Returns the region's visibility percentage.
     */
    int getVisibilityPercent(int yTop, int yBottom)
    {
        if(yTop >= yBottom)
        {
            return 0;
        }
        int vBottomPosition = vPosition + getFormHeight();
        int r1 = Math.max(vPosition, Math.min(yTop, vBottomPosition));
        int r2 = Math.min(vBottomPosition, Math.max(yBottom, vPosition));
        return ((r2 - r1) * 100) / (yBottom - yTop);
    }

    /**
     * Returns if the LayoutObject is partially visible.
     */
    boolean isPartiallyVisible(LayoutObject lo)
    {
        if(lo != null)
        {
            return isPartiallyVisible(lo.getY(), lo.getY() + lo.getHeight());
        }
        return false;
    }

    /**
     * Returns if at least the LayoutObject's given percentage is visible.
     */
    boolean isPartiallyVisible(LayoutObject lo, int minPercent)
    {
        if(lo != null)
        {
            return isPartiallyVisible(lo.getY(), lo.getY() + lo.getHeight(),
                                      minPercent);
        }
        return false;
    }

    /**
     * Returns if the Item is partially visible (if one of its LayoutObjects is
     * partially visible).
     *
     * @param item the Item
     * @return true if partially visible
     */
    boolean isItemPartiallyVisible(Item item)
    {
        if(item != null)
        {
            LayoutObject lo = null;
            while((lo = getNextLayoutObjectOfItem(lo, item)) != null)
            {
                if(isPartiallyVisible(lo))
                {
                    return true;
                }
            }
        }
        return false;
    }



    /**
     * Returns true if item is fully visible.
     *
     * @param item the Item.
     * @return true if fully visible.
     */
    boolean isItemFullyVisible(Item item)
    {
        if(item != null)
        {
            LayoutObject lo = null;
            while((lo = getNextLayoutObjectOfItem(lo, item)) != null)
            {
                if(!isLOFullyVisible(lo))
                {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Returns if the LayoutObject is fully visible.
     */
    boolean isLOFullyVisible(LayoutObject lo)
    {
        if(lo != null)
        {
            return isFullyVisible(lo.getY(), lo.getY() + lo.getHeight());
        }
        return false;
    }

    /**
     * Returns if the region is fully visible.
     *
     * @param viewTop viewPort's top position
     * @param viewBottom viewPort's bottom position
     * @return true if visible
     */
    boolean isFullyVisible(int yTop, int yBottom)
    {
        int vBottomPosition = vPosition + getFormHeight();
        if((vPosition <= yTop && vBottomPosition >= yBottom))
        {
            return true;
        }
        return false;
    }

    int getItemTopPosition(Item item)
    {
        LayoutObject lo = getFirstLayoutObjectOfItem(item);
        if(lo != null)
        {
            return lo.getY();
        }
        return 0;
    }

    int getItemBottomPosition(Item item)
    {
        LayoutObject lo = getLastLayoutObjectOfItem(item);
        if(lo != null)
        {
            return lo.getY() + lo.getHeight();
        }
        return 0;
    }

    int getItemCount()
    {
        return form.size();
    }

    Item getItem(int index)
    {
        try
        {
            return (Item) form.getItems().elementAt(index);
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
            // Logger.exception(e);
            return null;
        }
    }

    int getItemIndex(Item item)
    {
        return form.getItems().indexOf(item);
    }

    int getRowCount()
    {
        return rows.size();
    }

    /**
     * Return the Row with the given index.
     *
     * @param index Row's index
     * @return a Row
     */
    Row getRow(int index)
    {
        try
        {
            return (Row) rows.elementAt(index);
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
            Logger.exception("getRow", e);
            return null;
        }
    }

    /**
     * Return the Row to which the given LayoutObject belongs.
     *
     * @param lo LayoutObject
     * @return the owning Row
     */
    Row getRow(LayoutObject lo)
    {
        try
        {
            return getRow(lo.getRowIdx());
        }
        catch(NullPointerException e)
        {
            // Logger.exception("getRow", e);
            return null;
        }
    }

    /**
     * Returns the last row of the form.
     */
    Row getLastRow()
    {
        try
        {
            return (Row) rows.lastElement();
        }
        catch(NoSuchElementException nse)
        {
            // Logger.exception("getLastRow", nse);
            return null;
        }
    }

    /**
     * Find last row which contains corresponding Item.
     *
     * @param item Item in a Row.
     * @return Last Row with that item.
     */
    Row getLastRowOfItem(Item item)
    {
        return getRow(getLastLayoutObjectOfItem(item));
    }

    /**
     * Get Form which requires layout.
     *
     * @return form.
     */
    Form getForm()
    {
        return form;
    }

    /**
     * Form's content height.
     */
    int getFormHeight()
    {
        return form.getHeight();
    }

    /**
     * Form's content width.
     */
    int getFormWidth()
    {
        return form.getWidth();
    }

    /**
     * Returns LayoutObject for the given Item.
     *
     * @param item Item to layout
     * @return LayoutObject
     */
    LayoutObject getLayoutObject(Item item)
	{
		int num = layoutObjects.size();

		for(int i = 0; i < num; i++)
		{
			if(((LayoutObject)layoutObjects.elementAt(i)).getOwningItem() == item)
			{
				LayoutObject lo = (LayoutObject)(layoutObjects.elementAt(i));
				lo.setRowIdx(-1);
				return lo;
			}
		}

		return null;
	}

    /**
     * Removes Layout Object for the given Item.
     *
     * @param item Item to remove LayoutObject
     */
    void removeLayoutObject(Item item)
	{

		int num = layoutObjects.size();

		for(int i = 0; i < num; i++)
		{
			if(((LayoutObject)layoutObjects.elementAt(i)).getOwningItem() == item)
			{
				LayoutObject lo = (LayoutObject)(layoutObjects.elementAt(i));
				layoutObjects.removeElement(lo);
				lo.dispose();
				break;
			}
		}
	}

    /**
     * Removes all Layout Objects from the FormLayouter.
     */
    void removeAllLayoutObjects()
	{
		Enumeration e = layoutObjects.elements();

		while(e.hasMoreElements())
		{
			LayoutObject lo = (LayoutObject)e.nextElement();
			layoutObjects.removeElement(lo);
			lo.dispose();
		}
	}

    /**
     * Returns the next LayoutObject belonging to this Item.<br>
     *
     * @param lo starting LayoutObject; if null then it start from first.
     * @param item specifies the parent Item; null means any Item
     * @return the next LayoutObject in the layout.
     */
    LayoutObject getNextLayoutObjectOfItem(LayoutObject lo, Item item)
    {
        int startRow = (lo == null ? 0 : lo.getRowIdx());
        LayoutObject temp = null;
        for(int i = startRow; i < getRowCount(); i++)
        {
            temp = getRow(i).getNextLayoutObject(lo, item);
            if(temp != null && temp != lo)
            {
                return temp;
            }
        }
        return null;
    }

    /**
     * Returns the previous LayoutObject belonging to this Item.<br>
     *
     * @param lo starting LayoutObject; if null then it start from last.
     * @param item specifies the parent Item; null means any Item
     * @return the previous LayoutObject in the layout.
     */
    LayoutObject getPrevLayoutObjectOfItem(LayoutObject lo, Item item)
    {
        int startRow = (lo == null ? rows.size() - 1 : lo.getRowIdx());
        LayoutObject temp = null;
        for(int i = startRow; i >= 0; i--)
        {
            temp = getRow(i).getPrevLayoutObject(lo, item);
            if(temp != null && temp != lo)
            {
                return temp;
            }
        }
        return null;
    }

    /**
     * Returns the first LayoutObject of a layouted item.
     *
     * @param item
     * @return the LO, or NULL if the item is not layouted
     */
    LayoutObject getFirstLayoutObjectOfItem(Item item)
    {
        if(item != null)
        {
            return getNextLayoutObjectOfItem(null, item);
        }
        return null;
    }

    /**
     * Returns the last LayoutObject of a layouted item.
     *
     * @param item
     * @return the LO, or NULL if the item is not layouted
     */
    LayoutObject getLastLayoutObjectOfItem(Item item)
    {
        if(item != null)
        {
            return getPrevLayoutObjectOfItem(null, item);
        }
        return null;
    }

    /**
     * Update item state in form.
     *
     * @param item
     * @param updateReason
     * @param param additional parameter
     */
    void updateItemState(Item item, int updateReason, Object param)
    {
        Logger.method(this, "updateItemState", item,
                      String.valueOf(updateReason), param);

		LayoutObject lo = getFirstLayoutObjectOfItem(item);

		if(lo != null)
		{
			getItemLayouter(item).updateItem(item, lo.getControl(), updateReason,
										 param);
		}

        // Clean reason - without resizing flags
        int reason = updateReason & Item.UPDATE_SIZE_MASK;
        switch(reason)
        {
        case Item.UPDATE_ADDCOMMAND:
        {
            if(isFormCurrent() && param != null)
            {
                if(lo != null && param instanceof Command)
                {
                    lo.addCommand((Command) param);
                }
            }
            break;
        }
        case Item.UPDATE_REMOVECOMMAND:
        {
            if(isFormCurrent() && param != null)
            {
                if(lo != null && param instanceof Command)
                {
                    lo.removeCommand((Command) param);
                }
            }
            break;
        }
        }

        // Check this always - because this is a flag
        if((updateReason & Item.UPDATE_HEIGHT_CHANGED)
                == Item.UPDATE_HEIGHT_CHANGED)
        {
            resizeItemAndShift(item);
        }
    }

    int eswtGetMaxVPosition()
    {
        return formComposite.getSize().y - getFormHeight();
    }

    /**
     * Called by key Form to compute new vertical coordinate to position form's
     * content.
     *
     * @param swtDir scrolling direction.
     * @return New vertical position of Form's content.
     */
    protected int getNextScrollingPosition(int swtDir)
    {
        boolean scrollDown = (swtDir == SWT.ARROW_DOWN
                              || swtDir == SWT.ARROW_RIGHT);
        int formHeight = getFormHeight();
        int refPoint;
        int ret = vPosition;
        if(scrollDown)
        {
            ret += formHeight / 5;
            refPoint = (vPosition + 1) + formHeight;
        }
        else
        {
            ret -= formHeight / 5;
            refPoint = (vPosition - 1);
        }

        Row row = null;
        for(int i = 0; i < getRowCount(); i++)
        {
            row = getRow(i);
            if(row.isInsideRow(refPoint)
                    && (row.getRowHeight() < formHeight))
            {
                if(scrollDown)
                {
                    ret = row.getBottomPosition() - formHeight;
                }
                else
                {
                    ret = row.getYShift();
                }
                break;
            }
        }

        return ret;
    }

    /**
     * Set the scrolling to the specified position.<br>
     * This method also updates the Form's scrollbars.
     *
     * @param position new position
     */
    void eswtSetScrollingPosition(int position, boolean keyNav)
    {
        // check constraints
        int newVPosition = position;
        int maxVPos = eswtGetMaxVPosition();
        newVPosition = Math.min(newVPosition, maxVPos);
        newVPosition = Math.max(newVPosition, 0);

        vPosition = newVPosition;
        formComposite.setRedraw(false);
        formComposite.setOrigin(0, vPosition, keyNav);
        formComposite.pack();
        formComposite.setRedraw(true);

        eswtHandleVisibilityChanges();
    }

    /**
     * Returns the scrolling position.
     */
    protected int getScrollingPosition()
    {
        return vPosition;
    }

    /**
     * Updates visibility status of all items.
     */
    protected void eswtHandleVisibilityChanges()
    {
        // Logger.method(this, "eswtHandleVisibilityChanges");
        boolean shown = false;
        Item item = null;
        LayoutObject lo = null;
        // Go through all LayoutObjects and check/update visibilities
        while((lo = getNextLayoutObjectOfItem(lo, null)) != null)
        {
            // check if owning item is changing
            if(lo.getOwningItem() != item)
            {
                if(item != null)
                {
                    // set current item's visibility
                    getItemLayouter(item).eswtHandleVisibilityChange(item, shown);
                }
                // new item
                item = lo.getOwningItem();
                shown = false;
            }

            // track current item's visibility
            if(!shown && isFormCurrent() && isPartiallyVisible(lo))
            {
                shown = true;
            }
        }

        // call it for last item
        if(item != null)
        {
            getItemLayouter(item).eswtHandleVisibilityChange(item, shown);
        }

		eswtCheckCurrentSelectedItem();
    }

    /**
     * Changes item size and does shift of all Rows.
     *
     * @param item - item to changeSize.
     */
    void resizeItemAndShift(final Item item)
    {
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                int newVPosition = eswtResizeItemAndShift(item);
                if(newVPosition != vPosition)
                {
                    eswtSetScrollingPosition(newVPosition, true);
                }
                else
                {
                    eswtHandleVisibilityChanges();
                }
            }
        });
    }

    /**
     * eSWT callback for resizeItemAndShift.
     *
     * @param item Item to be resized.
     * @return new scrolling vPosition
     */
    int eswtResizeItemAndShift(Item item)
    {
		// save the state of the last row before resizing and Shifting.
		boolean itemWasVisible = isItemPartiallyVisible(item);
        Row row = getLastRowOfItem(item);
        int deltaYShift = row.getRowHeight();
        // if we un-comment this then when we set true,
        // focus will jump to first item.
        // eswtUpdateFormComposite(false);
        LayoutObject lo = getFirstLayoutObjectOfItem(item);
        if(lo != null)
        {
            getItemLayouter(item).eswtResizeObject(lo);
        }

        eswtUpdateRow(row);

        /*
        // to avoid double call of updateRowInternals
        if (row.getNumLayoutObjects(Item.LAYOUT_SHRINK) > 0
                || row.getNumLayoutObjects(Item.LAYOUT_EXPAND) > 0) {
            eswtUpdateRow(row);
        }
        else {
            row.updateRowInternals();
        }
        */
        deltaYShift = row.getRowHeight() - deltaYShift;
        // row's height change - all remaining rows are shifted with this.

        Row lastRow = getLastRow();
        if(row != lastRow)
        {
            for(int i = rows.indexOf(row) + 1; i < getRowCount(); i++)
            {
                row = getRow(i);
                row.setYShift(row.getYShift() + deltaYShift);
                eswtUpdateRow(row);
            }
        }

        // following code updates scrolling if needed.
        // need to check where in the form resize happeneed.

        int itemRowYShift = getLastRowOfItem(item).getYShift();
        // vPosition should be changed in syncexec
        int newVPosition = vPosition;

        // we need to scroll only if changes happened above the screen.
        if(newVPosition >= itemRowYShift)
        {
            newVPosition = Math.max(0, newVPosition + deltaYShift);
        }
        // check to avoid gap in the bottom of the form
        if(newVPosition + getFormHeight() > lastRow.getBottomPosition())
        {
            newVPosition = Math.max(0,
                                    lastRow.getBottomPosition() - getFormHeight());
        }

        eswtUpdateFormComposite(true);

        // formComposite.pack();
        // formComposite.updateScrollbarSize(lastRow.getBottomPosition());

		if(item == currentSelectedItem)
		{
			if(itemWasVisible)
			{
				int itemTop = getItemTopPosition(item);
				int itemBottom = getItemBottomPosition(item);
				// currentSelectedItem has to be focused if it was focused
				// before resizing e.g TextField when it is resized by adding a
				// new row and it was in the bottom of the Screen.
				if(newVPosition <= itemTop
						&& (newVPosition + getFormHeight()) >= itemBottom)
				{
					// do not change vPosition;
				}
				else if(newVPosition > itemTop)
				{
					newVPosition = itemTop;
				}
				else if((newVPosition + getFormHeight()) < itemBottom)
				{
					newVPosition = itemBottom - getFormHeight();
				}
			}
		}
		return newVPosition;
    }

    /**
     * Gets layouter that can layout the specified item.
     *
     * @param item Item to be layouted.
     * @return ItemLayouter or null if no Layouter found.
     */
    protected final ItemLayouter getItemLayouter(Item item)
    {
        if(item instanceof StringItem)
        {
            return sIL;
        }
        else if(item instanceof ImageItem)
        {
            return imIL;
        }
        else if(item instanceof Gauge)
        {
            return gL;
        }
        else if(item instanceof TextField)
        {
            return tfL;
        }
        else if(item instanceof DateField)
        {
            return dfL;
        }
        else if(item instanceof ChoiceGroup)
        {
            return cgL;
        }
        else if(item instanceof CustomItem)
        {
            return ciL;
        }
        else if(item instanceof Spacer)
        {
            return sL;
        }
        return null;
    }

    /**
     * Returns eSWT Control that represents the item specified.
     */
    Control getItemControl(final Item item)
    {
        final LayoutObject lo = getFirstLayoutObjectOfItem(item);
        if(lo != null)
        {
            ESWTUIThreadRunner.syncExec(new Runnable()
            {
                public void run()
                {
                    itemMainControl = getItemLayouter(item)
                                      .eswtFindSpecificControl(item, lo.getControl());
                }
            });
            return itemMainControl;
        }
        return null;
    }

    void updateScrolling(final int value, final boolean keyNav)
    {
        Logger.method("updateScrolling", String.valueOf(value));
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {

                eswtSetScrollingPosition(value, keyNav);

            }
        });
    }

    /**
     * Get current scrolling value.
     *
     * @return vPosition.
     */
    int getVPosition()
    {
        return vPosition;
    }


	private Timer eventTimer = new Timer();
	private EventGeneratorTask eventTask;

	/**
	 * Reset timer for do layout with a given start index.
	 */
	private void resetEventTimer(boolean directionUp, int y)
	{
		if(eventTimer != null)
		{
			if(eventTask != null)
			{
				eventTask.cancel();
				eventTask = null;
			}
			// schedule new timer
			eventTask = new EventGeneratorTask(directionUp, y);
			eventTimer.schedule(eventTask, Config.DFI_EVENT_TIMER_DELAY);
		}
	}

	/**
	 * Form Timer task. Triggers the formComposite to Layout.
	 */
	class EventGeneratorTask extends TimerTask
	{

		private boolean isUpDirection;
		private int localY;

		public EventGeneratorTask(boolean direction, int y)
		{
			isUpDirection = direction;
			localY = y;
			Logger.info("y is " + localY);
		}

		public void run()
		{
			if(isUpDirection)
			{
				localY -= Config.DFI_EVENT_MOVE_DELTA;
			}
			else
			{
				localY += Config.DFI_EVENT_MOVE_DELTA;
			}
			handlePointerEvent(0, localY, SWT.MouseMove);
		}
	}

}