javauis/lcdui_qt/src/javax/microedition/lcdui/TextFieldLayouter.java
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:27:20 +0300
changeset 21 2a9601315dfc
child 23 98ccebc37403
permissions -rw-r--r--
Revision: v2.1.22 Kit: 201018

/*
* 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.ConstrainedText;
import org.eclipse.ercp.swt.mobile.TextExtension;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

/**
 * Responsible for correct layout of TextField in a Form.
 */
class TextFieldLayouter extends ItemLayouter {

    /**
     * Key name for modify listener.
     */
    private static final String MODIFY_LISTENER = "modify";

    /**
     * Key name for key listener.
     */
    private static final String KEY_LISTENER = "key";

    /**
     * Key name for mouse listener.
     */
    private static final String MOUSE_LISTENER = "mouse";


    /**
     * Percentage of the whole screen.
     */
    private static final int TOTAL_PERCENTAGE = 100;


    // private static Control[] staticControls = new Control[6];

    private static TextExtension any;
    private static ConstrainedText numeric;
    private static ConstrainedText phonenr;
    private static ConstrainedText decimal;
    private static TextExtension   email;
    private static TextExtension   url;
    private static boolean isCorrectText;

    /**
     * Constructor.
     *
     * @param dflp DefaultFormLayoutPolicy`
     */
    TextFieldLayouter(DefaultFormLayoutPolicy dflp) {
        super(dflp);
    }

    /**
     * Get static eSWT control (ConstraintText or TextExtension).
     *
     * @param constraint
     */
    static Control eswtGetStaticTextControl(int constraint) {
        Control ret = null;

        /*
        int maskedConstraint = constraint & TextField.CONSTRAINT_MASK;

        if (staticControls[maskedConstraint] == null) {
            staticControls[maskedConstraint] = TextWrapper.eswtConstructText(
                    eswtGetStaticShell(), SWT.MULTI | SWT.WRAP, constraint);
            ret = staticControls[maskedConstraint];
        }
        */

        if (constraint == TextField.NUMERIC) {
            if (numeric == null) {
                numeric = new ConstrainedText(eswtGetStaticShell(), SWT.SINGLE,
                        ConstrainedText.NUMERIC);
            }
            ret = numeric;
        }
        else if (constraint == TextField.DECIMAL) {
            if (decimal == null) {
                decimal = new ConstrainedText(eswtGetStaticShell(), SWT.SINGLE,
                        ConstrainedText.DECIMAL);
            }
            ret = decimal;
        }
        else if (constraint == TextField.PHONENUMBER) {
            if (phonenr == null) {
                phonenr = new ConstrainedText(eswtGetStaticShell(), SWT.SINGLE,
                        ConstrainedText.PHONENUMBER);
            }
            ret = phonenr;
        }
        else {
            // TODO: eSWT support required - text validation on EMAIL and URL constraints
            // default
            if (any == null) {
                any = new TextExtension(eswtGetStaticShell(), SWT.MULTI | SWT.WRAP);
            }
            ret = any;
        }

        return ret;
    }

    /**
     * Check that text satisfies specified constraints.
     *
     * @param constraint TextField.NUMERIC etc.
     * @return true if text is correct for specified constraint.
     */
    static boolean checkText(final int constraint, final String text) {
        isCorrectText = true;
        ESWTUIThreadRunner.syncExec(new Runnable() {
            public void run() {
                try {
                    TextWrapper.eswtSetContent(
                            eswtGetStaticTextControl(constraint), text);
                }
                catch (IllegalArgumentException e) {
                    isCorrectText = false;
                }
            }
        });
        return isCorrectText;
    }

