javauis/lcdui_akn/lcdui/src/CMIDTextFieldItem.cpp
branchRCL_3
changeset 66 2455ef1f5bbc
child 77 7cee158cb8cd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_akn/lcdui/src/CMIDTextFieldItem.cpp	Wed Sep 01 12:33:18 2010 +0100
@@ -0,0 +1,691 @@
+/*
+* Copyright (c) 2003 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:  ?Description
+*
+*/
+
+
+#include <eikcapc.h>
+//
+#include "CMIDTextFieldItem.h"
+// CMIDEdwin API for iTextField and CMIDTextFieldEdwin::NewL
+#include "CMIDTextFieldEdwin.h"
+// API for iLabelControl
+#include "CMIDItemLabel.h"
+// CAknEdwinDrawingModifier API for iDrawingModifier
+#include <aknedwindrawingmodifier.h>
+
+// LAF in UpdateLayout
+#include <aknlayoutscalable_avkon.cdl.h>
+// information to decorate text
+#include <AknTextDecorationMetrics.h>
+
+#include <j2me/jdebug.h>
+
+/** This macro is executed each time a trapped call returns an error code different than KErrNone */
+#undef  TRAP_INSTRUMENTATION_LEAVE
+#define TRAP_INSTRUMENTATION_LEAVE(aResult) DEBUG_INT2("In CMIDTextFieldItem.cpp, trapped method was called at line %D and got exception %D", __LINE__, aResult);
+
+
+MMIDTextField* CMIDTextFieldItem::NewL(
+    const TDesC& aLabel, const TDesC& aText, TInt aConstraints,
+    TInt aMaxSize, CMIDUIManager* aUIManager)
+{
+    CMIDTextFieldItem* textFieldItem = new(ELeave)CMIDTextFieldItem(aUIManager);
+    CleanupStack::PushL(textFieldItem);
+    textFieldItem->ConstructL(aLabel,aText,aConstraints,aMaxSize);
+    CleanupStack::Pop(textFieldItem);
+    return textFieldItem;
+}
+
+CMIDTextFieldItem::~CMIDTextFieldItem()
+{
+    if (iText)
+    {
+        delete iText;
+        iText = NULL;
+    }
+    if (iTextField)
+    {
+        delete iTextField;
+        iTextField = NULL;
+    }
+    if (iDrawingModifier)
+    {
+        delete iDrawingModifier;
+        iDrawingModifier = NULL;
+    }
+}
+
+void CMIDTextFieldItem::DeleteTextL(TInt aOffset,TInt aLength)
+{
+    TextComponent()->DeleteTextL(aOffset,aLength);
+}
+
+void CMIDTextFieldItem::SetTextL(const TDesC& aText)
+{
+    ASSERT(iTextField);
+    // Position of cursor is temporally set to start of text.
+    // This is prevetion of panic caused cursor posited off rectagle of CMIDTextField.
+    iTextField->SetCursorPosL(0, EFalse);
+    iTextField->SetTextL(aText);
+    // Setting position to right position.
+    iTextField->SetCursorPosL(aText.Length(), EFalse);
+}
+
+void CMIDTextFieldItem::InsertTextL(const TDesC& aText,TInt aPosition)
+{
+    TextComponent()->InsertTextL(aText,aPosition);
+}
+
+void CMIDTextFieldItem::SetConstraintsL(TUint aConstraints)
+{
+    HBufC* text = iTextField->GetTextL();
+    CleanupStack::PushL(text);
+    TInt maxSize = iTextField->GetMaxSize();
+
+    delete iTextField;
+    iTextField = NULL;
+
+    CreateTextFieldL(KNullDesC, aConstraints, maxSize);
+    iTextField->SetTextWithNewConstraintsL(text);
+
+    if (iForm && IsFocused())
+    {//in case non-midlet cmds have been added or removed
+        iForm->CurrentDisplayable().InitializeCbasL();
+    }
+
+    if (iForm)
+    {
+        // the newly created textbox needs to be layouted, request layout for the whole form
+        // as applying new constraints may change the size of the textfield
+        iForm->RequestLayoutL();
+    }
+
+    CleanupStack::PopAndDestroy(text);
+}
+
+void CMIDTextFieldItem::CreateTextFieldL(const TDesC& aText, TUint aConstraints, TInt aMaxSize)
+{
+    ASSERT(!iTextField);
+
+    iTextField = CMIDTextFieldEdwin::NewL(aConstraints, aText, aMaxSize, NULL, this);
+    iTextField->SetFocusing(ETrue);
+    iTextField->SetEdwinSizeObserver(this);
+    iTextField->SetEdwinObserver(this);
+
+    // Get cursor position set by CMIDEdwin::NewL. This is needed since SetContainerWindowL
+    // and ActivateL calls lead into cursor position resetting, i.e. setting it to 0 pos.
+    TInt cursorPos = iTextField->CursorPos();
+
+    if (IsFocused())
+    {
+        iTextField->SetFocus(ETrue);
+    }
+
+    if (DrawableWindow())
+    {
+        // do not set container window if the fieldItem does not have CCoeControl::iWin set
+        iTextField->SetContainerWindowL(*this);
+    }
+
+    if (IsActivated())
+    {
+        iTextField->ActivateL();
+    }
+
+    // Restore original cursor position
+    iTextField->SetCursorPosL(cursorPos, EFalse);
+}
+
+void CMIDTextFieldItem::UpdateLayout()
+{
+    TAknWindowComponentLayout textPaneLayout =
+        AknLayoutScalable_Avkon::form2_midp_text_pane(0);
+
+    TAknTextComponentLayout textLayout =
+        AknLayoutScalable_Avkon::form2_mdip_text_pane_t1(0);
+
+    // set the top margin for Edwin
+    iEdwinMarginTop = textPaneLayout.LayoutLine().it;
+
+    // set the minimum height of the Edwin (one line text)
+    TAknTextDecorationMetrics decorationMetrics(textLayout.LayoutLine().iFont);
+    TInt topMargin, bottomMargin;
+
+    decorationMetrics.GetTopAndBottomMargins(topMargin, bottomMargin);
+
+    iEdwinMinHeight = textPaneLayout.LayoutLine().iH + topMargin + bottomMargin;
+}
+
+
+TInt CMIDTextFieldItem::SetMaxSizeL(TInt aMaxSize)
+{
+    return TextComponent()->SetMaxSizeL(aMaxSize);
+}
+
+TInt CMIDTextFieldItem::GetMaxSize()
+{
+    return TextComponent()->GetMaxSize();
+}
+
+TInt CMIDTextFieldItem::Size()
+{
+    return TextComponent()->Size();
+}
+
+TInt CMIDTextFieldItem::GetCaretPosition()
+{
+    return TextComponent()->GetCaretPosition();
+}
+
+HBufC* CMIDTextFieldItem::GetTextL()
+{
+    return TextComponent()->GetTextL();
+}
+
+void CMIDTextFieldItem::SetInitialInputModeL(const TDesC& aCharacterSubset)
+{
+    TextComponent()->SetInitialInputModeL(aCharacterSubset);
+}
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+CMIDTextFieldItem::CMIDTextFieldItem(CMIDUIManager* aUIManager)
+        : CMIDControlItem(EDefault, aUIManager)
+{
+    iMMidItem = this;
+    SetFocusing(ETrue);
+}
+
+/** Construct the item.
+
+We chose to inhibit redrawing of the edwin on reformat,
+see CEikEdwin::OnReformatL() - this is called when laying-out the edwin.
+This is done via iDrawingModifier. This avoids drawing of edwin in the wrong
+position when modifying the form, or changing focus between items and resizing
+the item when inserting or removing a line.
+*/
+void CMIDTextFieldItem::ConstructL(const TDesC& aLabel,const TDesC& aText,
+                                   TInt aConstraints, TInt aMaxSize)
+{
+    CMIDControlItem::ConstructL();
+
+    TInt formWidth = FormClientAreaWidth();
+    SetSize(TSize(formWidth, 0));
+
+    SetLabelL(aLabel);
+    iLabelControl->SetWidthL(formWidth);
+
+    iDrawingModifier = new(ELeave)CAknEdwinDrawingModifier();
+    iDrawingModifier->SetInhibitNotifyNewFormatDrawing(ETrue);
+
+    CreateTextFieldL(aText, aConstraints, aMaxSize);
+
+    UpdateLayout();
+}
+
+CMIDEdwin* CMIDTextFieldItem::TextComponent() const
+{
+    return iTextField;
+}
+
+/** The minimum size is usually the full form width and the height of the label, plus
+    the minimum height of the editor (usually one line) plus 3 times the vertical margin. */
+TSize CMIDTextFieldItem::MinimumSize()
+{
+    TInt minHeight =
+        iEdwinMarginTop +
+        iEdwinMinHeight +
+        ItemContentBottomMargin();
+
+    if (iLabelControl && iLabelControl->Text()->Length())
+        minHeight += OneLineLabelHeight();
+
+    return TSize(FormClientAreaWidth(), minHeight);
+}
+
+TInt CMIDTextFieldItem::CountComponentControls() const
+{
+    return 2;
+}
+
+CCoeControl* CMIDTextFieldItem::ComponentControl(TInt aIndex) const
+{
+    switch (aIndex)
+    {
+    case 0:
+        return iLabelControl;
+    case 1:
+        return iTextField;
+    }
+    return NULL;
+}
+
+void CMIDTextFieldItem::Draw(const TRect& aRect) const
+{
+    CMIDControlItem::Draw(aRect);
+}
+
+void CMIDTextFieldItem::SizeChanged()
+{
+    TPoint position = Position();
+
+    TRect labelRect(position, TSize(FormClientAreaWidth(), LabelHeight()));
+
+    TPoint textTl = TPoint(position.iX,
+                           LabelHeight() > 0 ? labelRect.iBr.iY : position.iY);
+    TPoint textBr = Rect().iBr - TPoint(0, ItemContentBottomMargin());
+
+    TRect textRect(textTl, textBr);
+
+    if (LabelHeight() > 0)
+    {
+        iLabelControl->SetRect(labelRect);
+    }
+
+    if (iTextField)
+    {
+        iTextField->DoLayout(textRect);
+    }
+
+    CMIDControlItem::SizeChanged();
+}
+
+void CMIDTextFieldItem::SetContainerWindowL(const CCoeControl& aContainer)
+{
+    CMIDControlItem::SetContainerWindowL(aContainer);
+
+    // Save the original cursor position. This is needed since SetContainerWindowL
+    // and ActivateL call lead into cursor position resetting, i.e. setting it to 0 pos.
+    TInt cursorPos = iTextField->CursorPos();
+
+    iTextField->SetContainerWindowL(*this);
+
+    SetObserver(iForm);
+
+    ActivateL();
+
+    // Restore original cursor position
+    iTextField->SetCursorPosL(cursorPos, EFalse);
+
+}
+
+void CMIDTextFieldItem::FocusChanged(TDrawNow aDrawNow)
+{
+    if (IsFocused())
+    {
+        // Setting focus to iTextField changes the cursor visibility. This might cause
+        // a cursor flashing on the screen, if the focus is set to a TF that is not in the
+        // visible form area currently when a form is switched from background
+        // to foreground. To prevent this EAvkonDisableCursor is set temporarily.
+        iTextField->AddFlagToUserFlags(CEikEdwin::EAvkonDisableCursor);
+        iTextField->SetFocus(ETrue);
+        iTextField->RemoveFlagFromUserFlags(CEikEdwin::EAvkonDisableCursor);
+        SetCursorVisibility(IsVisible());
+    }
+    else
+    {
+        iTextField->SetFocus(EFalse);
+    }
+
+    CMIDControlItem::FocusChanged(aDrawNow);
+    // DoLayout and change text color when focused
+    SizeChanged();
+    TRAP_IGNORE(UpdateTextColorsL());
+}
+
+void CMIDTextFieldItem::HandleCurrentL(TBool aCurrent)
+{
+    // set initial input modes when TextField becomes the focused item
+    iTextField->HandleCurrentL(aCurrent);
+}
+
+void CMIDTextFieldItem::ResolutionChange(TInt /*aType*/)
+{
+    UpdateLayout();
+}
+
+void CMIDTextFieldItem::PostFocusTransferEvent(TBool aFocus, CMIDForm::TDirection /*aDirection*/)
+{
+    TRAP_IGNORE(HandleCurrentL(aFocus));
+}
+
+/** Basically pass the key event to the text box. Also check if the text has been
+modified and if so report an item state change event. Attention must be taken here
+because the text box may eat the EEventKey type so we must store the existing text
+in the key down event and compare it in the key up event. */
+TKeyResponse CMIDTextFieldItem::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
+{
+
+    if (aType == EEventKeyDown)
+    {
+        delete iText;
+        iText = NULL;
+
+        iText = iTextField->GetTextInHBufL();
+    }
+    return iTextField->OfferKeyEventL(aKeyEvent, aType);
+}
+
+#ifdef RD_SCALABLE_UI_V2
+void CMIDTextFieldItem::HandlePointerEventL(const TPointerEvent& aPointerEvent)
+{
+    if (AknLayoutUtils::PenEnabled())
+    {
+        if (!iForm->PhysicsScrolling())
+        {
+            SetCursorVisibility(ETrue);
+        }
+
+        //update focus
+        if (!iForm->TryDetectLongTapL(aPointerEvent))
+        {
+#ifdef RD_JAVA_S60_RELEASE_9_2
+            // In single click UI VKB is opened with first tap.
+            if (!iForm->PhysicsScrolling())
+#else
+            // In non-single click UI,
+            // VKB is opened when tapping already focused item.
+            if (!iForm->IsFocusChangingWithPen() && IsFocused() && !iForm->PhysicsScrolling())
+#endif // RD_JAVA_S60_RELEASE_9_2
+            {
+                TPointerEvent pEvent = aPointerEvent;
+                // If label area is tapped the coordinates are transferred to editor area.
+                // By doing coordinate switching VKB is launched also when label is tapped.
+                if (iLabelControl->Rect().Contains(pEvent.iPosition))
+                {
+                    pEvent.iPosition.iY = iTextField->Rect().iTl.iY;
+                }
+                CCoeControl::HandlePointerEventL(pEvent);
+            }
+            //else do not forward pointer event to CCoeControl as do not want to have any action
+            //on unfocused TextField.
+        }
+    }
+}
+#endif // RD_SCALABLE_UI_V2
+
+void CMIDTextFieldItem::MakeVisible(TBool aVisible)
+{
+    CMIDControlItem::MakeVisible(aVisible);
+    SetCursorVisibility(aVisible);
+}
+
+TSize CMIDTextFieldItem::ResetPreferredSize() const
+{
+    CMIDTextFieldItem* self = const_cast<CMIDTextFieldItem*>(this);
+    TRAP_IGNORE(self->SetPreferredSizeL(iRequestedPreferredSize));
+    return iPreferredSize;
+}
+
+/** This is called before the form is re-laid-out. See ResetPreferredSize().
+    If we have a requested height we select the max between this height and our
+    minimum height. The width is always the form or screen width. Then we do layout.
+
+    If we don't have a requested height, we do layout with a negative height
+    (-1) and this signals to CMIDEdwin to use whatever height correcsponds to the
+    number of formatted lines. See CMIDEdwin::DoLayout(). So, in this case iPreferredSize
+    is set after doing layout.
+
+    The editor can be resized in HandleEdwinSizeEventL() only if the preferred height has
+    not been given.
+
+    @note SetPreferredSize() always sets TextField's preferred size
+    (i.e. how much space it needs so all control (label+text) is visible)
+*/
+void CMIDTextFieldItem::SetPreferredSizeL(const TSize& aSize)
+{
+    if (!iTextField)
+        return;
+
+    iRequestedPreferredSize = CheckRequestedSize(aSize);
+    iPreferredSize = iRequestedPreferredSize;
+
+    iPreferredSize.iWidth = FormClientAreaWidth(); //This is our fixed width, no less, no more
+
+    TSize editorSize(iPreferredSize);
+
+    // calculate the preferred size case
+    TRect rect(TPoint(0,0), editorSize);
+    iTextField->DoLayout(rect);
+
+    iPreferredSize.iHeight = LabelHeight() + ItemPreferredHeightWithoutLabel();
+}
+
+TInt CMIDTextFieldItem::ItemPreferredHeightWithoutLabel()
+{
+    TInt height = ((CCoeControl*)iTextField)->Size().iHeight +
+                  iEdwinMarginTop + ItemContentBottomMargin();
+
+    return height;
+}
+
+TRect CMIDTextFieldItem::FocusableRect()
+{
+    return iTextField->Rect();
+}
+
+/** We set the new height of the textbox and form item by calling SetSize(). If we
+are inside a form (which we most likely are) we then call CMIDForm::RefreshItemL(this).
+This will take care of re-positioning all other items and updating the scroll bars.
+
+We will not resize the editor if the use has specified a preferrred height
+(iRequestedPreferredSize.iHeight) in which case the editor must be either this height
+or the minimum height. Also, if iPreferredSize.iHeight is -1, it means we are inside
+a SetPreferredSizeL() call, so we mustn't call  iForm->RequestLayoutL() because this
+method ends up calling SetPreferredSizeL() and so we end up in infinite recursive calls.
+So iPreferredSize.iHeight == -1 means do not do anything basically. */
+TBool CMIDTextFieldItem::HandleEdwinSizeEventL(CEikEdwin* /*aEdwin*/,
+        TEdwinSizeEvent /*aEventType*/, TSize aDesirableEdwinSize)
+{
+    if (iTextField->IsPartialFormatting())
+    {
+        // ignore the asynchronous HandleEdwinSizeEvents (e.g TextLength() > 1900 chars)
+        // that will occur much later when Edwin finishes formatting.
+        // This is a protection against requesting form layout while form might be scrolling.
+        // In this partial formatting mode, the preferred size reflects only those number of lines
+        // that were reformatted when quering for the preferred size.
+        // This way, TextFieldItem will continue to have the preferred size (which does not show all visible lines).
+
+        return EFalse;
+    }
+
+    if (iPreferredSize.iHeight != -1)
+    {
+        TInt desiredHeight =
+            LabelHeight() +
+            iEdwinMarginTop +
+            aDesirableEdwinSize.iHeight +
+            ItemContentBottomMargin();
+
+        if (iPreferredSize.iHeight != desiredHeight)
+        {
+            if (iForm)
+            {//new layout for form, hence new edwin size will be taken into account
+                iForm->RequestLayoutL(ETrue);
+            }
+        }
+    }
+
+    return EFalse;
+}
+
+
+TTypeUid::Ptr CMIDTextFieldItem::MopSupplyObject(TTypeUid aId)
+{
+    // Return the edwin drawing modifier, CAknEdwinDrawingModifier, if ID matches, see
+    // HandleEdwinSizeEventL(). Otherwise call CMIDControlItem::MopSupplyObject().
+    if (aId.iUid == CAknEdwinDrawingModifier::ETypeId)
+        return aId.MakePtr(iDrawingModifier);
+
+    return CMIDControlItem::MopSupplyObject(aId);
+}
+
+void CMIDTextFieldItem::HandleEdwinEventL(CEikEdwin* /*aEdwin*/,TEdwinEvent /*aEventType*/)
+{
+    DoHandleEdwinEventL();
+}
+
+void CMIDTextFieldItem::HandleControlEventL(CCoeControl* /*aSource*/,
+        MCoeControlObserver::TCoeEvent aEventType)
+{
+    if (aEventType == MCoeControlObserver::EEventStateChanged)
+    {
+        DoHandleEdwinEventL();
+
+        // check if text has changed and eventually notify Java via
+        // itemStateChanged()
+        HBufC* newText = iTextField->GetTextInHBufL();
+        TBool textChanged = EFalse;
+
+        if ((!iText && newText) || (iText && !newText) ||
+                (iText && newText && (*iText != *newText)))
+        {
+            textChanged = ETrue;
+        }
+
+        delete iText;
+        iText = newText;
+
+        if (textChanged)
+        {
+            ReportEventL(MCoeControlObserver::EEventStateChanged);
+        }
+    }
+    else if (aEventType == MCoeControlObserver::EEventRequestFocus)
+    {
+        static_cast<MCoeControlObserver*>(iForm)->HandleControlEventL(this, aEventType);
+    }
+}
+
+void CMIDTextFieldItem::DoHandleEdwinEventL()
+{
+    TPoint point;
+    TBool success = GetCursorPosL(point);
+    if (success && iForm)
+    {
+        if (point.iY < 0)
+        {
+            iForm->ScrollRelative(-(point.iY - 10));
+            iForm->UpdatePhysics();
+        }
+        else if (point.iY >= iForm->Height())
+        {
+            iForm->ScrollRelative((iForm->Height() - point.iY) - 10);
+            iForm->UpdatePhysics();
+        }
+    }
+}
+
+TBool CMIDTextFieldItem::GetCursorPosL(TPoint& aPoint)
+{
+    TInt pos = iTextField->CursorPos();
+    CTextView* tv = iTextField->TextView();
+    return tv->DocPosToXyPosL(pos, aPoint);
+}
+
+void CMIDTextFieldItem::SetLabelL(const TDesC& aLabel)
+{
+    CMIDControlItem::SetLabelL(aLabel);
+}
+
+void CMIDTextFieldItem::Dispose()
+{
+    delete this;
+}
+
+void CMIDTextFieldItem::SetLayoutL(TLayout aLayout)
+{
+    CMIDItem::SetLayoutL(aLayout);
+}
+
+void CMIDTextFieldItem::AddCommandL(MMIDCommand* aCommand)
+{
+    CMIDItem::AddCommandL(aCommand);
+}
+
+void CMIDTextFieldItem::RemoveCommand(MMIDCommand* aCommand)
+{
+    CMIDItem::RemoveCommand(aCommand);
+}
+
+void CMIDTextFieldItem::SetDefaultCommand(MMIDCommand* aCommand)
+{
+    CMIDItem::SetDefaultCommand(aCommand);
+}
+
+TSize CMIDTextFieldItem::PreferredSize() const
+{
+    return iPreferredSize;
+}
+
+TSize CMIDTextFieldItem::MinimumSize() const
+{
+    CCoeControl* control = const_cast<CMIDTextFieldItem*>(this);
+    return control->MinimumSize();
+}
+
+TCoeInputCapabilities CMIDTextFieldItem::InputCapabilities() const
+{
+    TCoeInputCapabilities inputCapabilities(TCoeInputCapabilities::ENone, NULL,
+                                            const_cast<CMIDTextFieldItem*>(this));
+    inputCapabilities.MergeWith(iTextField->InputCapabilities());
+    return inputCapabilities;
+}
+
+void CMIDTextFieldItem::RestoreInnerFocus()
+{
+    // Restore active textfield line (with cursor) on screen by resetting
+    // the text selection and the cursor position
+    if (iTextField)
+    {
+        TCursorSelection sel = iTextField->Selection();
+        // Exceptions can be ignored, because failing of SetSelectionL may cause
+        // only a cosmetic visual problems
+        TRAP_IGNORE(iTextField->SetSelectionL(sel.iCursorPos, sel.iAnchorPos));
+    }
+}
+
+void CMIDTextFieldItem::SetCursorVisibility(TBool aVisible)
+{
+    if (IsFocused())
+    {
+        TCursor::TVisibility textCursor =
+            aVisible ? TCursor::EFCursorFlashing : TCursor::EFCursorInvisible;
+
+        // lineCursor is not used in TextField, so it is set to TCursor::EFCursorInvisible always
+        TRAP_IGNORE(iTextField->TextView()->SetCursorVisibilityL(TCursor::EFCursorInvisible,
+                    textCursor));
+    }
+}
+
+void CMIDTextFieldItem::UpdateTextColorsL()
+{
+    if (iTextField)
+    {
+        if (iHighlighted)
+        {
+            // Text colour from skin - highlighted
+            iTextField->SetTextSkinColorIdL(EAknsCIQsnTextColorsCG8);
+        }
+        else
+        {
+            // Text colour from skin - unfocused
+            iTextField->SetTextSkinColorIdL(EAknsCIQsnTextColorsCG6);
+        }
+    }
+}