    /**
     * eSWT specific calls to implement getControl.
     *
     * @param parent for the control.
     * @param item TextField item.
     */
    Control eswtGetControl(Composite parent, Item item) {
        TextField textfield = (TextField) item;

        Control te = TextWrapper.eswtConstructText(parent,
                SWT.WRAP | SWT.MULTI, textfield.getConstraints());
        TextWrapper.eswtSetMaxSize(te, textfield.getMaxSize());
        TextWrapper.eswtSetContent(te, textfield.getString());
        TextWrapper.eswtSetSelection(te,
                textfield.getCaretPosition(), textfield.getCaretPosition());

        if (textfield.getInitialInputMode() != null) {
            eswtUpdateItem(textfield, te, TextField.UPDATE_INITIAL_INPUT_MODE,
                    null);
        }
        return te;
    }

    /**
     * Returns true if this eSWT control is suitable to be used for updating.
     *
     * @param item Item.
     * @param control eSWT control.
     *
     * @return true if this control is suitable for update
     */
    boolean eswtIsSpecificControl(Item item, Control control) {
        return (control instanceof TextExtension
                || control instanceof ConstrainedText);
    }

    /**
     * Updates the values of TextField.
     *
     * @param item Item.
     * @param control eSWT control.
     * @param reason reason to update.
     */
    void eswtUpdateItem(Item item, Control control, int reason, Object param) {
        TextField textfield = (TextField) item;
        if (reason == Item.UPDATE_CONTENT) {
            TextWrapper.eswtSetContent(control, textfield.getString());
        }
        else if (reason == TextField.UPDATE_INITIAL_INPUT_MODE) {
            TextWrapper.eswtSetInputMode(control,
                    textfield.getInitialInputMode(),
                    textfield.getConstraints());
        }
    }

    /**
     * Update size of TextField.
     *
     * @param item TextField.
     * @param control Control which represents TextField.
     * @param width which control must occupy.
     */
    void eswtResizeControl(Item item, Control control, int width, int height) {
        super.eswtResizeControl(item, control, width, height);
        if (control instanceof TextExtension) {
            TextExtension te = (TextExtension) control;
            ((TextField) item).internalSetLinesCount(te.getLineCount());
        }
    }

    /**
     * Returns true if that key was consumed by TextField.
     *
     * @param item TextField.
     * @param key keyCode.
     */
    boolean eswtOfferKeyPressed(Item item, int key) {
        TextField tf = (TextField) item;
        if (item.hasLayout(Item.LAYOUT_SHRINK)) {
            if ((key == SWT.ARROW_LEFT
                    && tf.getCaretPosition() == 0)
                || (key == SWT.ARROW_RIGHT
                    && tf.getCaretPosition() == tf.size())) {
                return false;
            }
        }
        if (((key == SWT.ARROW_UP)
                    && (tf.getCaretPosition() == 0))
            || ((key == SWT.ARROW_DOWN)
                    && (tf.getCaretPosition() == tf.size()))) {
            return false;
        }
        return true;
    }

    /**
     * Responsible for reacting on focusGained event, and according to direction
     * from which that event comes sets the caret of the TextField.
     *
     * @param item TextField.
     * @param dir direction from which focus came, in case if it was set with
     *            setCurrentItem() default direction is used (-1).
     */
    void eswtFocusGained(Item item, int dir) {
        super.eswtFocusGained(item, dir);
        TextField tf = (TextField) item;
        // direction = dir;
        resetCaretPosition(tf, dir);
        Control control = eswtGetFirstSpecificControl(item);
        TextWrapper.eswtSetSelection(control,
                tf.getCaretPosition(), tf.getCaretPosition());
    }

    /**
     * Returns the minimum area needed to display a TextField.
     *
     * @param textField TextField object
     * @return Minimum area needed to display TextField.
     */
    static Point calculateMinimumBounds(final TextField textField) {
        final Point minSize = new Point(0, 0);
        ESWTUIThreadRunner.syncExec(new Runnable() {
            public void run() {
                TextExtension tempExt = (TextExtension) eswtGetStaticTextControl(TextField.ANY);
                tempExt.setText(ItemLayouter.MIN_TEXT);
                tempExt.pack();
                minSize.x = tempExt.getSize().x;
                minSize.y = tempExt.getSize().y + Config.TEXTFIELD_MARGIN;
                applyMinMargins(textField, minSize);
            }
        });
        return minSize;
    }

    /**
     * Returns the preferred area needed to display an Item.
     *
     * @param item Item.
     * @return Preferred area needed to display Item. x is width and y is
     *         height.
     */
    static Point calculatePreferredBounds(Item item) {
        final TextField textfield = (TextField) item;
        final Point prefSize = new Point(0, 0);
        ESWTUIThreadRunner.syncExec(new Runnable() {
            public void run() {
                TextExtension te = (TextExtension) eswtGetStaticTextControl(TextField.ANY);
                te.setText(textfield.getString());

                int maxHeight = (formHeigh
                        * Config.TEXTFIELD_MAX_SCREEN_PERCENTAGE / TOTAL_PERCENTAGE)
                        - Config.TEXTFIELD_MARGIN;
                textfield.internalSetMaxVisibleLines(maxHeight
                        / te.getLineHeight());

                prefSize.x = getMaximumItemWidth(textfield);
                prefSize.y = Config.TEXTFIELD_MARGIN + Math.min(
                        te.computeSize(prefSize.x, SWT.DEFAULT).y, maxHeight);
                // prefSize.y = Math.min(calc.y, maxHeight) + MARGIN;
                applyPrefMargins(textfield, prefSize);
            }
        });
        return prefSize;
    }

    /**
     * Update caret position based on direction.
     *
     * @param textfield TextField for which to update caret position.
     * @param dir direction of scrolling.
     */
    private void resetCaretPosition(TextField textfield, int dir) {
        switch (dir) {
            case SWT.ARROW_DOWN:
                textfield.internalSetCaretPosition(0);
                break;
            case SWT.ARROW_RIGHT:
                textfield.internalSetCaretPosition(0);
                break;
            case SWT.ARROW_UP:
                textfield.internalSetCaretPosition(textfield.size());
                break;
            case SWT.ARROW_LEFT:
                textfield.internalSetCaretPosition(textfield.size());
                break;
            default:
                break;
        }
    }

    /**
     * Add TextField listeners when form is activated.
     *
     * @param item TextField.
     * @param control Control which represents TextField.
     */
    void eswtAddSpecificListeners(Item item, Control control) {
        super.eswtAddSpecificListeners(item, control);
        TextField textfield = (TextField) item;
        ModifyListener listener = new TextFieldModifyListener(textfield);
        TextWrapper.eswtAddModListener(control, listener);
        control.setData(MODIFY_LISTENER, listener);
        KeyListener listener2 = new TextFieldKeyListener(textfield);
        control.addKeyListener(listener2);
        control.setData(KEY_LISTENER, listener2);
        MouseListener listener4 = new AllMouseListener(textfield);
        control.addMouseListener(listener4);
        control.setData(MOUSE_LISTENER, listener4);
    }

    /**
     * Remove listeners from a TextField if the form goes to background.
     *
     * @param item TextField.
     * @param control Control which represents TextField.
     */
    void eswtRemoveSpecificListeners(Item item, Control control) {
        super.eswtRemoveSpecificListeners(item, control);
        ModifyListener l1 = (ModifyListener) control.getData(MODIFY_LISTENER);
        if (l1 != null) {
            TextWrapper.eswtRemoveModListener(control, l1);
            control.setData(MODIFY_LISTENER, null);
        }
        KeyListener l2 = (KeyListener) control.getData(KEY_LISTENER);
        if (l2 != null) {
            control.removeKeyListener(l2);
            control.setData(KEY_LISTENER, null);
        }
        MouseListener l4 = (MouseListener) control.getData(MOUSE_LISTENER);
        if (l4 != null) {
            control.removeMouseListener(l4);
            control.setData(MOUSE_LISTENER, null);
        }
    }

    /**
     * Class that receives ModifyEvents from TextExtension and updates values of
     * TextField.
     */
    class TextFieldModifyListener implements ModifyListener {

        private TextField textfield;

        TextFieldModifyListener(TextField textField) {
            this.textfield = textField;
        }

        private void handleLinesChange(TextExtension te) {
            int lines = te.getLineCount();
            int visibleLines = te.getSize().y / te.getLineHeight();
            if (lines != textfield.internalGetLinesCount()) {
                textfield.internalSetLinesCount(lines);
                Control control = eswtGetFirstControl(textfield);
                if (control.getSize().y + te.getLineHeight()
                        + Config.TEXTFIELD_MARGIN <= dfi.getFormHeight()) {
                    textfield.updateParent(Item.UPDATE_HEIGHT_CHANGED);
                }
                if (textfield.internalGetLinesCount() > lines) {
                    if ((te.getTopIndex() + visibleLines) > lines) {
                        te.setTopIndex(Math.max(0, lines - visibleLines));
                    }
                    if (visibleLines > lines) {
                        textfield.updateParent(Item.UPDATE_HEIGHT_CHANGED);
                    }
                }

                te.setTopIndex(Math.max(te.getCaretLineNumber() + 1
                        - textfield.internalGetMaxVisibleLines(), 0));
            }
        }

        public void modifyText(ModifyEvent modifyEvent) {
            Control te = (Control) modifyEvent.widget;
            if (textfield.internalSetString(TextWrapper.eswtGetContent(te))) {
                Logger.method(textfield, "modify", modifyEvent);
                textfield.internalSetCaretPosition(
                        TextWrapper.eswtGetCaretPosition(te));
                if (te instanceof TextExtension) {
                    handleLinesChange((TextExtension) te);
                }
                textfield.notifyStateChanged();
            }
        }
    }

    /**
     * Class that receives KeyEvents from TextExtension and updates
     * caret position for TextField.
     */
    class TextFieldKeyListener implements KeyListener {

        private TextField textfield;

        TextFieldKeyListener(TextField textField) {
            this.textfield = textField;
        }

        public void keyPressed(KeyEvent keyEvent) {
            Control te =  (Control) keyEvent.widget;
            int caretPos = TextWrapper.eswtGetCaretPosition(te);
            int caretLine = TextWrapper.eswtGetCaretLine(te);

            if (keyEvent.keyCode == SWT.ARROW_UP && caretLine == 0) {
                caretPos = 0;
                TextWrapper.eswtSetSelection(te, caretPos, caretPos);
            }
            else if (keyEvent.keyCode == SWT.ARROW_DOWN
                    && (caretLine == (TextWrapper.eswtGetLineCount(te) - 1))) {
                caretPos = textfield.size();
                TextWrapper.eswtSetSelection(te, caretPos, caretPos);
            }

            textfield.internalSetCaretPosition(caretPos);
        }

        public void keyReleased(KeyEvent keyEvent) {
            // this is needed if focus was changed with touch.
            // so ne scrolling was done in DFI.
            if (!dfi.isItemFullyVisible(textfield)) {
                dfi.eswtScrollToItem(textfield);
            }
            textfield.internalSetCaretPosition(
                    TextWrapper.eswtGetCaretPosition((Control) keyEvent.widget));
        }

    }

    class AllMouseListener implements MouseListener, MouseMoveListener {

        private TextField textfield;
        private boolean isEnabled;

        AllMouseListener(TextField tf) {
            textfield = tf;
        }

        public void enable(boolean enabled) {
            isEnabled = enabled;
        }

        public void mouseUp(MouseEvent me) {
            if (isEnabled) {
                //
            }
        }

        public void mouseDown(MouseEvent me) {
            textfield.internalSetCaretPosition(
                    TextWrapper.eswtGetCaretPosition((Control) me.widget));
        }

        public void mouseMove(MouseEvent me) {
        }

        public void mouseDoubleClick(MouseEvent me) {
        }

    }

}