diff -r f5050f1da672 -r 04becd199f91 javauis/lcdui_akn/lcdui/src/CMIDForm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javauis/lcdui_akn/lcdui/src/CMIDForm.cpp Tue Apr 27 16:30:29 2010 +0300 @@ -0,0 +1,4477 @@ +/* +* Copyright (c) 2003-2008 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: Implements the Form LCDUI component. +* +*/ + + +//RD_SCALABLE_UI definition +#include +// using CEikScrollBarFrame API for iSBFrame +#include + +// API used for retrieving layout for initial alignment (in ConstructL) +#include +// constants used in retrieving layout for initial alignment (in ConstructL) +#include + +// control context that provides a layout background +#include +// highlight background +#include +// API for text formatting used in DrawNoDataStringL function +#include + +#include "CMIDForm.h" +#include "CMIDFormRow.h" +// API for iDisplayable +#include "CMIDDisplayable.h" +#include "CMIDItem.h" +#include "CMIDControlItem.h" +#include "CMIDGaugeItem.h" +#include "CMIDImageItem.h" +#include "CMIDStringItem.h" +// CMIDLabelContainerItem API used to obtain CMIDStringItem item +#include "CMIDLabelContainerItem.h" +#include "CMIDTextFieldItem.h" +#include "CMIDChoiceGroupItem.h" +#include "CMIDSpacer.h" +#include "CMIDCustomItem.h" +#include "CMIDDateFieldItem.h" +#include "CMIDComponentFactory.h" +#include "CMIDUtils.h" +#include "CMIDItemLabel.h" +#include "CMIDTicker.h" +#include "CMIDFont.h" +#include "CMIDPopupNoteController.h" +// Kinetic scrolling +#include "CMIDFormPhysics.h" + +#include +// LAF +#include +// LAF +#include + +// Api for skin layout +#include +using namespace SkinLayout; + +#include + +#undef TRAP_INSTRUMENTATION_LEAVE +#define TRAP_INSTRUMENTATION_LEAVE(aResult) DEBUG_INT2("In CMIDForm.cpp, trapped method was called at line %D and got exception %D", __LINE__, aResult); + +#ifdef RD_SCALABLE_UI_V2 +/** Dividers used when the the scroll amount is calculated*/ +const TInt KScrollAmountDivider = 4; +const TInt KScrollAmountDividerForThumbDrag = 10; + +/** Interval for scrolling when stylus is dragged outside the form and held down */ +const TInt KScrollInterval = 100000; // 0,1 sec +#endif //RD_SCALABLE_UI_V2 + +/** The minimum amount to scroll */ +const TInt KMinScrollAmount = 40; + +/** The timeout in microseconds for visual updates. When items are added or removed +or modified, the form is not redrawn immediately but only after this timeout. This way +if more items are being added or removed or changed we reduce the number of redraws. +*/ +const TInt KLayoutTimeout = 60000; //60 msecs + +/** If layout has been requested for a period longer than the cap then perform layout +even if the midlet is still performing form insert/delete operations. */ +const TInt KLayoutCap = 1000000; // 1 sec + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// MMIDForm interface +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Add items list. Note: this is no longer called by framework!! + */ +void CMIDForm::SetAllItemsL(const RArray& aItems) +{ + DEBUG("CMIDForm::SetAllItemsL"); + + ASSERT(iItems.Count() == 0); + // + TInt count = aItems.Count(); + for (TInt i=0; iHideMenuIfVisible(); + + if (ci.IsSelectable()) + { + SetFocusedItem(aIndex); + } + else + { + SetFocusedItem(KErrNotFound); + } + } + + RequestLayoutL(); +} + +/** + * Inserts a new Item. + */ +void CMIDForm::InsertItemL(MMIDItem& aItem,TInt aIndex) +{ + DEBUG("CMIDForm::InsertItemL"); + + User::LeaveIfError(iItems.Insert(&aItem,aIndex)); + CMIDControlItem& ci = ControlItem(aIndex); + + AddItemToFormL(ci); + ci.MakeVisible(ETrue); + // if insert item then index of pointed control is increase + if (aIndex <= iIndexPointedControl) + { + iIndexPointedControl++; + } + + // If there isn't any focused item yet, we have to set focus + // to first item which is focusable. Once the item has focus, + // we can skip this step. + if (iFocused == KErrNotFound) + { + SetFocusedItem(ci.IsSelectable() ? aIndex : KErrNotFound); + } + + RequestLayoutL(); +} + +void CMIDForm::DeleteItemL(TInt aIndex) +{ + DEBUG("CMIDForm::DeleteItemL"); + + TBool updateFocused = EFalse; + // if delete item then index of pointed control is decrease + if (iIndexPointedControl > aIndex) + { + iIndexPointedControl--; + } + else + // if delete item is same as index of pointed control then index is set to not active + { + if (iIndexPointedControl == aIndex) + { + iIndexPointedControl = -1; + } + } + if (iFocused > aIndex) + { + iFocused--; + } + else if (iFocused == aIndex) + { + iDisplayable.MenuHandler()->HideMenuIfVisible(); + ReplaceFocusedItem(); + updateFocused = ETrue; + } + + CMIDControlItem& ci = ControlItem(aIndex); + RemoveItemFromForm(ci); + if (&ci == iPointedControl) + { + iPointedControl = NULL; + } + + iItems.Remove(aIndex); + + if (iFocused != KErrNotFound && updateFocused) + { + iFocused--; + } + + RequestLayoutL(); +} + +void CMIDForm::DeleteAllItemsL() +{ + DEBUG("CMIDForm::DeleteAllItemsL"); + iDisplayable.MenuHandler()->HideMenuIfVisible(); + SetFocusedItem(KErrNotFound); + + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CMIDControlItem& ci = ControlItem(i); + RemoveItemFromForm(ci); + } + iItems.Reset(); + + iScroll = 0; + // if delete all then index of pointed control is set to not active + iIndexPointedControl = -1; + + iPointedControl = NULL; + RequestLayoutL(); +} + +/** + * Called when the item has changed its appearance in some way. + */ +void CMIDForm::RefreshItemL(TInt aIndex) +{ + DEBUG("CMIDForm::RefreshItemL"); + + if (aIndex != KErrNotFound) + { + RequestLayoutL(); + } +} + +/** + * Returns whether the item is actually visible to the user. Must therefore take into + * account the visibility of the entire form, and whether the specific item is scrolled + * in/out of the viewable area. + */ +TBool CMIDForm::IsItemVisible(TInt aIndex) +{ + DEBUG("CMIDForm::IsItemVisible"); + + CMIDControlItem& control = ControlItem(aIndex); + + if ((control.Position().iY > Height()) || + ((control.Position().iY + control.Size().iHeight) < 0)) + { + return EFalse; + } + return ETrue; +} + +/** + * Called prior to the Form itself being made the current Displayable. + * It provides an opportunity to prepare the form such that this item will + * be visible and focused when the form is eventually made current + */ +void CMIDForm::SetCurrentItemL(TInt aIndex) +{ + DEBUG("CMIDForm::SetCurrentItemL"); + + // If it is pending request for layout, than layout now. + if (LayoutPending()) + { + LayoutAndDrawL(); + } + + // Set focus of item to aIndex and scroll to it. + SetFocusedItem(aIndex); + +} + + +// +// End of MMIDForm interface +// + +TInt CMIDForm::CountComponentControls() const +{ + return iRows.Count(); +} + +CCoeControl* CMIDForm::ComponentControl(TInt aIndex) const +{ + return iRows[aIndex]; +} + +TKeyResponse CMIDForm::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType) +{ + DEBUG("< CMIDForm::OfferKeyEventL"); + + TBool isArrowKey = (aKeyEvent.iCode == EKeyDownArrow || aKeyEvent.iCode == EKeyUpArrow || + aKeyEvent.iCode == EKeyLeftArrow || aKeyEvent.iCode == EKeyRightArrow); + + if (iFocused != KErrNotFound) + { + CMIDControlItem& controlItem = ControlItem(iFocused); + TRect controlRect = GetControlRect(iFocused); + TBool visible = RectPartiallyVisible(controlRect); + + // arrow key events are not sent to the hidden focused item + if ((visible || !isArrowKey) && + controlItem.OfferKeyEventL(aKeyEvent,aType) == EKeyWasConsumed) + { + DEBUG("CMIDForm::OfferKeyEventL - out after item consumed key"); + +#ifdef RD_SCALABLE_UI_V2 + // If focused TextField or DateField item is at least partly out of + // Form viewport, then key event (excluding arrow keys and soft keys) + // scrolls the item completely visible. This situation + // is possible when scrolling has been done with scrollbar. + + // Soft keys and arrow keys are ignored. Filtering must be done comparing + // scan codes because the aEventType.iCode is not available for all key presses. + TBool ignoreKey = (aKeyEvent.iScanCode == EStdKeyRightArrow || + aKeyEvent.iScanCode == EStdKeyUpArrow || + aKeyEvent.iScanCode == EStdKeyDownArrow || + aKeyEvent.iScanCode == EStdKeyLeftArrow || + aKeyEvent.iScanCode == EStdKeyDevice0 || + aKeyEvent.iScanCode == EStdKeyDevice1 || + aKeyEvent.iScanCode == EStdKeyDevice3); + + if ((controlItem.Type() == MMIDComponent::ETextField || + controlItem.Type() == MMIDComponent::EDateField) && + !RectFullyVisible(controlRect) && !ignoreKey) + { + // Scroll up if controlItem is above the Form viewport. + // Otherwise scroll down. + if (controlRect.iTl.iY < 0) + { + ScrollToFocused(EUp, ETrue); + } + else + { + ScrollToFocused(EDown, ETrue); + } + + } +#endif // RD_SCALABLE_UI_V2 + + return EKeyWasConsumed; + } + } + + DEBUG("CMIDForm::OfferKeyEventL - got chance to scroll"); + + if ((aType == EEventKey) && isArrowKey && (iItems.Count() > 0)) + { + DEBUG("CMIDForm::OfferKeyEventL - about to call Traverse"); + + Traverse(ConvertKeyToDirection(aKeyEvent.iCode)); + + DEBUG("CMIDForm::OfferKeyEventL - out after Traverse"); + return EKeyWasConsumed; + } + + // msk: this is needed if MSK is not enabled + if ((aType == EEventKey) && + ((aKeyEvent.iScanCode == EStdKeyDevice3) || (aKeyEvent.iCode == EKeyEnter)) + ) + { + if (!DoDefaultCommand()) + { + iDisplayable.ShowOkOptionsMenuL(); + } + + DEBUG("CMIDForm::OfferKeyEventL - out after default command or ok menu"); + return EKeyWasConsumed; + } + // end msk + DEBUG("> CMIDForm::OfferKeyEventL"); + return EKeyWasNotConsumed; +} + +/** Handle a focus change. Make sure there is no layout pending and if there is +one then do it, see TrappedLayoutAndDraw(). This is because when there is a layout +pending we are in a temporary inconsistent phase with no rows and the rows are the +intermediary to the items via the CountComponentControl() and ComponentControl() calls. +Then pass on focus to our children according to the value stored in iFocused. Finally +update the scroll bars if gaining focus and draw. */ +void CMIDForm::FocusChanged(TDrawNow /*aDrawNow*/) +{ + DEBUG("CMIDForm::FocusChanged"); + + TrappedLayoutAndDraw(ETrue); + + TInt i = -1; + TInt itemCount = iItems.Count(); + while (++i < itemCount) + { + if (i == iFocused) + { + ControlItem(iFocused).SetFocus(IsFocused()); + } + else + { + ControlItem(i).SetFocus(EFalse); + } + } + + // make sure scrollbar is up to date when we get focus + if (IsFocused()) + { + UpdateScrollBar(); + } + + DrawNow(); +} + +void CMIDForm::SizeChanged() +{ + DEBUG("CMIDForm::SizeChanged"); + + iBackgroundControlContext->SetParentPos(PositionRelativeToScreen()) ; + iBackgroundControlContext->SetRect(Rect()) ; +} + +/** Before calling CCoeControl::InputCapabilities(), make sure there is +no outstanding layout. This is because if there is one outstanding, we have +no rows and hence ComponentControls() returns zero. This means that, for example +if there are any editors in the items, CCoeControl::InputCapabilities() will not +reach them and the FEP will not be initialised. @see TrappedLayoutAndDraw(), +CCoeControl::InputCapabilities(), CAknFepManager::HandleChangeInFocusL() and +CCoeAppUi::InputCapabilities() */ +TCoeInputCapabilities CMIDForm::InputCapabilities() const +{ + if (iFocused != KErrNotFound) + { + return ControlItem(iFocused).InputCapabilities(); + } + return CCoeControl::InputCapabilities(); //TCoeInputCapabilities::ENone +} + +TTypeUid::Ptr CMIDForm::MopSupplyObject(TTypeUid aId) +{ + if (aId.iUid == MAknsControlContext::ETypeId && iBackgroundControlContext) + { + return MAknsControlContext::SupplyMopObject(aId, iBackgroundControlContext); + } + + CMIDMenuHandler* menuHandler = iDisplayable.MenuHandler(); + return SupplyMopObject(aId, menuHandler->Cba(), menuHandler->MenuBar()); +} + + +#ifdef RD_SCALABLE_UI_V2 +void CMIDForm::HandlePointerEventL(const TPointerEvent& aPointerEvent) +{ + if (!AknLayoutUtils::PenEnabled()) + { + return; + } + + if (iPhysics) + { + HandlePhysicsPointerEventL(aPointerEvent); + return; + } + + switch (aPointerEvent.iType) + { + case TPointerEvent::EButton1Down: + { + DEBUG_INT3("CMIDForm::HandlePointerEventL(): event=%D, x,y=%D,%D", + aPointerEvent.iType, aPointerEvent.iPosition.iX, aPointerEvent.iPosition.iY); + iLastValidPointerEvent = aPointerEvent; + + CMIDControlItem* ci = ControlItemAtPoint(aPointerEvent.iPosition); + // stored on ButtonDown value + + if (ci && ci->IsSelectable()) + { + TInt itemIndex = ItemIndex(*ci); + iIndexPointedControl = itemIndex; + iLastValidPointedItemPosition = ci->Position(); + TBool changeFocus = (itemIndex != iFocused); + + if (changeFocus) + { + // changing the focused item by dragging must start from unfocused item + iCanDragFocus = ETrue; + iFocusChangingWithPen = ETrue; + // give basic tactile feedback on button down when move focus +#ifdef RD_TACTILE_FEEDBACK + DoFeedbackOnFocusChange(*ci); +#endif // RD_TACTILE_FEEDBACK + SetFocusedItem(itemIndex, ENone, EFalse); + } + + TInt scroll = ScrollDistanceToTappedItem(ci); + + iScrollOnPointerDown = (scroll != 0); + + CCoeControl::HandlePointerEventL(aPointerEvent); + + if (iScrollOnPointerDown) + { + ScrollRelative(scroll); + ControlItem(iFocused).NotifyScrollingCompleted(); + } + else if (changeFocus) + { + HandleItemVisibilityChange(); + DrawNow(); + } + else + { + iCanDragFocus = ETrue; + } + } + else // pointer down did not happen on any control item, allow starting drag + { + iCanDragFocus = ETrue; + } + + break; + } + + case TPointerEvent::EButton1Up: + { + if (iIndexPointedControl>-1 && &ControlItem(iIndexPointedControl) == ControlItemAtPoint(aPointerEvent.iPosition)) + // When this is item where TPointerEvent::EButton1Down was happened, + // then event is forwarded to item. It is its responsibility check that + // up event occured if it have some actions on up event. + { + DEBUG_INT3("CMIDForm::HandlePointerEventL(): event=%D, x,y=%D,%D", + aPointerEvent.iType, aPointerEvent.iPosition.iX, aPointerEvent.iPosition.iY); + + HandleDragEventL(aPointerEvent, ETrue); + } + else + { + // When this is not item where TPointerEvent::EButton1Down was happened, + // then e`vent is forwarded to item where originally. It is its responsibility + // check that up event occured if it have some actions on up event. + // Setting default values of related variables. + + if (iIndexPointedControl>-1 && &ControlItem(iIndexPointedControl)) + { + TBool bbutton = EFalse; + + if (IsStringItem(ControlItem(iIndexPointedControl))) + { + // get appearance from StringItem + CMIDStringItem *strItem = static_cast< CMIDStringItem* >(&ControlItem(iIndexPointedControl)); + MMIDItem::TAppearance appearance = strItem->Appearance(); + bbutton = (appearance == MMIDItem::EButton || appearance == MMIDItem::EHyperLink); + } + + if (ControlItem(iIndexPointedControl).Type() != MMIDComponent::EChoiceGroup && !bbutton) + { + TPointerEvent pointerevent; + pointerevent.iType = TPointerEvent::EButton1Up; + pointerevent.iModifiers = aPointerEvent.iModifiers; + pointerevent.iPosition = iLastValidPointerEvent.iPosition + + ControlItem(iIndexPointedControl).Position() - iLastValidPointedItemPosition; + pointerevent.iParentPosition = iLastValidPointerEvent.iParentPosition; + + ControlItem(iIndexPointedControl).HandlePointerEventL(pointerevent); + + DEBUG_INT3("CMIDForm::HandlePointerEventL(): event=%D, x,y=%D,%D", + pointerevent.iType, pointerevent.iPosition.iX, pointerevent.iPosition.iY); + + } + + // forward event for aPointerEvent + CCoeControl::HandlePointerEventL(aPointerEvent); + } + } + + // If it is pending request for layout, than layout form now and refresh. + if (LayoutPending()) + { + LayoutFormL(); + DrawDeferred(); + } + + iCanDragFocus = EFalse; + iFocusChangingWithPen = EFalse; + iScrollOnPointerDown = EFalse; + iIndexPointedControl = -1; + + break; + } + + case TPointerEvent::EDrag: + // for dragging + { + if (iIndexPointedControl>-1 && &ControlItem(iIndexPointedControl) == ControlItemAtPoint(aPointerEvent.iPosition)) + { + // When this is item where TPointerEvent::EButton1Down was happened, + // then it is updated iLastValidPointerEvent. + iLastValidPointerEvent = aPointerEvent; + // If it is pending request for layout, than layout Form now and refresh + // elsewhere call HandleDragEventL and forward EDrag events to event. + if (LayoutPending()) + { + LayoutFormL(); + DrawDeferred(); + } + else + { + HandleDragEventL(aPointerEvent, ETrue); + } + DEBUG_INT3("CMIDForm::HandlePointerEventL(): event=%D, x,y=%D,%D", + aPointerEvent.iType, aPointerEvent.iPosition.iX, + aPointerEvent.iPosition.iY); + } + else + { + // When this is not item where TPointerEvent::EButton1Down was happened, + // then we do only dragging. There it is not send event to item. + HandleDragEventL(aPointerEvent, EFalse); + } + break; + } + + case TPointerEvent::EButtonRepeat: + // For button repeating + { + DEBUG_INT3("CMIDForm::HandlePointerEventL(): event=%D, x,y=%D,%D", + aPointerEvent.iType, aPointerEvent.iPosition.iX, + aPointerEvent.iPosition.iY); + + RequestPointerRepeat(aPointerEvent); + + if (iCanDragFocus) + { + if (aPointerEvent.iPosition.iY < 0 && CanScrollUp()) + { + TimedScroll(EUp); + } + else if (aPointerEvent.iPosition.iY > Height() && CanScrollDown()) + { + TimedScroll(EDown); + } + } + else + { + CCoeControl::HandlePointerEventL(aPointerEvent); + } + + break; + } + default: + // By defalt only implementation of base class. + { + CCoeControl::HandlePointerEventL(aPointerEvent); + DEBUG_INT3("CMIDForm::HandlePointerEventL(): event=%D, x,y=%D,%D", + aPointerEvent.iType, aPointerEvent.iPosition.iX, + aPointerEvent.iPosition.iY); + break; + } + } +} + + +void CMIDForm::HandleDragEventL(const TPointerEvent& aPointerEvent, TBool aForItem) +{ + RequestPointerRepeat(aPointerEvent); + + if (iCanDragFocus) + { + // formRect covers the whole vertical form area, but excludes + // the left and right margins and the scroll bar area + TRect formRect(TPoint(iFormRect.Rect().iTl.iX, 0), + TPoint(iFormRect.Rect().iBr.iX, Height())); + + // Try to find the new focused item, if needed + if (formRect.Contains(aPointerEvent.iPosition) && + !FocusedItemContainsPoint(aPointerEvent.iPosition)) + { + CMIDControlItem* ci = ControlItemAtPoint(aPointerEvent.iPosition); + + if (ci && ci->IsSelectable()) + { + TInt itemIndex = ItemIndex(*ci); + + // Find the direction to which the focus is dragged + TDirection direction = ENone; + if (iFocused != KErrNotFound) + { + TRect rect = ControlItem(iFocused).Rect(); + + if (aPointerEvent.iPosition.iY < rect.iTl.iY) + { + direction = EUp; + } + else if (aPointerEvent.iPosition.iY >= rect.iBr.iY) + { + direction = EDown; + } + else // focus is moved horizontally + { + direction = (itemIndex < iFocused) ? ELeft : ERight; + } + } + +#ifdef RD_TACTILE_FEEDBACK + if (iFocused != itemIndex) + { + iFeedback->InstantFeedback(ETouchFeedbackSensitive); + } +#endif + SetFocusedItem(itemIndex, direction, EFalse); + HandleItemVisibilityChange(); + DrawNow(); + } + } + + if (FocusedItemContainsPoint(aPointerEvent.iPosition)) + { + // forward EDrag events to the focused so that the focus can + // move inside the item + // (only when this is item where TPointerEvent::EButton1Down was occured) + + CMIDControlItem* focusedControl = ControlItemAtPoint(aPointerEvent.iPosition); + + if (aForItem || (iIndexPointedControl > -1 && &ControlItem(iIndexPointedControl) && + (IsChoiceGroup(ControlItem(iIndexPointedControl)) || IsCustomItem(ControlItem(iIndexPointedControl)))) || + focusedControl && IsChoiceGroup(*focusedControl) || + focusedControl && IsCustomItem(*focusedControl)) + { + ControlItem(iFocused).HandlePointerEventL(aPointerEvent); + } + else + { + if (iIndexPointedControl > -1 && &ControlItem(iIndexPointedControl)) + { + TBool bbutton = EFalse; + + if (IsStringItem(ControlItem(iIndexPointedControl))) + { + CMIDStringItem *strItem = static_cast< CMIDStringItem* >(&ControlItem(iIndexPointedControl)); + MMIDItem::TAppearance appearance = strItem->Appearance(); + bbutton = (appearance == MMIDItem::EButton || appearance == MMIDItem::EHyperLink); + } + + //forward event (only for changing focus) for Button & Hyperlink + if (bbutton && &ControlItem(iIndexPointedControl)) + { + ControlItem(iIndexPointedControl).HandlePointerEventL(aPointerEvent); + } + else + { + iDisplayable.TryDetectLongTapL(aPointerEvent); + } + } + else + { + iDisplayable.TryDetectLongTapL(aPointerEvent); + } + } + } + else + { + // make sure that long tap animation is canceled in this case + iDisplayable.TryDetectLongTapL(aPointerEvent); + } + } + else // cannot drag focus + { + CCoeControl::HandlePointerEventL(aPointerEvent); + } +} + + +void CMIDForm::RequestPointerRepeat(const TPointerEvent& aPointerEvent) +{ + if (aPointerEvent.iPosition.iY < 0 || + aPointerEvent.iPosition.iY > Height()) + { + TRect screen; + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EScreen, screen); + + TRect repeatRect = screen; + if (aPointerEvent.iPosition.iY < 0) + { + repeatRect.iBr.iY = iMidpFormRect.Rect().iTl.iY; + } + else + { + repeatRect.iTl.iY = iMidpFormRect.Rect().iBr.iY; + } + + // repeatRect must be given in propotion to form's window, move it up + repeatRect.Move(0, -iMidpFormRect.Rect().iTl.iY); + Window().RequestPointerRepeatEvent(KScrollInterval, repeatRect); + } +} + + +void CMIDForm::TimedScroll(TDirection aDirection) +{ + ASSERT(aDirection == EDown || aDirection == EUp); + + // First find the row that is the first or last row (depending on + // the scroll direction) that is at least partailly visible on the form. + // Search direction is opposite to aDirection. + TDirection searchDirection = (aDirection == EUp) ? EDown : EUp; + TInt idx = FirstRowOnScreen(searchDirection); + + if (idx == KErrNotFound) + { + return; + } + + TInt scroll = Height() / KScrollAmountDividerForThumbDrag; + + // Find the row that will be visible (at least partially) after + // after the scrolling. + if (aDirection == EUp) + { + while (idx > 0 && (iRows[idx]->Rect().iTl.iY + scroll > 0)) + { + --idx; + } + } + else + { + while ((idx < iRows.Count() - 1) && + (iRows[idx]->Rect().iBr.iY - scroll < Height())) + { + ++idx; + } + } + + TBool rowFocused = EFalse; + if (iFocused != KErrNotFound) + { + rowFocused = GetControlRect(iFocused).Intersects(iRows[idx]->Rect()); + } + + // If the focus is not yet on the row, try to set one of its items focused + if (!rowFocused) + { + for (TInt i = 0; i < iRows[idx]->NumItems(); ++i) + { + CMIDControlItem* item = iRows[idx]->Item(i); + if (item->IsSelectable()) + { + SetFocusedItem(ItemIndex(*item), aDirection, EFalse); + } + } + } + +#ifdef RD_TACTILE_FEEDBACK + iFeedback->InstantFeedback(ETouchFeedbackSensitive); +#endif + + ScrollRelative((aDirection == EUp) ? scroll : -scroll); +} + + +TBool CMIDForm::CanScrollUp() const +{ + return (iScroll < 0); +} + + +TBool CMIDForm::CanScrollDown() const +{ + return ((iFormHeight - iScroll) < iTotalFormHeight); +} + + +TBool CMIDForm::FocusedItemContainsPoint(const TPoint& aPoint) const +{ + if (iFocused != KErrNotFound) + { + if (ItemIsUnconstrained(iFocused)) + { + return StringItemContainsPoint( + static_cast(&ControlItem(iFocused)), aPoint); + } + else + { + return ControlItem(iFocused).Rect().Contains(aPoint); + } + } + else + { + return EFalse; + } +} + +CMIDControlItem* CMIDForm::ControlItemAtPoint(const TPoint& aPoint) const +{ + TInt idx = RowAtPoint(aPoint); + + if (idx != KErrNotFound) + { + for (TInt i = 0; i < iRows[idx]->NumItems(); ++i) + { + CMIDControlItem* ci = iRows[idx]->Item(i); + if (ci && ci->Rect().Contains(aPoint)) + { + return ci; + } + } + } + + return NULL; +} + + +TInt CMIDForm::RowAtPoint(const TPoint& aPoint) const +{ + TInt rowCount = iRows.Count(); + // loop until the first row fully below the visible form area + for (TInt i = 0; i < rowCount && (iRows[i]->Position().iY < iFormHeight); ++i) + { + if (iRows[i]->Rect().Contains(aPoint)) + { + return i; + } + } + return KErrNotFound; +} + +#endif // #ifdef RD_SCALABLE_UI_V2 + + +void CMIDForm::SetFocusedItemIfNone(CMIDItem* aItem) +{ + if (iFocused == KErrNotFound) + { + ASSERT(aItem); + TInt index = Index(aItem); + + if (index >= 0 && index < iItems.Count()) + { + CMIDControlItem& ci = ControlItem(iItems[index]); + if (ci.IsSelectable()) + { + SetFocusedItem(index); + ScrollToFocused(); + } + } + } +} + +/** This method is called when an item loses focusability, +for example because it loses all its commands. If this item +is focused, call ReplaceFocusedItem() to find another focusable +item. @see CMIDItem::RemoveCommand() and ReplaceFocusedItem*/ +void CMIDForm::RemoveFocusIfFocused(CMIDItem* aItem) +{ + ASSERT(aItem); + TInt index = Index(aItem); + if (iFocused == index) + { + // Hide menu because item has lost all item commands and + // another item may have become focused behind the menu. + iDisplayable.MenuHandler()->HideMenuIfVisible(); + ReplaceFocusedItem(); + } +} + +/** + * Traverse is a method to go thorough the form; it includes moving focus from + * item to another possibly scrolling the form. Note that sometimes traversal + * does not move focus, because an item is larger that the screen and thus form + * is scrolled little by little so that the user can see the whole content. On + * other cases, traversing means only moving focus from item to another as both + * items are visible on the screen. + * + * @param aDirection defines the direction of the traversing (up, down, left, right). + **/ +void CMIDForm::Traverse(TDirection aDirection) +{ + DEBUG("< CMIDForm::Traverse"); + DEBUG_INT("< CMIDForm::Traverse Direction is %D", aDirection); + TBool horizontalScroll = EFalse; + + if (LayoutPending()) + { + DEBUG("CMIDForm::Traverse - calling layout"); + // lets do the layout now so we can always assume that we are layed out before doing this stuff + TrappedLayoutAndDraw(); + } + +#ifdef RD_SCALABLE_UI_V2 + TBool focusHidden = EFalse; + // If the focused item is currently fully outside the visible form area, + // it means that form has been scrolled by moving the scrollbar with pen. In this case + // set iFocused (temporarily) to KErrNone in order that scrolling with arrow keys works ok. + if (iFocused != KErrNotFound && !RectPartiallyVisible(GetControlRect(iFocused))) + { + focusHidden = ETrue; + SetFocusedItem(KErrNotFound, ENone, EFalse); + } +#endif + + if (aDirection == ELeft || aDirection == ERight) + { + DEBUG("CMIDForm::Traverse - move focus horizontally"); + if (!TryMovingFocusHorizontally(aDirection)) + { + if (iInitialAlignment == MMIDItem::ERight) + {//RIGHT TO LEFT + TryMovingFocusVertically(aDirection == ELeft ? EDown : EUp, EFalse); + } + else + {//LEFT TO RIGHT + TryMovingFocusVertically(aDirection == ELeft ? EUp : EDown, EFalse); + } + } + else + { + horizontalScroll = ETrue; + } + } + else if (aDirection == EUp || aDirection == EDown) + { + DEBUG("CMIDForm::Traverse - move focus vertically"); + TryMovingFocusVertically(aDirection, ETrue); + } + + // if there is still a focused item, make sure it is still on the screen. + // if it isn't remove focus as we've already tried out best to have a + // focussed item on screen + if (iFocused != KErrNotFound) + { + TRect rect = GetControlRect(iFocused); + if (!RectPartiallyVisible(rect)) + { + SetFocusedItem(KErrNotFound, ENone); + } + } +#ifdef RD_SCALABLE_UI_V2 + else if (focusHidden) + { + // Try to bring the focus back to visible area by setting the last focusable + // item in the scroll direction focused. + TDirection searchDirection = (aDirection == EDown || aDirection == ERight) ? + EUp : EDown; + TInt newFocusIndex = IndexOfFirstFocusableItemOnScreen(searchDirection); + if (newFocusIndex != KErrNotFound) + { + // Pass searchDirection also to SetFocusedItem(). This is needed at least + // for choice group and custom item cases, so that they can set there + // internal focus to the last element in the actual scroll direction. + SetFocusedItem(newFocusIndex, searchDirection, EFalse); + DrawDeferred(); + } + } +#endif + + RawScrollFinalize(horizontalScroll); + DEBUG("> CMIDForm::Traverse"); +} + + +// ----------------------------------------------------------------------------- +// Form scrolling methods +// ----------------------------------------------------------------------------- +/** + * Scroll to the focused control if there is one and update form visibility (draw) and scroll bar. + * @see ScrollToRect() and SetRowExtentsWithRespectToScrolledLocation(). + **/ +void CMIDForm::ScrollToFocused(TDirection aDirection /*=EDown*/, TBool aReset /*= ETrue*/) +{ + DEBUG("< CMIDForm::ScrollToFocused"); + if (!LayoutPending()) + { + if (iFocused != KErrNotFound) + { + TRect rect = GetControlRect(iFocused); + DEBUG_INT4("CMIDForm::ScrollToFocused - rect is %D %D %D %D", + rect.iTl.iX, rect.iTl.iY, rect.iBr.iX, rect.iBr.iY); + RawScrollToRect(rect, aDirection, aReset); + } + + RawScrollFinalize(ETrue); + + if (iFocused != KErrNotFound) + { + ControlItem(iFocused).NotifyScrollingCompleted(); + } + } + + UpdatePhysics(); + + DEBUG("> CMIDForm::ScrollToFocused"); +} + +TInt CMIDForm::ScrollDistanceToTappedItem(CMIDControlItem* aPointedControl) +{ + TRect focusedRect = aPointedControl->Rect(); + TInt scroll = 0; + + // Calculate how much we need to scroll to make the tapped item fully/more visible + if (!RectFullyVisible(focusedRect)) + { + TInt topMargin = iFormRect.Rect().iTl.iY - iMidpFormRect.Rect().iTl.iY; + TInt bottomMargin = iMidpFormRect.Rect().iBr.iY - iFormRect.Rect().iBr.iY; + + if (focusedRect.Height() <= Height()) + { + scroll = (focusedRect.iBr.iY > Height()) ? + Height() - focusedRect.iBr.iY : -focusedRect.iTl.iY; + } + // Scroll long item so that it fills the whole form height. + // No scroll in case item already fills the whole screen or if there would + // be just few pixels (~margins) to scroll. + else if ((focusedRect.iTl.iY > 0 && focusedRect.iTl.iY > topMargin) || + (focusedRect.iBr.iY < Height() && + Height() - focusedRect.iBr.iY > bottomMargin)) + { + scroll = (focusedRect.iTl.iY > 0) ? + -focusedRect.iTl.iY : -(focusedRect.iBr.iY - Height()); + } + } + return scroll; +} + +/** + * Scrolls to the rect corresponding to the row specified by aIdx using + * the direction indicated by aDirection. @see ScrollToRect() + **/ +void CMIDForm::RawScrollToRow(TInt aIdx, TDirection aDirection) +{ + DEBUG("< CMIDForm::RawScrollToRow"); + if (aIdx >= 0 && aIdx < iRows.Count()) + { + TRect rect = iRows[aIdx]->Rect(); + DEBUG("CMIDForm::RawScrollToRow - scroll to rect"); + RawScrollToRect(rect, aDirection); + } + DEBUG("> CMIDForm::RawScrollToRow"); +} + +/** */ +void CMIDForm::RawScrollToRect(const TRect& aRect, TDirection aDirection, TBool aReset) +{ + DEBUG("< CMIDForm::RawScrollToRect"); + DEBUG_INT4("CMIDForm::RawScrollToRect - rect is %D %D %D %D", aRect.iTl.iX, aRect.iTl.iY, + aRect.iBr.iX, aRect.iBr.iY); + + if (RectFullyVisible(aRect)) + { + DEBUG("CMIDForm::RawScrollToRect - return rect fully visible"); + TInt maxScrollValue = Height() - HeightOfAllItems(); + // Avoid scrolling out of the form (i.e. after the view port has lower height) + + if (iScroll < maxScrollValue) + { + RawScroll(maxScrollValue); + } + return; + } + + TInt scrollOffset = 0; + if (aRect.Size().iHeight > Height()) + {//rect higher than screen height + if (RectPartiallyVisible(aRect) && !aReset) + {//if already visible, scroll only of KMinScrollAmount (panning) + scrollOffset = (aDirection == EDown) ? -KMinScrollAmount : KMinScrollAmount; + } + else + {//if not visible and scrolling down scroll to top - KMinScrollAmount + // or to bottom + KMinScrollAmount if scrolling up + scrollOffset = (aDirection == EDown) ? (Height() - (aRect.iTl.iY + KMinScrollAmount)) + : -(aRect.iBr.iY - KMinScrollAmount); + } + } + else + {//if it fits on screen make sure its BR is at the bottom if scrolling down + //or its TL is at the top if scrolling up + scrollOffset = (aDirection == EDown) ? (Height() - aRect.iBr.iY) : (-aRect.iTl.iY); + } + + RawScroll(iScroll + scrollOffset); + DEBUG("> CMIDForm::RawScrollToRect"); +} + +void CMIDForm::ScrollRelative(TInt aScroll) +{ + DEBUG("CMIDForm::ScrollRelative"); + RawScrollRelative(aScroll); + RawScrollFinalize(EFalse); +} + +void CMIDForm::RawScrollRelative(TInt aScroll) +{ + TInt scroll = iScroll + aScroll; + RawScroll(scroll); +} + +void CMIDForm::RawScroll(TInt aScroll) +{ + TInt contentHeight = HeightOfAllItems(); + TInt viewPortHeight = Height(); + if (aScroll > 0 || contentHeight <= viewPortHeight) + { // make sure that top does not slide down + aScroll = 0; + } + if (contentHeight > viewPortHeight && ((-aScroll) + viewPortHeight) > contentHeight) + { // scroll only as long as there is content + aScroll = -(contentHeight - viewPortHeight); + } + iHasScrolled = iScroll != aScroll; + iScrollDelta = aScroll - iScroll; + iScroll = aScroll; + + SetRowPositionsWithRespectToScrolledLocation(); +} + +void CMIDForm::SetRowExtentsWithRespectToScrolledLocation() +{ + iTotalFormHeight = 0; + TInt xMargin = iFormRect.Rect().iTl.iX; + TInt yTopMargin = iFormRect.Rect().iTl.iY - iMidpFormRect.Rect().iTl.iY; + TInt yBottomMargin = iMidpFormRect.Rect().iBr.iY - iFormRect.Rect().iBr.iY; + + iTotalFormHeight = yTopMargin; + + TInt rowCount = iRows.Count(); + for (TInt i=0; i < rowCount; i++) + { + // Form left margin is added in the beginning of each row + iRows[i]->SetExtent(TPoint(xMargin, iScroll + iTotalFormHeight), + TSize(Width(), iRows[i]->Size().iHeight)); + iTotalFormHeight += iRows[i]->Size().iHeight; + } + // Form bottom margin is added + iTotalFormHeight += yBottomMargin; +} + +void CMIDForm::SetRowPositionsWithRespectToScrolledLocation() +{ + TInt rowCount = iRows.Count(); + for (TInt i=0; i < rowCount; i++) + { + // Form left margin is added in the beginning of each row + TPoint pos = iRows[i]->Position(); + pos.iY += ScrollDelta(); + iRows[i]->SetPosition(pos); + } +} + +// Finalizes scrolling after any RawScrollXxxx method call, e.g. causes repaint. +// Draw can be prevented e.g. because of the performance reasons. +void CMIDForm::RawScrollFinalize(TBool forseAction, TBool aDraw /*= ETrue*/) +{ + if (iHasScrolled || forseAction) + { + UpdateHighlightBackgroundPosition(); + HandleItemVisibilityChange(); + UpdateScrollBar(); + if (iFocused != KErrNotFound) + { + CMIDControlItem& ci = ControlItem(iFocused); + if (IsTextFieldItem(ci)) + { + if (RectPartiallyVisible(ci.Rect())) + { + ((CMIDTextFieldItem&) ci).SetCursorVisibility(ETrue); + } + else + { + ((CMIDTextFieldItem&) ci).SetCursorVisibility(EFalse); + } + } + if (IsDateField(ci)) + { + // Update right cursor position on Datefiled + ((CMIDDateFieldItem&) ci).CursorUpdate(); + } + } + if (aDraw) + { + DrawDeferred(); + } + iHasScrolled = EFalse; + } +} + +/** Returns the client area */ +TRect CMIDForm::GetClientArea() +{ + TRect scrolledClientArea = TRect(Position(), TSize(Width(), Height())); + return scrolledClientArea; +} + +// returns ETrue if it moved the focus +// returns EFalse otherwise +TBool CMIDForm::TryMovingFocusHorizontally(TDirection aDirection) +{ + ASSERT(aDirection == ELeft || aDirection == ERight); + + TInt idx = NextFocusableHorizontalIdx(aDirection); + if (idx != KErrNotFound) + { + SetFocusedItem(idx, aDirection); + return ETrue; + } + + return EFalse; +} + +/** + Moves the focus vertically. + + We check if there is a focussable item, + which is on a row totally or partially visible and if there is such + an item we then select that row. This is done by calling + NextPartiallyVisibleRowWithFocusableItem(). If no such item was found we pick + the top or bottom (depending on scrolling direction) row that is partially + visible or the first not visible if the last one is completely shown. + This is done by FirstRowWithBottomBelowScreen() and LastRowWithTopAboveScreen(). + + At this point if we have a row, we select a focussable item on it and + scroll to it if aScroll is true. Calling scroll methods doesn't mean there is going to be + scrolling of the form, + if the item is already visible no scrolling is done. We select the + next focussable item according to the value of the parameter aSelectClosest. + If this paramerter is true we call SelectClosestFocussableItem() otherwise + we either select the first or the last selectable item on the row depending + on the direction. + + If we find a control we scroll to it by calling ScrollToFocused() (if aScroll is true) and move + the focus to it by calling SetFocusedItem(). Otherwise we + scroll to the row by calling ScrollToRow() if aScroll is true. + + If we could not find any row with a focusable item on it, we scroll the form + of a fixed quantity, KMinScrollAmount, if aScroll is true. + + We return ETrue if the focus was moved, EFalse otherwise. Note: Currently the + return value of this method is ignored. + + @see NextPartiallyVisibleRowWithFocusableItem(), FirstRowWithBottomBelowScreen(), + LastRowWithTopAboveScreen(), SelectClosestFocussableItem() + ScrollToRow(), SetFocusedItem() and DoScrollRelative(). + Also see ReplaceFocusedItem(), called when the focused item is removed or + loses focusability - in this case no scrolling will occur. + */ +TBool CMIDForm::TryMovingFocusVertically(TDirection aDirection, TBool aSelectClosest, + TBool aScroll /*=ETrue*/) +{ + DEBUG("> CMIDForm::TryMovingFocusVertically"); + ASSERT(aDirection == EUp || aDirection == EDown); + + // is VKB opened + if (iDisplayable.IsVKBOnScreen()) + { + return ETrue; + } + + TInt rowIdx = NextPartiallyVisibleRowWithFocusableItem(aDirection); + + if (rowIdx == KErrNotFound) + { + DEBUG("CMIDForm::TryMovingFocusVertically - no partially visible focusable control found"); + rowIdx = aDirection == EDown ? FirstRowWithBottomBelowScreen() : LastRowWithTopAboveScreen(); + } + + if (rowIdx != KErrNotFound) + { + CMIDControlItem* ci = aSelectClosest ? SelectClosestFocussableItem(rowIdx) : + (aDirection == EDown ? iRows[rowIdx]->FirstFocusableItem() : + iRows[rowIdx]->LastFocusableItem()); + + + if (ci) + { + DEBUG("CMIDForm::TryMovingFocusVertically - got control"); + TInt index = Index(ci); + + if (index == KErrNotFound && IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem* ucsi = static_cast(ci); + CMIDStringItem& si = ucsi->StringItem(); + index = Index(&si); + } + + if (index != KErrNotFound) + { + TRect rect = GetControlRect(index); + + if (aScroll) + { + SetFocusedItem(index, aDirection, ETrue); + } + else if (RectPartiallyVisible(rect)) + { + SetFocusedItem(index, aDirection); + } + else + { + SetFocusedItem(KErrNotFound); + } + + DEBUG("CMIDForm::TryMovingFocusVertically - out after scroll to control"); + return ETrue; + } + } + + DEBUG("CMIDForm::TryMovingFocusVertically - scrolling to row"); + if (aScroll) + { + RawScrollToRow(rowIdx, aDirection); + } + } + else + { + DEBUG("CMIDForm::TryMovingFocusVertically - small default scrolling"); + if (aScroll) + { + RawScrollRelative((aDirection == EDown) ? -KMinScrollAmount : KMinScrollAmount); + } + } + + DEBUG("< CMIDForm::TryMovingFocusVertically"); + return EFalse; +} + +/** Given a row index, selects on this row the focusable control whose center on +the x axis is closes to the center of the currently focused item, if there is one. +If no control is found, returns the first selectable item on the row. */ +CMIDControlItem* CMIDForm::SelectClosestFocussableItem(TInt aRowIndex) const +{ + ASSERT(aRowIndex >= 0 && aRowIndex < iRows.Count()); + TInt selectedIndex = -1; + + if (iFocused != -1) + { + TRect rect = GetControlRect(iFocused); + TInt focusedCenter = rect.iTl.iX + rect.Width()/2; + + TInt diff = iInnerWidth; + + TInt numItemsOnRow = iRows[aRowIndex]->NumItems(); + for (TInt i = 0; i < numItemsOnRow; i++) + { + CMIDControlItem* item = iRows[aRowIndex]->Item(i); + if (item->IsSelectable()) + { + TInt center = item->Position().iX + (item->Size().iWidth / 2); + TInt itemDiff = focusedCenter > center ? (focusedCenter - center) : (center - focusedCenter); + + if (itemDiff < diff) + { + selectedIndex = i; + diff = itemDiff; + } + } + } + } + + return selectedIndex == -1 ? iRows[aRowIndex]->FirstFocusableItem() : + iRows[aRowIndex]->Item(selectedIndex); +} + +TInt CMIDForm::IndexOfFirstFocusableItemOnScreen(TDirection aDirection) +{ + ASSERT(aDirection == EUp || aDirection == EDown); + + TInt startIdx = FirstRowOnScreen(aDirection); + + if (startIdx == KErrNotFound) + { + return KErrNotFound; + } + + CMIDControlItem* ci = NULL; + + if (aDirection == EDown) + { + TInt idx = startIdx - 1; + TInt rowCount = iRows.Count(); + while (++idx < rowCount) + { + CMIDFormRow* row = iRows[idx]; + if (row->Position().iY > Height()) + { + break; + } + ci = row->FirstFocusableItemOnScreen(); + if (ci) + { + break; + } + } + } + else + { + TInt idx = startIdx + 1; + while (--idx >= 0) + { + CMIDFormRow* row = iRows[idx]; + if ((row->Position().iY + row->Size().iHeight) < 0) + { + break; + } + ci = row->FirstFocusableItemOnScreen(); + if (ci) + { + break; + } + } + } + + if (!ci) + { + return KErrNotFound; + } + + TInt index = Index(ci); + if (index == KErrNotFound) + { + if (IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem* ucsi = static_cast(ci); + CMIDStringItem& si = ucsi->StringItem(); + index = Index(&si); + } + } + + return index; +} + +/** Return the rect for a control, taking into account if the control is an +unconstrained string item which has been split over more than one label container +*/ +TRect CMIDForm::GetControlRect(TInt aIdx) const +{ + CMIDControlItem& ci = ControlItem(aIdx); + TRect rect; + + if (ItemIsUnconstrained(aIdx)) + { + rect = GetUnconstrainedStringItemRect(static_cast(&ci)); + } + else + { + rect = ci.Rect(); + } + + return rect; +} + +/** Collate rects for unconstrained string items that have been split across more +than one lable container */ +TRect CMIDForm::GetUnconstrainedStringItemRect(CMIDStringItem* aStringItem) const +{ + TRect rect(0,0,0,0); + TBool found = EFalse; + + TInt rowIdx = FirstRowOfUnconstrainedStringItem(*aStringItem); + TInt rowCount = iRows.Count(); + + // if the first row of unconstrained string item cannot be found + // it means that string item is placed as default item + // so simply return item's rect + if (rowIdx == KErrNotFound) + { + return aStringItem->Rect(); + } + + while (rowIdx != KErrNotFound && rowIdx < rowCount) + { + CMIDFormRow* row = iRows[rowIdx]; + for (TInt idxOnRow = 0; idxOnRow < row->NumItems(); idxOnRow++) + { + CMIDControlItem* ci = row->Item(idxOnRow); + if (ci && IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem* usil = static_cast(ci); + CMIDStringItem* si = &(usil->StringItem()); + + if (si == aStringItem) + { + if (!found) + { + rect.iTl = usil->Rect().iTl; + found = ETrue; + } + rect.iBr = usil->Rect().iBr; + } + else if (found) + { + return rect; + } + } + else if (found) + { + return rect; + } + } + rowIdx++; + } + + return rect; +} + +TBool CMIDForm::RectPartiallyVisible(const TRect& aRect) +{ + return ((aRect.iTl.iY >= 0) && (aRect.iTl.iY < Height())) || + ((aRect.iBr.iY >= 0) && (aRect.iBr.iY < Height())) || + ((aRect.iTl.iY < 0) && (aRect.iBr.iY >= Height())); +} + +TBool CMIDForm::RectFullyVisible(const TRect& aRect) +{ + return (aRect.iTl.iY >= 0) && (aRect.iBr.iY <= Height()); +} + +TInt CMIDForm::FirstRowWithBottomBelowScreen() +{ + TInt idx = -1; + TInt rowWithFocus = iFocused == KErrNotFound ? -1 : Row(ControlItem(iFocused)); + TInt rowCount = iRows.Count(); + while (++idx < rowCount) + { + CMIDFormRow* row = iRows[idx]; + TInt height = row->Size().iHeight; + TInt yPos = row->Position().iY; + if ((height + yPos) > Height() && row->HasNonSpacerItems() && idx != rowWithFocus) + { + return idx; + } + } + + return KErrNotFound; +} + +TInt CMIDForm::LastRowWithTopAboveScreen() +{ + TInt idx = iRows.Count(); + TInt rowWithFocus = iFocused == KErrNotFound ? -1 : Row(ControlItem(iFocused)); + + while (--idx >= 0) + { + CMIDFormRow* row = iRows[idx]; + TInt yPos = row->Position().iY; + if (yPos < 0 && row->HasNonSpacerItems() && idx != rowWithFocus) + { + return idx; + } + } + + return KErrNotFound; +} + +// note: inspite of the name of this method, we only consider rows with +// a focusable item that has not already been scrolled off the screen. +// For Example: +// +// ---- +// | A | This is a +// | | string item +// ---- that is placed +//----------------------- <- top of form +// next to item A. +// This +// string +// item +// is +// quite +// tall +// and +//----------------------- <- botom of form +// fills +// the +// screen +// +// Assuming that A is focusable, and we are scrolling down, and also assuming +// that the string item is not focusable, we do not consider A when looking +// for focusable items, because we are scrolling down and A is already above +// the screen. +TInt CMIDForm::NextPartiallyVisibleRowWithFocusableItem(TDirection aDirection) +{ + ASSERT(aDirection == EUp || aDirection == EDown); + + TInt startIdx = 0; + if (iFocused != KErrNotFound) + { + //If there is layout request pending, the array of rows is probably + //empty. So we need to do layout of form, which fills the array of rows. + //Then row, where current focused item is, will be found. + if (iRows.Count() == 0) + { + TrappedLayoutAndDraw(ETrue); + } + TInt row = Row(ControlItem(iFocused)); + if (row == KErrNotFound) + { + CMIDControlItem& ci = ControlItem(iFocused); + if (IsStringItem(ci)) + { + // find the first row either above or below (depends on direction) + // this unconstrained stringItem + CMIDStringItem& stringItem = + *(static_cast(&ci)); + if (aDirection == EDown) + { + startIdx = FirstRowBelowUnconstrainedStringItem(stringItem); + } + else + { + startIdx = FirstRowAboveUnconstrainedStringItem(stringItem); + } + } + else + { + // shouldn't get here + ASSERT(EFalse); + } + } + else + { + startIdx = row + ((aDirection == EDown) ? 1 : -1); + } + } + else + { + // There is no focused item on Form (iFocused value is KErrNotFound). + // So we need to try to find another Item which could gain focus. + startIdx = FirstRowOnScreen(aDirection); + } + + if (startIdx == KErrNotFound) + { + return KErrNotFound; + } + + if (aDirection == EDown) + { + TInt idx = startIdx - 1; + TInt rowCount = iRows.Count(); + while (++idx < rowCount) + { + CMIDFormRow* row = iRows[idx]; + if (row->Position().iY > Height()) + { + break; + } + if (row->HasFocusableItemOnOrBelowScreen()) + { + return idx; + } + } + } + else + { + TInt idx = startIdx + 1; + while (--idx >= 0) + { + CMIDFormRow* row = iRows[idx]; + + if (row->HasFocusableItemOnOrAboveScreen()) + { + return idx; + } + + if ((row->Position().iY + row->Size().iHeight) < 0) + { + break; + } + } + } + + return KErrNotFound; +} + +// if aFromTop is false we look from the bottom up +TInt CMIDForm::FirstRowOnScreen(TDirection aDirection) +{ + ASSERT(aDirection == EUp || aDirection == EDown); + + TInt idx = 0; + if (aDirection == EDown) + { + idx = -1; + TInt rowCount = iRows.Count(); + while (++idx < rowCount) + { + CMIDFormRow* row = iRows[idx]; + TInt bottom = row->Position().iY + row->Size().iHeight; + if (bottom > 0) + { + return idx; + } + } + } + else + { + idx = iRows.Count(); + while (--idx >= 0) + { + CMIDFormRow* row = iRows[idx]; + TInt top = row->Position().iY; + if (top < Height()) + { + return idx; + } + } + } + + return KErrNotFound; +} + +// Returns the item index of the next focusable item in the given direction. +// Note: this may return an object that is not on the current row if there is +// an unconstrained string item on the current row. +// +// -------- +// | | +// | A | +// | | -------- +// -------- -------- +// ----------------- +// __________ ------ +// | | +// | B | +// | | +// ------ +// In the above diagram we see a selectable item A, an unconstrained stringItem, +// and another selectable item B. If A has focus and the stringItem is not focusable +// then calling NextFocusableHorizontalIdx( ERight ) should return the index of B. +// If the string item is selectable then its index would get returned and a further +// call to NextFocusableHorizontalIdx( ERight ) would return the index of B. +// If B has focus calling NextFocusableHorizontalIdx( ELeft ) would simply return the +// index of the stringItem if it is selectable or A if not. +TInt CMIDForm::NextFocusableHorizontalIdx(TDirection aDirection) +{ + ASSERT(aDirection == ELeft || aDirection == ERight); + + TInt idx = KErrNotFound; + + if (iFocused != KErrNotFound) + { + CMIDFormRow* rowPtr = NULL; + CMIDControlItem& controlItem = ControlItem(iFocused); + + TInt rowIdx = Row(controlItem, rowPtr); + + if (rowIdx != KErrNotFound) + { + + idx = rowPtr->Find(&controlItem); + + if (aDirection == ELeft) + { + while (--idx >= 0) + { + if (rowPtr->Item(idx)->IsSelectable()) + { + return ItemIndex(*(rowPtr->Item(idx))); + } + } + + } + else + { + // direction is ERight + while (++idx < rowPtr->NumItems()) + { + if (rowPtr->Item(idx)->IsSelectable()) + { + return ItemIndex(*(rowPtr->Item(idx))); + } + } + + } + } + else + { + // there is something focused but we failed to find a row index for it, + // so it is probably an unconstrained stringItem + if (aDirection == ELeft) + { + if (IsStringItem(controlItem)) + { + CMIDStringItem* si = static_cast(&controlItem); + idx = FirstFocusableItemBeforeStartOfUnconstrainedStringItemIdx(*si); + return idx; + } + } + else + { + // direction ie ERight + if (IsStringItem(controlItem)) + { + CMIDStringItem* si = static_cast(&controlItem); + idx = FirstFocusableItemOnLastLineOfUnconstrainedStringItemIdx(*si); + return idx; + } + } + } + } + + return KErrNotFound; +} + +/** Replaces the currently focused item. This is called when the focused item is removed +or it loses focusability, for example a string item that loses all its commands. So this +is always triggered by a java call, never by the user. We call the same methods that are +used by Scroll(), ie TryMovingFocusHorizontally() and TryMovingFocusVertically(). However +in this case we don't want the form to scroll if the focused item is not on screen. So +these two methods will move the focus if a focusable item is either completely or partially +visible on screen following the good direction (right-down for left to right devices and +vice-versa for right to left devices). TryMovingFocusVertically() will set the focused item +to KErrNotFound if no focusable item is currently visible. */ +void CMIDForm::ReplaceFocusedItem() +{ + if (LayoutPending()) + { + DEBUG("CMIDForm::ReplaceFocusedItem - calling layout"); + TRAP_IGNORE(LayoutFormL()); + } + + CMIDForm::TDirection direction = (iInitialAlignment == MMIDItem::ERight) ? ELeft : ERight; + + if (!TryMovingFocusHorizontally(direction)) + { + if (iInitialAlignment == MMIDItem::ERight) + {//RIGHT TO LEFT + if (!TryMovingFocusVertically(direction == ELeft ? EDown : EUp, EFalse, EFalse)) + { + SetFocusedItem(KErrNotFound); + } + } + else + {//LEFT TO RIGHT + if (!TryMovingFocusVertically(direction == ELeft ? EUp : EDown, EFalse, EFalse)) + { + SetFocusedItem(KErrNotFound); + } + } + } +} + +void CMIDForm::SetFocusedItem(TInt aFocusIdx, TDirection aDirection /*= ENone*/, + TBool aDoScroll /*= ETrue*/) +{ + TInt oldFocused = iFocused; + TInt focusIndexDelta = aFocusIdx - iFocused; + if (iFocused != KErrNotFound) + { // actions for the item loosing focus + CMIDControlItem& control = ControlItem(iFocused); + control.PostFocusTransferEvent(EFalse, aDirection); + control.SetFocus(EFalse); + UpdateItemCommands(NULL, NULL); + } + + iFocused = aFocusIdx; + + //If there is traverse from visible to invisible TextField, Form has to scroll first + //before actions for the item which gaining focus are done. Form scrolling provides + //position settings to all visible items in form. + //When TextField gains focus, cursor values are calculated. Because this step was done before + //scrolling, position of edwin in TextField was not set (@see CMIDFormRow::SizeChanged()) + //and cursor was drawn to wrong position. + //So Form has to scroll first -> this action provides proper position settings + //to item and items which are part of it (e.g. edwin in TextField) and after that + //changing focus makes proper cursor drawing. + if (focusIndexDelta != 0 && aDoScroll) + { + if (oldFocused != KErrNotFound) + { + aDirection = (focusIndexDelta > 0) ? EDown : EUp; + } + + ScrollToFocused(aDirection, EFalse); + } + + if (iFocused != KErrNotFound) + { // actions for the item gaining focus + CMIDControlItem& control = ControlItem(iFocused); + + SetHighlightBackgroundRects(); + + control.PostFocusTransferEvent(ETrue, aDirection); + control.SetFocus(ETrue); + // msk: deliver also the possible MSK command to displayable + UpdateItemCommands(control.CommandList(), control.GetMSKCommand()); + } +} + +/** Updates the model of the scrollbar and moves the thumb. Scroll bar must have +already been layouted.*/ +void CMIDForm::UpdateScrollBar() +{ + ASSERT(iSBFrame); + DEBUG_INT("CMIDForm::UpdateScrollBar - iScroll is %D", iScroll); + + TInt heightOfAllItems = HeightOfAllItems(); + TInt height = heightOfAllItems > Height() ? heightOfAllItems : Height(); + + TEikScrollBarModel vSbarModel(height, Height(), -iScroll); + iSBFrame->Tile(&vSbarModel); + iSBFrame->MoveVertThumbTo(-iScroll); + + iPopupController->HidePopup(); // hide info popup window if visible +} + +CMIDForm* CMIDForm::NewL(MMIDEnv& aEnv,MMIDDisplayable& aDisplayable) +{ + CMIDForm* form = new(ELeave) CMIDForm(aEnv, static_cast(aDisplayable)); + CleanupStack::PushL(form); + form->ConstructL(); + CleanupStack::Pop(form); + return form; +} + +CMIDForm::CMIDForm(MMIDEnv& aEnv, CMIDDisplayable& aDisplayable) + : iDisplayable(aDisplayable), iEnv(&aEnv), iFocused(KErrNotFound), + iInitialAlignment(MMIDItem::ELeft), iHasScrolled(EFalse), + iLastFadeMessage(0) +{ +} + +CMIDForm::~CMIDForm() +{ + TRAP_IGNORE(DeleteAllItemsL()); + DeleteRows(); + iCurrentRow = NULL; + + delete iSBFrame; + SetItemsFormPointersToVal(NULL); + iItems.Close(); + iRows.Close(); + + delete iLayoutTimer; + delete iNoDataText; + + if (iBackgroundControlContext) + { + delete iBackgroundControlContext; + iBackgroundControlContext = NULL; + } + if (iHighlightedBackgroundCc) + { + delete iHighlightedBackgroundCc; + iHighlightedBackgroundCc = NULL; + } + + // Displayable is notified about content control deletion + iDisplayable.NotifyContentDestroyed(); + + delete iPopupController; + + if (iHighlightTimer) + { + iHighlightTimer->Cancel(); + } + delete iHighlightTimer; + + delete iPhysics; +} + +#include "javaoslayer.h" + +void CMIDForm::Draw(const TRect& aRect) const +{ + if (!iStartUpTraceDone) + { + java::util::JavaOsLayer::startUpTrace("MIDP: CMIDForm::Draw starts", -1, -1); + } + if (LayoutPending()) + { + return; + } + + CWindowGc& gc = SystemGc(); + + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + AknsDrawUtils::Background(skin, iBackgroundControlContext, this, gc, aRect); + + // Draw focus highlight + if (iFocused != KErrNotFound) + { + TRect focusedRect = ControlItem(iFocused).Rect(); + if (focusedRect.Intersects(aRect)) + { + AknsDrawUtils::Background(AknsUtils::SkinInstance(), + iHighlightedBackgroundCc, this, + SystemGc(), focusedRect); + } + } + + // If there is no items, "No data" string is printed to the middle of the screen + if (iItems.Count() == 0) + { + TRAP_IGNORE(DrawNoDataStringL()); + } + if (!iStartUpTraceDone) + { + java::util::JavaOsLayer::startUpTrace("MIDP: CMIDForm::Draw done", -1, -1); + iStartUpTraceDone = ETrue; + } +} + +void CMIDForm::DrawNoDataStringL() const +{ + CWindowGc& gc = SystemGc(); + + // Actual skin instance + MAknsSkinInstance* skin = AknsUtils::SkinInstance(); + + // If there is not the color fot the particular skin and id found, default (black) is used + TRgb color; + // Try to receive the color by skin and color Id, if not successful, black is used + TInt err = AknsUtils::GetCachedColor(skin, color, KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG6); + if (err != KErrNone) + { + color = AKN_LAF_COLOR(AknLayout::Title_pane_texts_Line_1(0, 0).iC); + } + + const CFont* font = CMIDFont::DefaultFont(CMIDFont::EDefaultTextId); + TInt textWidth = font->TextWidthInPixels(iNoDataText->Des()); + + // Rectangle for computing the center + TRect screenRect; + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EScreen, screenRect); + + // Centers the string to the middle of the screen + TInt x = (screenRect.Width() - textWidth) / 2; + TInt y = (iInnerHeight - font->HeightInPixels()) / 2; + TPoint point(x, y); + //Reserve extra space for biditext formatting + HBufC* bidiTextBuffer = HBufC::NewLC(iNoDataText->Size() + KAknBidiExtraSpacePerLine); + TPtr bidiText = bidiTextBuffer->Des(); + TPtr caption = iNoDataText->Des(); + bidiText = caption; + // Handle (bidi)text formatting for all languages + AknBidiTextUtils::ConvertToVisualAndClipL(bidiText, *font, textWidth, textWidth); + + gc.SetPenColor(color); + gc.UseFont(font); + gc.DrawText(bidiText, point); + CleanupStack::PopAndDestroy(bidiTextBuffer); + gc.DiscardFont(); +} + + +/** Request a layout operaiton. We must delete the rows if postponing layout +in case java side some of the controls have been deleted. Otherwise because those +controls may be on existing rows we would get a panic in a draw request outside +our control. By deleting the rows in the worse scenario we'll get a blank form +drawn temporarily until the next layout operation. */ +void CMIDForm::RequestLayoutL(TBool aImmediately /*= EFalse*/) +{ + if (aImmediately) + { + iLayoutTimer->Cancel(); + LayoutAndDrawL(); + } + else + { + if (LayoutPending()) + { + TTime now; + now.HomeTime(); + + if (now.MicroSecondsFrom(iLayoutRequestTime) > KLayoutCap) + { + LayoutAndDrawL(); + } + else + { + iLayoutTimer->Cancel(); + } + } + else + { + iLayoutRequestTime.HomeTime(); + DeleteRows(); + } + + if (iLayoutRequestTime != -1) + { + iLayoutTimer->After(KLayoutTimeout); + } + } +} + +/** Return true if a layout request has been made. */ +TBool CMIDForm::LayoutPending() const +{ + return (iLayoutRequestTime != -1) && (iLayoutTimer->IsActive()); +} + +/** +Layout the form, do scrolling and draw. Reset any previous layout request. + +@see RequestLayout(), LayoutPending(), LayoutFormL() and DoScroll() +*/ +void CMIDForm::LayoutAndDrawL() +{ + DEBUG("CMIDForm::LayoutAndDrawL"); + + LayoutFormL(); + + HandleItemVisibilityChange(); + UpdateScrollBar(); + SetHighlightBackgroundRects(); + DrawDeferred(); + + UpdatePhysics(); +} + +/** Call LayoutAndDrawL() inside a trap harness and log an error message if it +levaes. + + @param aOnlyIfPending Call LayoutAndDrawL() only if there is one pending, + @see LayoutPending(). By default this parameter is EFalse, i.e. always do layout + + @return The leave code returned by LayoutAndDrawL() +*/ +TInt CMIDForm::TrappedLayoutAndDraw(TBool aOnlyIfPending /* = EFalse */) +{ + if (aOnlyIfPending && !LayoutPending()) + { + return KErrNone; + } + + TRAPD(err, LayoutAndDrawL()); + return err; +} + +/* Called when the form becomes current and after the focus was set + * @see CMIDDisplayable::HandleCurrentL() + */ +void CMIDForm::HandleCurrentL(TBool aCurrent) +{ + if (iFocused != KErrNotFound) + { + CMIDControlItem& control = ControlItem(iFocused); + control.HandleCurrentL(aCurrent); + } + + if (!aCurrent) + { + iPopupController->HidePopup(); + } + +#ifdef RD_SCALABLE_UI_V2 + iCanDragFocus = EFalse; +#endif +} + +void CMIDForm::ConstructL() +{ + UpdateMemberVariables(); + + SetContainerWindowL(iDisplayable); + + iPopupController = CMIDPopupNoteController::NewL(); + + // Scroll initialisations +#ifdef RD_SCALABLE_UI_V2 + iSBFrame = new(ELeave) CEikScrollBarFrame(this, this, ETrue); + Window().SetPointerGrab(ETrue); +#else + iSBFrame = new(ELeave) CEikScrollBarFrame(this, NULL, ETrue); +#endif //RD_SCALABLE_UI_V2 + + iSBFrame->SetScrollBarVisibilityL( + CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto); + iSBFrame->CreateDoubleSpanScrollBarsL(ETrue, EFalse); + + // Background initialisation + iBackgroundControlContext = CAknsListBoxBackgroundControlContext::NewL( + KAknsIIDQsnBgAreaMainListGene, Rect(), ETrue, + KAknsIIDQsnBgColumnAB, Rect()) ; // Rect parameters are only place-holders + + // Background for highlighted item, frame rects are set later + iHighlightedBackgroundCc = CAknsFrameBackgroundControlContext::NewL( + KAknsIIDQsnFrInput, + TRect(), TRect(), ETrue); + + iDisplayable.SetComponentL(*this); + + // get the layout from shared data so we can tell what our + // initial alignment should be + TInt layoutId = 0; + + CRepository* repository = CRepository::NewL(KCRUidAvkon); + repository->Get(KAknLayoutId, layoutId); + delete repository; + + if (layoutId == EAknLayoutIdABRW) + { + iInitialAlignment = MMIDItem::ERight; + } + + iLayoutTimer = CTimeOutTimer::NewL(CActive::EPriorityIdle, *this); + iNoDataText = iEikonEnv->AllocReadResourceL(R_AVKON_NO_DATA); + +#ifdef RD_TACTILE_FEEDBACK + iFeedback = MTouchFeedback::Instance(); +#endif + //index of pointed control is set to not active + iIndexPointedControl=-1; + + if (CMIDFormPhysics::FeatureEnabled()) + { + iPhysics = CMIDFormPhysics::NewL(*this); + + // Destroy iPhysics immediately if physics is not allowed. + // This should happen in error situation only, when implementation + // dll couldn't be loaded although the feature was enabled. + if (iPhysics && !iPhysics->PhysicsAllowed()) + { + delete iPhysics; + iPhysics = NULL; + } + else + { + iHighlightTimer = CPeriodic::NewL(CActive::EPriorityStandard); + } + } + + LayoutAndDrawL(); +} + +void CMIDForm::UpdateMemberVariables() +{ + TAknLayoutRect mainMidpPane; + +#ifdef RD_JAVA_S60_RELEASE_9_2 + TRect mainPaneRect; + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainPaneRect); + mainMidpPane.LayoutRect(mainPaneRect, + AknLayoutScalable_Avkon::main_midp_pane().LayoutLine()); +#else + mainMidpPane.LayoutRect(iEikonEnv->ScreenDevice()->SizeInPixels(), + AknLayoutScalable_Avkon::main_midp_pane().LayoutLine()); +#endif // RD_JAVA_S60_RELEASE_9_2 + + TInt variety = Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0; + iMidpFormRect.LayoutRect(mainMidpPane.Rect(), + AknLayoutScalable_Avkon::midp_form_pane(variety).LayoutLine()); + + TAknLayoutRect listForm2MidpPane; + listForm2MidpPane.LayoutRect(iMidpFormRect.Rect(), + AknLayoutScalable_Avkon::list_form2_midp_pane().LayoutLine()); + + iFormRect.LayoutRect(listForm2MidpPane.Rect(), + AknLayoutScalable_Avkon::form2_midp_field_pane().LayoutLine()); + + iInnerWidth = iFormRect.Rect().Width(); + + iInnerHeight = iFormRect.Rect().Height(); + iFormHeight = iMidpFormRect.Rect().Height(); +} + +MMIDItem* CMIDForm::CurrentItem() const +{ + return iFocused == KErrNotFound ? NULL : iItems[iFocused]; +} + +CMIDItem& CMIDForm::Item(TInt aIndex) const +{ + MMIDItem* item = iItems[aIndex]; + return ControlItem(item->Type()); +} + +CMIDControlItem& CMIDForm::ControlItem(TInt aIndex) const +{ + return ControlItem(iItems[aIndex]); +} + +CMIDControlItem& CMIDForm::ControlItem(MMIDItem* aMMidItem) +{ + CMIDControlItem* base = NULL; + TInt type = (TInt) aMMidItem->Type(); + switch (type) + { + case MMIDComponent::EImageItem: + base = static_cast(aMMidItem); + break; + case MMIDComponent::EStringItem: + base = static_cast(aMMidItem); + break; + case MMIDComponent::ESpacer: + base = static_cast(aMMidItem); + break; + case MMIDComponent::EGauge: + base = static_cast(aMMidItem); + break; + case MMIDComponent::ETextField: + base = static_cast(aMMidItem); + break; + case MMIDComponent::EChoiceGroup: + base = static_cast(aMMidItem); + break; + case MMIDComponent::EDateField: + base = static_cast(aMMidItem); + break; + case MMIDComponent::ECustomItem: + base = static_cast(aMMidItem); + break; + default: + ASSERT(EFalse); + break; + } + + return *base; +} + +TInt CMIDForm::Index(CCoeControl* aControl) const +{ + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CCoeControl* control = static_cast(&(ControlItem(i))); + if (control == aControl) + { + return i; + } + } + return KErrNotFound; +} + +TInt CMIDForm::FormWidth() +{ + return CMIDForm::StaticFormRect().Width(); +} + +/*static*/ TRect CMIDForm::StaticFormRect() +{ + TInt variety = Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0; + TRect mainPaneRect; + +#ifdef RD_JAVA_S60_RELEASE_9_2 + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainPaneRect); +#else + mainPaneRect = iAvkonAppUi->ApplicationRect(); +#endif // RD_JAVA_S60_RELEASE_9_2 + + TAknLayoutRect mainMidpPane; + mainMidpPane.LayoutRect(mainPaneRect, + AknLayoutScalable_Avkon::main_midp_pane().LayoutLine()); + + TAknLayoutRect midpFormRect; + midpFormRect.LayoutRect(mainMidpPane.Rect(), + AknLayoutScalable_Avkon::midp_form_pane(variety).LayoutLine()); + + TAknLayoutRect listForm2MidpPane; + listForm2MidpPane.LayoutRect(midpFormRect.Rect(), + AknLayoutScalable_Avkon::list_form2_midp_pane().LayoutLine()); + + TAknLayoutRect formRect; + formRect.LayoutRect(listForm2MidpPane.Rect(), + AknLayoutScalable_Avkon::form2_midp_field_pane().LayoutLine()); + + return formRect.Rect(); +} + + +TBool CMIDForm::IsCurrentItem(CMIDItem* aItem) const +{ + return (iFocused != KErrNotFound) && + (iFocused == Index(static_cast(aItem))); +} + + +void CMIDForm::HandleControlEventL(CCoeControl* aControl,TCoeEvent aEventType) +{ + switch (aEventType) + { + case MCoeControlObserver::EEventStateChanged: + { + TInt index = KErrNotFound; + User::LeaveIfError(index = Index(aControl)); + MMIDComponent::TType type = iItems[index]->Type(); + if (type == EStringItem || type == EImageItem || type == ECustomItem) + { + // These don't change state in the MIDP sense, so it must be a button + // press so we need to post a default command event, if there is one + + // msk: this is needed if msk is not enabled + PostDefaultItemCommandEvent(index); + } + else + { + iEnv->PostJavaEvent(*this,EDisplayable,index); + } + } + break; + case MCoeControlObserver::EEventRequestFocus: + if (!aControl->IsNonFocusing()) + { + SetFocusedItem(Index(aControl)); + } + break; + default: + break; + } +} + + +/* + * Pointer events from different Items are are routed via this function to private HandleControlEventL. + * Focus is not accepted if Item is not focusable. + */ +void CMIDForm::HandleTouchControlEventL(CCoeControl* aControl,TCoeEvent aEventType) +{ + + CMIDControlItem& ci = ControlItem(Index(aControl)); + if (ci.IsSelectable()) + { + HandleControlEventL(aControl, aEventType); + } +} + + +void CMIDForm::TimerExpired() +{ + TrappedLayoutAndDraw(); +} + +/* + * Called whenever something's happened which could change the visibility of an item + * controls. e.g. when made current, when update Items, when scroll. + */ +void CMIDForm::HandleItemVisibilityChange() +{ + + // CUSTOM ITEM ONLY + TInt count = iItems.Count(); + for (TInt i=0; iType() == MMIDComponent::ECustomItem) + { + // this will get the CustomItem's FocusChanged method called which will cause + // some visibility events to be sent + CMIDControlItem& ci = ControlItem(i); + ((CMIDCustomItem&) ci).HandleVisibilityChange(); + } + } + // END CUSTOM ITEM + +} + +/** Handles an item size change event. This is called by within the plugin, not by java +side unlike RefreshItemL(TInt). + +First of all we check if there is a layout pending operation. +If there is one we don't do anything. This is because this method is called during +control creation when items are inserted in the form. However, it is also called later +on when the control is already displaying and its size changes. During control creation +we should do a delayed layout (for performance reasons) wheras if we delay layout in the +second case we get an unpleasant redraw effect because the item control usually draws +immediately. So we should try and layout+draw immediately in the second case. +We distinguish between the two cases by checking if there is a layout +operation (which is requested by InsertItemL()). Also, in the second case, instead of +doing a full LayoutAndDrawL() we only resize elements already assigned to rows and +update the scroll bars. And then we draw. This is a bit more efficient than recreating +the rows. Because this method is only called by items that fill an entire row anyway, +we shouln't need to re-create rows. +*/ +void CMIDForm::RefreshItemL(MMIDItem* aItem) +{ + TInt index = iItems.Find(aItem); + + if (!LayoutPending() && index != KErrNotFound) + { + SizeItemsInFormRowsL(); + SetRowExtentsWithRespectToScrolledLocation(); + + if ((aItem->Type() == MMIDComponent::ETextField) && (index == iFocused)) + { + CMIDTextFieldItem* tf = static_cast(aItem); + TPoint p; + TBool success = tf->GetCursorPosL(p); + if (success) + { + if (p.iY < 0) + { + RawScrollRelative(p.iY + 10); + } + else if (p.iY > Height()) + { + RawScrollRelative(Height() - (p.iY + 10)); + } + } + } + RawScrollFinalize(ETrue); + } +} + +/** + * If the item at aIndex has a default command set for it, then this will be invoked and True + * will be returned. False if a command has not been posted + */ +// msk: this method is needed if msk is not enabled +TBool CMIDForm::PostDefaultItemCommandEvent(TInt aIndex) +{ + if (aIndex != KErrNotFound) + { + CMIDCommand* defaultCommand = ControlItem(iItems[aIndex]).DefaultCommand(); + if (defaultCommand) + { + iEnv->PostJavaEvent(*iItems[aIndex],EItem,ECommand,defaultCommand->Id()); + return ETrue; + } + } + return EFalse; +} + +void CMIDForm::AddItemToFormL(CMIDControlItem& aControlItem) +{ + aControlItem.SetForm(this); + aControlItem.SetContainerWindowL(*this); + aControlItem.ItemAddedToFormL(); +} + +void CMIDForm::RemoveItemFromForm(CMIDControlItem& aControlItem) +{ + aControlItem.MakeVisible(EFalse); + aControlItem.SetPosition(TPoint(-Width(), 0)); + // Item is not allowed to access container window from now. + aControlItem.ItemRemovedFromForm(); + aControlItem.SetForm(NULL); +} + +void CMIDForm::UpdateItemCommands(CMIDCommandList* aCommandList, CMIDCommand* aMSKCommand) +{ + iDisplayable.SetItemCommandList(aCommandList, aMSKCommand); +} + +// msk: this method is needed if msk is not enabled +TBool CMIDForm::DoDefaultCommand() +{ + if (iFocused != KErrNotFound) + { + CMIDControlItem& ci = ControlItem(iFocused); + + CMIDCommand* defaultCommand = ci.DefaultCommand(); + if (defaultCommand) + { + iEnv->PostJavaEvent(*(iItems[iFocused]), EItem, ECommand, defaultCommand->Id()); + return ETrue; + } + } + + return EFalse; +} + + +TInt CMIDForm::Row(const CMIDControlItem& aItem) const +{ + CMIDFormRow* rowPtr = NULL; + return Row(aItem, rowPtr); +} + +TInt CMIDForm::Row(const CMIDControlItem& aItem, CMIDFormRow*& aRowPtr) const +{ + aRowPtr = NULL; + TInt rowCount = iRows.Count(); + for (TInt i=0; i < rowCount; i++) + { + TInt idx = iRows[i]->Find(&aItem); + if (idx != KErrNotFound) + { + aRowPtr = iRows[i]; + return i; + } + } + return KErrNotFound; +} + +CMIDForm::TDirection CMIDForm::ConvertKeyToDirection(TUint aCode) +{ + switch (aCode) + { + case EKeyDownArrow: + return EDown; + case EKeyUpArrow: + return EUp; + case EKeyLeftArrow: + return ELeft; + case EKeyRightArrow: + return ERight; + default: + return ENone; + } +} + +// returns the index of a CMIDControlItem in the iItems array. If the +// CMIDControlItem is a CMIDLabelContainerItem, then the index +// of the stringItem associated with it is returned. +TInt CMIDForm::ItemIndex(CMIDControlItem& aItem) +{ + MMIDItem* item = aItem.iMMidItem; + + TInt idx = 0; + TInt itemCount = iItems.Count(); + while (idx < itemCount) + { + if (iItems[idx] == item) + { + return idx; + } + idx++; + } + + // OK, so we didn't find it. Maybe it is a CMIDLabelContainerItem + if (IsLabelContainerItem(aItem)) + { + CMIDLabelContainerItem* usil = static_cast(&aItem); + return ItemIndex(usil->StringItem()); + } + + return KErrNotFound; +} + +// this method will return the index of the row ABOVE the indicated unconstrained string item +// if the string item was on the top row then KErrNotFound is returned, also if the string item +// could not be found or was not an unconstrained string item. +TInt CMIDForm::FirstRowAboveUnconstrainedStringItem(CMIDStringItem& aStringItem) const +{ + TInt idx = FirstRowOfUnconstrainedStringItem(aStringItem); + return (idx == KErrNotFound) ? KErrNotFound : idx - 1; +} + +// returns the index of the first row of an unconstrained string item. +// Returns KErrNotFound if the string item could not be found (or if it was not +// an unconstrained string item). +TInt CMIDForm::FirstRowOfUnconstrainedStringItem(CMIDStringItem& aStringItem) const +{ + TInt idx = -1; + TInt rowCount = iRows.Count(); + while (++idx < rowCount) + { + CMIDFormRow* row = iRows[idx]; + TInt innerIdx = -1; + while (++innerIdx < row->NumItems()) + { + CMIDControlItem* ci = row->Item(innerIdx); + if (IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem* ucsi = static_cast(ci); + if (&(ucsi->StringItem()) == &aStringItem) + { + return idx; + } + } + } + } + return KErrNotFound; +} + +TInt CMIDForm::FirstRowBelowUnconstrainedStringItem(CMIDStringItem& aStringItem) const +{ + TInt idx = iRows.Count(); + TInt rowCount = iRows.Count(); + while (--idx < rowCount) + { + CMIDFormRow* row = iRows[idx]; + TInt innerIdx = -1; + while (++innerIdx < row->NumItems()) + { + CMIDControlItem* ci = row->Item(innerIdx); + if (IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem* ucsi = static_cast(ci); + if (&(ucsi->StringItem()) == &aStringItem) + { + return ((idx + 1) >= rowCount) ? KErrNotFound : idx + 1; + } + } + } + } + return KErrNotFound; +} + +TInt CMIDForm::FirstFocusableItemBeforeStartOfUnconstrainedStringItemIdx(CMIDStringItem& aStringItem) +{ + TInt rowIdx = FirstRowOfUnconstrainedStringItem(aStringItem); + if (rowIdx != KErrNotFound) + { + CMIDFormRow* row = iRows[rowIdx]; + TInt idxOnRow = row->NumItems(); + while (--idxOnRow >= 0) // search backward along the row for the unconstrained string item + // we probably could assume it is the last element + { + CMIDControlItem* ci = row->Item(idxOnRow); + if (IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem* usil = static_cast(ci); + CMIDStringItem* si = &(usil->StringItem()); + if (si == &aStringItem) + { + break; + } + } + } + if (idxOnRow > 0) + { + // there are some items on the row before the start of the unconstrained string item + while (--idxOnRow >= 0) // search backward along the row for the first focusable item + { + CMIDControlItem* ci = row->Item(idxOnRow); + if (ci->IsSelectable()) + { + return ItemIndex(*ci); + } + } + } + } + return KErrNotFound; +} + +TInt CMIDForm::FirstFocusableItemOnLastLineOfUnconstrainedStringItemIdx(CMIDStringItem& aStringItem) +{ + TInt rowIdx = FirstRowBelowUnconstrainedStringItem(aStringItem) - 1; + + if (rowIdx == -2) + { + rowIdx = iRows.Count() -1; + } + + if (rowIdx > KErrNotFound) // if FirstRowBelowUnconstrainedStringItem returned KErrNotFound then we have -2 so check with '>' + { + CMIDFormRow* row = iRows[rowIdx]; + TInt idxOnRow = -1; + + TBool found = EFalse; + while (++idxOnRow < row->NumItems()) + { + CMIDControlItem* ci = row->Item(idxOnRow); + + if (ControlBelongsToSameUnconstrainedItem(ci,aStringItem)) + {//ok we found our item, if the next one is selectable we return + found = ETrue; + } + else if (found && ci->IsSelectable()) + { + return ItemIndex(*ci); + } + } + } + return KErrNotFound; +} + +/** Return true if aCi belongs to the same unconstrained string item (aStringItem) */ +TBool CMIDForm::ControlBelongsToSameUnconstrainedItem(CMIDControlItem* aCi, CMIDStringItem& aStringItem) const +{ + if (IsLabelContainerItem(*aCi)) + { + CMIDLabelContainerItem* usil = static_cast(aCi); + if (&(usil->StringItem()) == &aStringItem) + { + return ETrue; + } + } + + return EFalse; +} + +/** */ +void CMIDForm::RemoveDeletedItem(CMIDControlItem& aItem) +{ + iFocused = KErrNotFound; + TInt i=-1; + TInt rowCount = iRows.Count(); + while (++i < rowCount) + { + CMIDFormRow* row = iRows[i]; + if (row->RemoveItemIfExists(aItem)) + { + break; + } + } + + i = -1; + TInt itemCount = iItems.Count(); + while (++i < itemCount) + { + if (aItem.iMMidItem) + { + if (aItem.iMMidItem == iItems[i]) + { + iItems.Remove(i); + break; + } + } + } + +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// new layout methods +///////////////////////////////////////////////////////////////////////////////////////// + +// does the layout for the entire form +void CMIDForm::LayoutFormL() +{ + DEBUG("CMIDForm::LayoutFormL"); + + if (LayoutPending()) + { + iLayoutRequestTime = -1; + iLayoutTimer->Cancel(); + } + + TReal prevHeight = HeightOfAllItems() - Height(); + TInt prevFocusedYPos = 0; // by default it is on top of screen + TInt focusedItem = iFocused; + + if (focusedItem != KErrNotFound) + { + TRect rect = GetControlRect(focusedItem); + prevFocusedYPos = rect.iTl.iY; + } + + + // scroll bar layouting + AknLayoutUtils::LayoutVerticalScrollBar(iSBFrame, Rect(), + AknLayoutScalable_Avkon::scroll_pane_cp51().LayoutLine()); + ResetItemsPreferredSizes(); + + // this prevents items from calling layout recursively + SetItemsFormPointersToVal(NULL); + + AssignItemsToRowsL(); + SizeItemsInFormRowsL(); + + SetItemsFormPointersToVal(this); + + SetRowExtentsWithRespectToScrolledLocation(); + + TReal newHeight = HeightOfAllItems() - Height(); + + if (newHeight != prevHeight) + { + // layout has changed significantly, but we need to keep focused component scrolled so that + // user sees it at the same level in viewport + TInt newFocusedYPos = 0; + if (focusedItem != KErrNotFound) + { + TRect rect = GetControlRect(focusedItem); + newFocusedYPos = rect.iTl.iY; + TInt delta = prevFocusedYPos - newFocusedYPos; + if (iScroll != 0) + { + // correct only if not on top of window + ScrollRelative(delta); + } + } + else + { + if (newHeight < prevHeight) + { + // no focused item, just try to keep same component visible + iScroll = newHeight > 0 ? ((TReal) iScroll * (newHeight / prevHeight)) : 0; + } + } + + SetRowExtentsWithRespectToScrolledLocation(); + } +} + +void CMIDForm::ResetItemsPreferredSizes() +{ + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CMIDControlItem& ci = ControlItem(i); + ci.ResetPreferredSize(); + } +} + +void CMIDForm::SetItemsFormPointersToVal(CMIDForm* aVal) +{ + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CMIDControlItem& ci = ControlItem(i); + ci.SetForm(aVal); + } +} + +/** If an items preferred size is zero +we shouldn't add it to the form row and so we skip it */ +TBool CMIDForm::SkipNullItem(TInt aIndex) +{ + CMIDControlItem& ci = ControlItem(iItems[aIndex]); + if (ci.PreferredSize() == TSize(0, 0)) + { + if (iFocused == aIndex) + { + SetFocusedItem(KErrNotFound); + } + + return ETrue; + } + + return EFalse; +} + +/** + If the current row index is the same as the row count, + create and append to iRows a new row and store it into + iCurrentRow. Increment the current row index. Also reset + the current alignment. +*/ +void CMIDForm::CreateNewRowAndSetAsCurrentL() +{ + CMIDFormRow* row = CMIDFormRow::NewL(*this); + CleanupStack::PushL(row); + + User::LeaveIfError(iRows.Append(row)); + CleanupStack::Pop(row); + + iCurrentRow = row; + iCurrentRow->SetAlignment(iCurrentAlignment); +} + +/** Verify the current alignment and change it if the current row +is empty. If the row is not empty and the item alignement is different +and the item requires a row break, then add a new row. */ +void CMIDForm::VerifyRowAlignmentL(TInt aIndex) +{ + TInt leftRightMask = MMIDItem::ELeft | MMIDItem::ERight; + CMIDControlItem& ci = ControlItem(iItems[aIndex]); + + if (iCurrentRow->NumItems() == 0) + { + // this is the first item in the row + if (!LayoutDefault(ci)) + { + // we take the items alignment for the row + iCurrentAlignment = MMIDItem::TLayout(ci.Layout() & leftRightMask); + iCurrentRow->SetAlignment(iCurrentAlignment); + } + } + else if (!LayoutMatchesCurrentAlignment(ci, iCurrentAlignment)) + { + // the new layout doesn't match the current so go to the next row + CreateNewRowAndSetAsCurrentL(); + iCurrentAlignment = MMIDItem::TLayout(ci.Layout() & leftRightMask); + iCurrentRow->SetAlignment(iCurrentAlignment); + } +} + +/** + Inserts the specified number of new rows (aNumNewLines). If there is a control + item passed as a parameter it means the lines to be inserted are empty lines + preceeding the control and therefore should have their empty size set to the + size of the control. + + @param aControlItem The control item used to set the size of empty rows. + + @param aNumNewLines The number of rows to be added, if equal to zero this method does + nothing + */ +void CMIDForm::InsertNewlinesL(TInt aNumNewlines, CMIDControlItem* aControlItem) +{ + for (TInt idx = 0; idx < aNumNewlines; idx++) + { + if (aControlItem && aControlItem->LabelControl() && iCurrentRow && iCurrentRow->NumItems() == 0) + { + iCurrentRow->SetEmptyRowSize(TSize(iInnerWidth,aControlItem->OneLineLabelHeight())); + } + + CreateNewRowAndSetAsCurrentL(); + } +} + + +/** + * Creates a new string item, using special type CMIDLabelContainerItem. + * This is a special string item which has no label and is added to wrap + * content of a previous string item across several rows. + * + * @see CMIDLabelContainerItem + */ +void CMIDForm::CreateAndAddLabelContainerItemL(CMIDStringItem& aStringItem, + CEikLabel& aLabel, TBool aIsStringItemContent) +{ + // Label container items items have an horizontal margin only + // when at the beginning of the row + TBool hasHMargin = (iCurrentRow->NumItems() == 0); + + CMIDLabelContainerItem* usil = + CMIDLabelContainerItem::NewL(aStringItem, aLabel, hasHMargin); + + CleanupStack::PushL(usil); + usil->SetContainerWindowL(*this); + // sets flag for detecting if label container item is created + // from string item label or content + usil->SetIsStringItemContent(aIsStringItemContent); + + iCurrentRow->AppendL(usil); + CleanupStack::Pop(usil); +} + +/** + * Inserts a CMIDLabelContainerItem for each line in aTextControl. + * + * @see CreateAndAddLabelContainerItemL() + */ +void CMIDForm::InsertContainerItemsL(CMIDStringItem& aStringItem, + CMIDItemLabel& aTextControl, TBool aIsStringItemContent) +{ + TInt numLines = aTextControl.NumLines(); + for (TInt j = 0; j < numLines; j++) + { // insert every line as a CMIDLabelContainerItem + CreateAndAddLabelContainerItemL( + aStringItem, *(aTextControl.LabelAtIdx(j)), aIsStringItemContent); + + if (j != (numLines-1)) + { // insert a row break except for the last line + CreateNewRowAndSetAsCurrentL(); + } + } +} + +/** + Adds an unconstrained string item to one or more rows. If the item + has a label always insert a line break (new row) before and if it + is followed by constrained item also after the label. Crop the label + to the full line width and insert is as a CMIDLabelContainerItem. + + If the item has no label we can start anywhere in the current row. + Split its content across the remaining width for the first line + and then across the full line width. Insert a CMIDLabelContainerItem + for each line and insert a row break after each CMIDLabelContainerItem + except the last one. + + Item with label can be inserted as default item when one of this + situation occurs: + - it is the last item in form + - following item is constrained + - following item is unconstrained but has a label + + Item without label can be inserted as default item when: + - it is the only item in form + - it is the first item in form and following item is + unconstrained but has a label (it starts in a new row) + + @see CMILabelContainerItem, InsertContainerItemsL(), + CreateNewRowAndSetAsCurrentL(), InsertNewlinesL() +*/ +void CMIDForm::AddUnconstrainedStringItemL(TInt& aIndex) +{ + CMIDStringItem* si = (CMIDStringItem*)iItems[aIndex]; + + TInt widthForText = Width(); + + // check if next item is unconstrained and has label + TBool nextUnconstrainedHasLabel(EFalse); + if (aIndex < iItems.Count()-1 && ItemIsUnconstrained(aIndex + 1)) + { + CMIDStringItem* nextItem = (CMIDStringItem*)iItems[aIndex+1]; + if (nextItem->LabelControl() && + (nextItem->LabelControl()->Text()->Length() > 0)) + { + nextUnconstrainedHasLabel = ETrue; + } + } + + if (si->LabelControl() && (si->LabelControl()->Text()->Length() > 0)) + {//item has a label - it should be placed in a new row + if (iCurrentRow->NumItems() > 0) + { + CreateNewRowAndSetAsCurrentL(); + } + + // If the item is last or next item is constrained or next item + // is unconstrained but has a label (it will be placed in a new row) + // this item is placed as default item. + if ((aIndex == iItems.Count()-1) || !ItemIsUnconstrained(aIndex + 1) + || nextUnconstrainedHasLabel) + { + AddDefaultItemL(aIndex); + return; + } + + // otherwise place the label in one row and then insert the text + si->LabelControl()->SetWidthL(widthForText); + // label of string item is inserted as label container item + // EFalse identifies that label container item is created from string item label + InsertContainerItemsL(*si, *(si->LabelControl()), EFalse); + // string items is divided to label container items due to concatenation of contents + // contained in adjacent string items + si->SetIsDivided(ETrue); + CreateNewRowAndSetAsCurrentL(); + } + else + { // item has no label + + // If the item is the only item in form or it is the first item in form + // and the next item is unconstrained with label (it starts at a new row) + // this item is placed as default item + if ((iItems.Count() == 1) || (aIndex == 0 && nextUnconstrainedHasLabel)) + { + AddDefaultItemL(aIndex); + return; + } + } + + // this is either the remaining width on the current row or the full + // line width if we are on a new row + TInt remainingLineWidth = widthForText - iCurrentRow->CurrentWidth(); + + if ((remainingLineWidth < si->StringContentControl()->FirstWordWidth()) + && (iCurrentRow->NumItems() != 0)) + { // if not even one word fits then go to the next line unless + // we are already on a new line + CreateNewRowAndSetAsCurrentL(); + remainingLineWidth = widthForText; + } + + // wrap the content to the remaining width for the first line and to + // the full width for any other line + si->StringContentControl()->SetVariableWidthL(remainingLineWidth, widthForText); + // content of string item is inserted as label container item + // ETrue identifies that label container item is created from string item content + InsertContainerItemsL(*si, *(si->StringContentControl()), ETrue); +} + +/** + * Adds a standard case item to the current row. It verifies if a new + * row should be added and adds one if so. Then it appends the item + * to the current row. + * + * @see CMIDControlItem, OkToMoveToNextRow() + */ +void CMIDForm::AddDefaultItemL(TInt& aIndex) +{ + CMIDControlItem& ci = ControlItem(iItems[aIndex]); + + TSize itemSize = LayoutShrink(ci) ? ci.MinimumSize() : ci.PreferredSize(); + ci.SetSizeQuiet(itemSize); + + if (OkToMoveToNextRow(itemSize)) + { + CreateNewRowAndSetAsCurrentL(); + } + + iCurrentRow->AppendL(&ci); +} + +/** + Return true if the given size does not fit into the current + row and the row is not empty and the size is not bigger than + an empty row. +*/ +TBool CMIDForm::OkToMoveToNextRow(const TSize& aItemSize) const +{ + return ((iCurrentRow->CurrentWidth() + aItemSize.iWidth) > iInnerWidth) && + !((aItemSize.iWidth > iInnerWidth) && (iCurrentRow->NumItems() == 0)); +} + +/** + Assigns the items to the rows, which are re-created. +*/ +void CMIDForm::AssignItemsToRowsL() +{ + DeleteRows(); + + //reset current alignment + iCurrentAlignment = InitialAlignment(); + + //Create a row, the form must have at least an empty row + CreateNewRowAndSetAsCurrentL(); + + for (TInt i = 0; i < iItems.Count(); i++) + { + if (SkipNullItem(i)) + { //zero size items are ignored + continue; + } + + // if the current alignment changes we may need to change row + VerifyRowAlignmentL(i); + + CMIDControlItem& ci = ControlItem(iItems[i]); + + // add any extra rows before the current item if needed + InsertNewlinesL(NumNewLinesBefore(ci), &ci); + + if (ItemIsUnconstrained(i)) + { // unconstrained string items are processed specially + AddUnconstrainedStringItemL(i); + } + else + { // any other item is processed in here + AddDefaultItemL(i); + } + + // add any additional row breaks if needed + InsertNewlinesL(NumNewLinesAfter(ci), &ci); + } +} + +/** Return true if the item is an unconstrained string item, false otherwise */ +TBool CMIDForm::ItemIsUnconstrained(TInt aIndex) const +{ + if (aIndex >= 0 && aIndex < iItems.Count()) + { + CMIDControlItem& ci = ControlItem(iItems[aIndex]); + return (IsStringItem(ci) && ((CMIDStringItem*)&ci)->IsUnconstrainedStringItem()); + } + + return EFalse; +} + +TBool CMIDForm::LayoutMatchesCurrentAlignment(CMIDControlItem& aControlItem, + MMIDItem::TLayout aCurrentAlignment) +{ + TInt leftRightMask = MMIDItem::ELeft | MMIDItem::ERight; + + return ((aControlItem.Layout() & leftRightMask) == MMIDItem::EDefault) || + ((aControlItem.Layout() & leftRightMask) == aCurrentAlignment); +} + +TBool CMIDForm::LayoutDefault(CMIDControlItem& aControlItem) +{ + TInt leftRightMask = MMIDItem::ELeft | MMIDItem::ERight; + + return ((aControlItem.Layout() & leftRightMask) == MMIDItem::EDefault); +} + +TBool CMIDForm::LayoutNewLineBefore(CMIDControlItem& aControlItem) +{ + return (aControlItem.Layout() & MMIDItem::ENewLineBefore); +} + +TBool CMIDForm::LayoutNewLineAfter(CMIDControlItem& aControlItem) +{ + return (aControlItem.Layout() & MMIDItem::ENewLineAfter); +} + +/** Return true if the LAYOUT_SHRINK directive has been set. For +string items which have one or both dimensions of the preferred +size set the layout shrink directive is ignored. */ +TBool CMIDForm::LayoutShrink(CMIDControlItem& aControlItem) +{ + if (IsStringItem(aControlItem)) + { + CMIDStringItem* si = (CMIDStringItem*)(&aControlItem); + + if (si->WidthOrHeightSpecified()) + { + return EFalse; + } + } + + return (aControlItem.Layout() & MMIDItem::EShrink); +} + +/** Return true if the LAYOUT_EXPAND directive has been set. For +string items which have one or both dimensions of the preferred +size set the layout expand directive is ignored. */ +TBool CMIDForm::LayoutExpand(CMIDControlItem& aControlItem) +{ + if (IsStringItem(aControlItem)) + { + CMIDStringItem* si = (CMIDStringItem*)(&aControlItem); + + if (si->WidthOrHeightSpecified()) + { + return EFalse; + } + } + + return (aControlItem.Layout() & MMIDItem::EExpand); +} + +/** Return true if the LAYOUT_VSHIRNK directive has been set. For +string items which have one or both dimensions of the preferred +size set the layout vertical shrink directive is ignored. */ +TBool CMIDForm::LayoutVerticalShrink(CMIDControlItem& aControlItem) +{ + if (IsStringItem(aControlItem)) + { + CMIDStringItem* si = (CMIDStringItem*)(&aControlItem); + + if (si->WidthOrHeightSpecified()) + { + return EFalse; + } + } + + return (aControlItem.Layout() & MMIDItem::EVerticalShrink); +} + +/** Return true if the LAYOUT_VEXPAND directive has been set. For +string items which have one or both dimensions of the preferred +size set the layout vertical expand directive is ignored. */ +TBool CMIDForm::LayoutVerticalExpand(CMIDControlItem& aControlItem) +{ + if (IsStringItem(aControlItem)) + { + CMIDStringItem* si = (CMIDStringItem*)(&aControlItem); + + if (si->WidthOrHeightSpecified()) + { + return EFalse; + } + } + + return (aControlItem.Layout() & MMIDItem::EVerticalExpand); +} + +TBool CMIDForm::LayoutVerticalCenter(CMIDControlItem& aControlItem) +{ + TInt layout = aControlItem.Layout(); + layout &= MMIDItem::EVerticalCenter; + return (layout == MMIDItem::EVerticalCenter); +} + +TBool CMIDForm::LayoutBottom(CMIDControlItem& aControlItem) +{ + TInt layout = aControlItem.Layout(); + layout &= MMIDItem::EVerticalCenter; + return (layout == MMIDItem::EBottom) || (layout == 0); +} + +TBool CMIDForm::IsStringItem(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDComponent::EStringItem); +} + +TBool CMIDForm::IsStringItemHyperlink(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + if (aControlItem.iMMidItem->Type() == MMIDComponent::EStringItem) + { + return (((CMIDStringItem&) aControlItem).Appearance() == MMIDItem::EHyperLink); + } + else + { + return EFalse; + } +} + +TBool CMIDForm::IsStringItemButton(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + if (aControlItem.iMMidItem->Type() == MMIDComponent::EStringItem) + { + return (((CMIDStringItem&) aControlItem).Appearance() == MMIDItem::EButton); + } + else + { + return EFalse; + } +} + +TBool CMIDForm::IsCustomItem(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDComponent::ECustomItem); +} + +TBool CMIDForm::IsGaugeItem(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDComponent::EGauge); +} + +TBool CMIDForm::IsTextFieldItem(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDComponent::ETextField); +} + +TBool CMIDForm::IsChoiceGroup(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDComponent::EChoiceGroup); +} + +TBool CMIDForm::IsDateField(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDComponent::EDateField); +} + +TBool CMIDForm::IsLabelContainerItem(CMIDControlItem& aControlItem) +{ + return (aControlItem.Type() == KLabelContainerItem); +} + +TBool CMIDForm::IsSpacerUsedForFormatting(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + if (aControlItem.iMMidItem->Type() == MMIDComponent::ESpacer) + { + CMIDSpacer* spacer = static_cast(&aControlItem); + return spacer->UsedForStringFormatting(); + } + return EFalse; +} + +TBool CMIDForm::IsImageItem(CMIDControlItem& aControlItem) +{ + if (!aControlItem.iMMidItem) + { + return EFalse; + } + return (aControlItem.iMMidItem->Type() == MMIDItem::EImageItem); +} + +#ifdef RD_JAVA_S60_RELEASE_9_2 +void CMIDForm::PostPendingUpEventL() +{ + MMIDItem* curItem = CurrentItem(); + if (curItem) + { + CMIDControlItem& item = ControlItem(curItem); + + if (IsChoiceGroup(item)) + { + CMIDChoiceGroupItem* cgItem = static_cast(&item); + cgItem->PostPendingUpEventL(); + } + } +} +#endif // RD_JAVA_S60_RELEASE_9_2 + +// returns: The number of new lines before an item. +// This only takes into account new lines that come from +// preceeding a string item with new line characters. +TInt CMIDForm::NumNewLinesBefore(CMIDControlItem& aControlItem) +{ + if (IsStringItem(aControlItem)) + { + CMIDStringItem* si = (CMIDStringItem*)(&aControlItem); + TInt nLinesBefore = si->NumNewlinesBefore(); + if (nLinesBefore > 0) + { + return nLinesBefore; + } + } + if (iCurrentRow && iCurrentRow->NumItems() == 0) + { + return 0; + } + return LayoutNewLineBefore(aControlItem) ? 1 : 0; +} + +// returns: The number of new lines that should appear after an item +// This takes into account the new lines that can occour from +// newlines at the end of a string item, and if there are none +// of these, then it returns 1 if the Layout NewLineAfter +// directive has been used and 0 otherwise. +TInt CMIDForm::NumNewLinesAfter(CMIDControlItem& aControlItem) +{ + if (IsStringItem(aControlItem)) + { + CMIDStringItem* si = (CMIDStringItem*)(&aControlItem); + if (si->NumNewlinesAfter() > 0) + { + TInt nLinesAfter = si->NumNewlinesAfter(); + if (nLinesAfter > 0) + { + return nLinesAfter; + } + } + } + return LayoutNewLineAfter(aControlItem) ? 1 : 0; +} + +// effects: Sizes the items within the rows based on their layout characteristics +// modifies: CMIDFormRow +void CMIDForm::SizeItemsInFormRowsL() +{ + TInt rowCount = iRows.Count(); + for (TInt i=0; i < rowCount; i++) + { + iRows[i]->SizeItemsL(); + } +} + +// effects: Deletes all rows in the Form. It doesn't affect the items that have +// been added to the form. +// modifies: CMIDForm, CMIDFormRow +void CMIDForm::DeleteRows() +{ + for (TInt i=0; i < iRows.Count(); i++) + { + delete iRows[i]; + } + + iRows.Reset(); + iCurrentRow = NULL; +} + +TInt CMIDForm::HeightOfAllItems() +{ + return iTotalFormHeight; +} + +void CMIDForm::UpdateHeightOfAllItems() +{ + TInt yTopMargin = iFormRect.Rect().iTl.iY - iMidpFormRect.Rect().iTl.iY; + TInt yBottomMargin = iMidpFormRect.Rect().iBr.iY - iFormRect.Rect().iBr.iY; + + iTotalFormHeight = yTopMargin; + + TInt rowCount = iRows.Count(); + for (TInt i=0; i < rowCount; i++) + { + iTotalFormHeight += iRows[i]->Size().iHeight; + } + // Form bottom margin is added + iTotalFormHeight += yBottomMargin; +} + +/* HandleResourceChange + * + * This method is called after a resource change event, for example after + * screen dynamic resolution change. + * + * In the case of a screen dynamic resolution change we notify all items and + * repeat the entire form layout. + * + * We calculate the % of form that has been scrolled so far and we + * modify iScroll so that we have scrolled the same % of form after resolution + * change. Note that the max extent of iScroll is HeightOfAllItems() - Height() + * and not simply HeightOfAllItems() as one may think, see + * SetRowExtentsWithRespectToScrolledLocation(). Also note that applying a % + * scrolling does not necessarily mean the same items will be on screen after a + * resolution change. + * Especially when changing from portrait to landscape or vice-versa the + * number of items on a row vary greatly and as a consequence the number of + * rows may vary. Therefore, if there is a focused item on screen before + * resolution change (iFocused not KErrNotFound) we make sure this item is + * visible and focused after res change by scrolling to it, see SetFocusedItem() + * and ScrollToFocused(). + */ +void CMIDForm::HandleResourceChange(TInt aType) +{ + TInt maxScrollExtentOld = HeightOfAllItems() - Height(); //max extent of iScroll + CCoeControl::HandleResourceChange(aType); + + if (aType == KEikDynamicLayoutVariantSwitch) + { // dynamic resolution change + TInt focusedItem = iFocused; + + UpdateMemberVariables(); + UpdateHeightOfAllItems(); + + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CMIDControlItem& controlItem = ControlItem(i); + controlItem.ResolutionChange(aType); + + if (controlItem.LabelControl()) + { + controlItem.LabelControl()->ResolutionChange(); + } + } + + TInt maxScrollExtentNew = HeightOfAllItems() - Height(); //max extent of iScroll + + TRAPD(err,LayoutFormL()); + + if (err == KErrNone) + { + if ((maxScrollExtentNew <= 0) && (maxScrollExtentOld > 0)) + { + // scroll on top + RawScroll(0); + if (focusedItem != KErrNotFound) + { + // restore the form item focus + CMIDControlItem& control = ControlItem(focusedItem); + control.RestoreInnerFocus(); + } + } + else + { + if ((maxScrollExtentNew > 0) || (maxScrollExtentOld > 0)) + { + // if there is something focused + if (focusedItem != KErrNotFound) + { + // restore the form item focus + CMIDControlItem& control = ControlItem(focusedItem); + ScrollToFocused(); + control.RestoreInnerFocus(); + } + // if scroll out of maxScrollExtent + if (maxScrollExtentNew+iScroll < 0) + { + // scroll to bottom + RawScroll(-maxScrollExtentNew); + } + + } + } + } + + SetHighlightBackgroundRects(); + HandleItemVisibilityChange(); + UpdateScrollBar(); + DrawDeferred(); + } + else if (aType == KEikColorResourceChange || aType == KAknsMessageSkinChange || + aType == KUidValueCoeColorSchemeChangeEvent) + {//skin or color scheme change + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CMIDControlItem& controlItem = ControlItem(i); + controlItem.ColorChange(aType); + } + + // send this event also to form scrollbar + CEikScrollBar* sb = iSBFrame->GetScrollBarHandle(CEikScrollBar::EVertical); + if (sb) + { + sb->HandleResourceChange(aType); + } + } + else if ((aType == KEikMessageUnfadeWindows) || + (aType == KEikMessageFadeAllWindows)) + { + iLastFadeMessage = aType; + } + else if ((aType == KEikMessageWindowsFadeChange) && + ((iLastFadeMessage == KEikMessageUnfadeWindows) || + (iLastFadeMessage == KEikMessageFadeAllWindows))) + { + TInt itemCount = iItems.Count(); + for (TInt i=0; i < itemCount; i++) + { + CMIDControlItem& controlItem = ControlItem(i); + controlItem.HandleWindowFade(iLastFadeMessage == KEikMessageFadeAllWindows); + } + iLastFadeMessage = 0; + } + + UpdatePhysics(); +} + +TBool CMIDForm::TryDetectLongTapL(const TPointerEvent &aPointerEvent) +{ + return iDisplayable.TryDetectLongTapL(aPointerEvent); +} + +#ifdef RD_SCALABLE_UI_V2 +void CMIDForm::HandleScrollEventL(CEikScrollBar* aScrollBar, TEikScrollEvent aEventType) +{ + if (AknLayoutUtils::PenEnabled()) + { + + if (LayoutPending()) + { + LayoutFormL(); + } + + TInt pixelIndex; + + switch (aEventType) + { + case EEikScrollUp: + { + pixelIndex = iScroll + (Height() / KScrollAmountDivider); + break; + } + + case EEikScrollDown: + { + pixelIndex = iScroll - (Height() / KScrollAmountDivider); + break; + } + + case EEikScrollPageUp: + { + pixelIndex = iScroll + Height(); + break; + } + + case EEikScrollPageDown: + { + pixelIndex = iScroll - Height(); + break; + } + + default://thumb drag + { + //scroll form if thumb position has moved more than 1/10 of the screen height either up or down + //or thumb position is at top or at bottom of the scrollbar. + if (-aScrollBar->ThumbPosition() < (iScroll - (Height() / KScrollAmountDividerForThumbDrag)) + || -aScrollBar->ThumbPosition() > (iScroll + (Height() / KScrollAmountDividerForThumbDrag)) + || aScrollBar->ThumbPosition() == 0 + || aScrollBar->ThumbPosition() == aScrollBar->Model()->MaxThumbPos()) + { + aScrollBar->DrawNow(); + pixelIndex = - aScrollBar->ThumbPosition(); + } + else + { + return; + } + break; + } + } + RawScroll(pixelIndex); + RawScrollFinalize(ETrue); + UpdatePhysics(); + } +} + + +TBool CMIDForm::StringItemContainsPoint(CMIDStringItem* aStringItem, const TPoint& aPoint) const +{ + CMIDControlItem* ci = ControlItemAtPoint(aPoint); + + if (ci) + { + if (IsLabelContainerItem(*ci)) + { + CMIDLabelContainerItem *lci = static_cast(ci); + CMIDStringItem& si = lci->StringItem(); + if (&si == aStringItem) + { // LabelContainer item is (part of) StringItem + if (si.IsDivided()) + { // StringItem was divided due to concatenation of contents + if (lci->IsStringItemContent()) + { // return ETrue if is pointed to StringItem content + return ETrue; + } + else + { // return EFasle if is pointed to StringItem label + return EFalse; + } + } + else + { // StringItem was not divided, pointed directly to unconstrained StringItem + return ETrue; + } + } + } + else + { + return (ci == aStringItem && aStringItem->TappingActionRect().Contains(aPoint)); + } + } + + return EFalse; +} + +TBool CMIDForm::IsFocusChangingWithPen() const +{ + return iFocusChangingWithPen; +} + +#endif //RD_SCALABLE_UI_V2 + + +void CMIDForm::HandleForegroundL(TBool aForeground) +{ + // send HandleForegroundL notification to gauge items + TInt itemsCount = iItems.Count(); + for (TInt i=0; i < itemsCount; i++) + { + if (iItems[i]->Type() == MMIDComponent::EGauge) + { + CMIDGaugeItem* gauge = static_cast(iItems[i]); + ASSERT(gauge); + gauge->HandleForegroundL(aForeground); + } + } +} + +CMIDPopupNoteController* CMIDForm::GetPopupNoteController() const +{ + return iPopupController; +} + +TInt CMIDForm::GetMidpNaviPos() +{ + // get main pane size from CEikAppU + TRect mainPane = iAvkonAppUi->ApplicationRect(); + + // get screen size in pixels + TAknLayoutRect mainMidpPane; + mainMidpPane.LayoutRect(iEikonEnv->ScreenDevice()->SizeInPixels(), + AknLayoutScalable_Avkon::main_midp_pane().LayoutLine()); + + // getting form size depends on screen orientation + TInt variety = Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0; + iMidpFormRect.LayoutRect(mainMidpPane.Rect(), + AknLayoutScalable_Avkon::midp_form_pane(variety).LayoutLine()); + + // different between screen size and form size + TInt xFormDiff = mainMidpPane.Rect().iBr.iX - iMidpFormRect.Rect().iBr.iX; + // width of navi bar position only for ( iAlignment == MMIDItem::ERight ) + TInt xNaviPos = iMidpFormRect.Rect().iTl.iX - xFormDiff; + + return xNaviPos; +} + +void CMIDForm::UpdatePhysics() +{ + if (iPhysics) + { + TSize worldSize(Width(), HeightOfAllItems()); + TSize viewSize(Width(), Height()); + TPoint viewCenter(Width() / 2, -iScroll + Height() / 2); + TRAP_IGNORE(iPhysics->InitPhysicsL(worldSize, viewSize, viewCenter)); + } +} + +TInt CMIDForm::HighlightTimerCallback(TAny* aPtr) +{ + CMIDForm* me = static_cast(aPtr); + me->HandleHighlightTimer(); + return 0; +} + +void CMIDForm::HandleHighlightTimer() +{ + iHighlightTimer->Cancel(); + + if (iPointedControl && iPointedControl->IsSelectable()) + { + // If tactile feedback has not been given already + // give basic tactile feedback when focus changes. + // Don't give tactile feedback for button because it is handled + // by button implementation. +#ifdef RD_TACTILE_FEEDBACK + if (!iFeedbackExecutedInPointerDown) + { + DoFeedbackOnFocusChange(*iPointedControl); + iFeedbackExecutedInPointerDown = ETrue; + } +#endif // RD_TACTILE_FEEDBACK + + TInt itemIndex = ItemIndex(*iPointedControl); + + if (itemIndex != iFocused) + { + SetFocusedItem(itemIndex, ENone, EFalse); + DrawDeferred(); + } + + if (!PhysicsScrolling() && !iUpEventSent) + { + TRAP_IGNORE(ForwardPointerEventToItemL(iLastPointerDownEvent)); + } + } +} + +void CMIDForm::SetHighlightBackgroundRects() +{ + if (iFocused == KErrNotFound) + { + return; + } + + CMIDControlItem& ci = ControlItem(iFocused); + + // Limit rect size to screen size. If Rect() is way larger + // than screen, the svg engine will not cope handling that. + TRect r(ci.Size()); + TRect s; + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EScreen, s); + + if (r.Intersects(s)) + { + s.Intersection(r); + } + + TRect rect(s); + rect.Normalize(); // ensure positive w & h, just in case + + TAknLayoutRect topLeft ; + topLeft.LayoutRect(rect, SkinLayout::Input_field_skin_placing__general__Line_2()); + + TAknLayoutRect bottomRight; + bottomRight.LayoutRect(rect, SkinLayout::Input_field_skin_placing__general__Line_5()); + + TRect outerRect = TRect(topLeft.Rect().iTl, bottomRight.Rect().iBr); + TRect innerRect = TRect(topLeft.Rect().iBr, bottomRight.Rect().iTl); + + iHighlightedBackgroundCc->SetParentContext(iDisplayable.BackGroundControlContext()); + iHighlightedBackgroundCc->SetFrameRects(outerRect, innerRect); + + UpdateHighlightBackgroundPosition(); +} + +void CMIDForm::UpdateHighlightBackgroundPosition() +{ + + if (iFocused != KErrNotFound) + { + CMIDControlItem& ci = ControlItem(iFocused); + TPoint highlightPosition = ci.PositionRelativeToScreen(); + TRect focusedRect = ci.Rect(); + + // Max size of the higlight background is limited to screen size, + // see SetHighlightBackgroundRects(). + // Need to adjust the highlight position if focused item is partially visible + // on top of the form and it is larger than screen. + if (focusedRect.iTl.iY < 0 && focusedRect.iBr.iY > 0) + { + TRect screen; + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EScreen, screen); + if (focusedRect.Height() > screen.Height()) + { + if (focusedRect.iBr.iY > iSize.iHeight) + { + // Focused item fills the visible form area fully + highlightPosition.iY = 0; + } + else + { + // Bottom of focused item is in the visible form area + highlightPosition.iY += focusedRect.Height() - screen.Height(); + } + } + } + + iHighlightedBackgroundCc->SetParentPos(highlightPosition); + } +} + +void CMIDForm::HandlePhysicsPointerEventL(const TPointerEvent& aPointerEvent) +{ + if (iPhysics && iPhysics->CanBeStopped()) + { + switch (aPointerEvent.iType) + { + case TPointerEvent::EButton1Down: + { + iPreventPhysicsScrolling = EFalse; + iFlickStoppedOnDownEvent = iFlickOngoing; + iFeedbackExecutedInPointerDown = EFalse; + iPhysics->Stop(); + CMIDControlItem* ci = ControlItemAtPoint(aPointerEvent.iPosition); + + // Physics scrolling was not ongoing and tap hit already selected item. + // Then forward pointer event to the item. + if (ci && IsCurrentItem((CMIDItem*)ci) && !iFlickStoppedOnDownEvent) + { + iPointedControl = ci; + ForwardPointerEventToItemL(aPointerEvent); + } + else + { + // Tactile fedback is executed when kinetic scrolling is stopped on down event. + if (iFlickStoppedOnDownEvent) + { +#ifdef RD_TACTILE_FEEDBACK +#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK + iFeedback->InstantFeedback(ETouchFeedbackList); +#else + iFeedback->InstantFeedback(ETouchFeedbackBasic); +#endif // RD_JAVA_ADVANCED_TACTILE_FEEDBACK + iFeedbackExecutedInPointerDown = ETrue; +#endif // RD_TACTILE_FEEDBACK + } + + // Pointed control changes. + if (ci && ci->IsSelectable() && (MMIDItem*)ci != CurrentItem()) + { + iFocusChangingWithPen = ETrue; + } + + // Start highlight timer + iPointedControl = ci; + iHighlightTimer->Cancel(); + // Start highlight timer if some any control is tapped. + if (iPointedControl) + { + TInt highlightTimeout = iPhysics->HighlightDelay() * 1000; + iHighlightTimer->Start(TTimeIntervalMicroSeconds32(highlightTimeout), + TTimeIntervalMicroSeconds32(highlightTimeout), + TCallBack(HighlightTimerCallback, this)); + } + } + // Setup members + iStartPosition = aPointerEvent.iPosition; + iLastPointerDownEvent = aPointerEvent; + iUpEventSent = EFalse; + iStartTime.HomeTime(); + iLastDragPosition = aPointerEvent.iPosition; + + break; + } + // EDrag + case TPointerEvent::EDrag: + { + TInt dragY = iStartPosition.iY - aPointerEvent.iPosition.iY; + TInt dragX = iStartPosition.iX - aPointerEvent.iPosition.iX; + iDisplayable.TryDetectLongTapL(aPointerEvent); + + // Override triggering of physicsScrolling in case of doing text painting in TextField-item + // or moving slider in Gauge-item. + if (!iPreventPhysicsScrolling && iPointedControl && iPointedControl->iMMidItem && + iPointedControl->iMMidItem->Type() == MMIDComponent::ETextField) + { + if ((Abs(dragX) > iPhysics->DragThreshold()) && (Abs(dragY) < iPhysics->DragThreshold())) + { + iPreventPhysicsScrolling = ETrue; + } + } + + // Check whether DragTreshold for panning is exceeded. If yes, then cancel timer and longtap. + if (!iPanningOngoing && Abs(dragY) > iPhysics->DragThreshold() && !iPreventPhysicsScrolling) + { + iPanningOngoing = ETrue; + iHighlightTimer->Cancel(); + iPhysics->SetPanningPosition(iLastDragPosition - aPointerEvent.iPosition); + iLastDragPosition = aPointerEvent.iPosition; + //Forward one drag event to the focused textField or Gauge when panning is triggered + if ((iFocused != KErrNotFound) + && IsTextFieldItem(ControlItem(iFocused))) + { + ControlItem(iFocused).HandlePointerEventL(aPointerEvent); + } + } + // If panning is already ongoing. Update panning position. Also we redraw entire Form + // to prevent unexpected behavior(artifacts on the screen) during flick scrolling and + // to make scrolling smoother. + else if (iPanningOngoing) + { + iPhysics->SetPanningPosition(iLastDragPosition - aPointerEvent.iPosition); + iLastDragPosition = aPointerEvent.iPosition; + DrawDeferred(); + } + // If dragged outside of StringItem area or panning starts, then up event is sent to the item. + if (!iUpEventSent && iPointedControl && IsStringItem(*iPointedControl) && (iPanningOngoing || + (iPointedControl && !iPointedControl->Rect().Contains(aPointerEvent.iPosition))) + ) + { + TPointerEvent pointerEvent = aPointerEvent; + pointerEvent.iType = TPointerEvent::EButton1Up; + ForwardPointerEventToItemL(pointerEvent); + iPointedControl = ControlItemAtPoint(aPointerEvent.iPosition); + iUpEventSent = ETrue; + } + // If physics scrolling is not ongoing forward event to the item. + else if (!iHighlightTimer->IsActive() && !PhysicsScrolling() && + (iPointedControl && iPointedControl->Rect().Contains(aPointerEvent.iPosition))) + { + ForwardPointerEventToItemL(aPointerEvent); + } +#ifdef RD_JAVA_S60_RELEASE_9_2 + // ChoiceGroup gets drag events while physics scrolling. + // Enables internal highlight disappearing when panning begins. + // We have control if there is any Item on Form. + else if (!iHighlightTimer->IsActive() + && (iFocused > KErrNotFound) + && IsChoiceGroup(ControlItem(iFocused)) + && PhysicsScrolling()) + { + ForwardPointerEventToItemL(aPointerEvent); + } +#endif // RD_JAVA_S60_RELEASE_9_2 + break; + } + + // EButton1Up + case TPointerEvent::EButton1Up: + { + TPoint distance = iStartPosition - aPointerEvent.iPosition; + if (Abs(distance.iY) > iPhysics->DragThreshold() && !iPreventPhysicsScrolling) + { + iFlickOngoing = iPhysics->StartFlick(distance, iStartTime); + if (!iUpEventSent) + { + ForwardPointerEventToItemL(aPointerEvent); + iUpEventSent = ETrue; + } + } + else + { + if (!iUpEventSent) + { + ForwardPointerEventToItemL(aPointerEvent); + iUpEventSent = ETrue; + } + } + iPanningOngoing = EFalse; + iCanDragFocus = EFalse; + break; + } + default: + { + ForwardPointerEventToItemL(aPointerEvent); + break; + } + } + } + else + { + IgnoreEventsUntilNextPointerUp(); + return; + } +} + +void CMIDForm::ForwardPointerEventToItemL(const TPointerEvent& aPointerEvent) +{ + if (iPointedControl) + { + // Drag events are not forwarded to CustomItem + if (!iFlickStoppedOnDownEvent && + !(IsCustomItem(*iPointedControl) && aPointerEvent.iType == TPointerEvent::EDrag)) + { + if (iFocused != KErrNotFound) + { + ControlItem(iFocused).HandlePointerEventL(aPointerEvent); + } + + if (LayoutPending()) + { + LayoutFormL(); + DrawDeferred(); + } + } + + if (aPointerEvent.iType == TPointerEvent::EButton1Up) + { + iFocusChangingWithPen = EFalse; + } + } +} + +void CMIDForm::HandlePhysicsScroll(TInt aScroll, TBool aDrawNow, TUint /*aFlags*/) +{ +#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK + //Dragging/flicking of Form content should give tactile feedback. + //This implementation is similar to native List: during dragging/flicking + //feedback is given when new item appears ont top/bottom of Form + //visible area. + // + //First we have to reset current visibility for all items in Form. + TInt count = iItems.Count(); + for (TInt i=0; i 0) + { + //scrolling up (pointer dragged down) + DoFeedbackOnDraggingUp(); + } + if (aScroll < 0) + { + //scrolling down (pointer dragged up) + DoFeedbackOnDraggingDown(); + } +#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK +} + +void CMIDForm::PhysicsScrollingEnd() +{ + iFlickOngoing = EFalse; + iPanningOngoing = EFalse; +} + +TBool CMIDForm::PhysicsEnabled() +{ + if (iPhysics) + { + return ETrue; + } + else + { + return EFalse; + } +} + +TBool CMIDForm::PhysicsScrolling() +{ + return iFlickOngoing | iPanningOngoing; +} + +TInt CMIDForm::ScrollDelta() +{ + return iScrollDelta; +} + +void CMIDForm::DoFeedbackOnFocusChange(CMIDControlItem& aControlItem) +{ + //Tapping on StringItem BUTTON should not do feedback, here. + //Native class CAknButton does it by itself + if (!IsStringItemButton(aControlItem)) + { + //Other items should do some feedback +#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK + if (IsStringItemHyperlink(aControlItem)) + { + //if StringItem is HYPERLIK it should give sensitive button feedback + iFeedback->InstantFeedback(ETouchFeedbackSensitiveButton); + } + else if (IsImageItem(aControlItem)) + { + //if pointed control is ImageItem, its focusable, it has to be either + //HYPERLIK ot BUTTON, so it should give sensitive button feedback + iFeedback->InstantFeedback(ETouchFeedbackSensitiveButton); + } + else if (IsChoiceGroup(aControlItem)) + { + //tapping on unfocused ChoiceGroup makes also activation, so it should + //give basic list feedback + iFeedback->InstantFeedback(ETouchFeedbackList); + } + else + { + //changing focus to other items should give sensitive list feedback + iFeedback->InstantFeedback(ETouchFeedbackSensitiveList); + } +#else + iFeedback->InstantFeedback(ETouchFeedbackBasic); +#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK + } +} + +#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK +void CMIDForm::DoFeedbackOnDraggingUp() +{ + TInt count = iItems.Count(); + //If dragging/flicking reaches the first/last item in Form, + //tactile feedback shouldn't be given. There should be only + //'bounce' effect, when first/last item goes back to to/bottom + //of screen. Flag firstOrLastItemReached determines this case. + //NOTE: feedback for 'bounce' is implemented in CAknPhysics. + TBool firstOrLastItemReached = EFalse; + for (TInt i = 0; i < count; i++) + { + //Try find first item from top, which changed its visibility + CMIDControlItem& ci = ControlItem(i); + CMIDControlItem& last = ControlItem(count-1); + TBool visibility = RectPartiallyVisible(ci.Rect()); + //In case of 'bounce' effect, there shouldn't be any feedback + //on dragging/flicking (as in native side): + if (RectFullyVisible(last.Rect())) + { + firstOrLastItemReached = ETrue; + } + if (i == 0 && RectFullyVisible(ci.Rect())) + { + firstOrLastItemReached = ETrue; + } + if (ci.GetVisibilityInForm() != visibility) + { + //item changed its visibility form invisible to visible + if (visibility && !firstOrLastItemReached) + { + //if there isn't 'bounce' effect, do feedback + iFeedback->InstantFeedback(ETouchFeedbackSensitiveList); + break; + } + } + } +} + +void CMIDForm::DoFeedbackOnDraggingDown() +{ + TInt count = iItems.Count(); + //If dragging/flicking reaches the first/last item in Form, + //tactile feedback shouldn't be given. There should be only + //'bounce' effect, when first/last item goes back to to/bottom + //of screen. Flag firstOrLastItemReached determines this case. + //NOTE: feedback for 'bounce' is implemented in CAknPhysics. + TBool firstOrLastItemReached = EFalse; + for (TInt i = count-1; i >= 0; i--) + { + CMIDControlItem& ci = ControlItem(i); + CMIDControlItem& first = ControlItem(0); + TBool visibility = RectPartiallyVisible(ci.Rect()); + if (RectFullyVisible(first.Rect())) + { + firstOrLastItemReached = ETrue; + } + if (i == count-1 && RectFullyVisible(ci.Rect())) + { + firstOrLastItemReached = ETrue; + } + if (ci.GetVisibilityInForm() != visibility) + { + //item changed its visibility form invisible to visible + if (visibility && !firstOrLastItemReached) + { + iFeedback->InstantFeedback(ETouchFeedbackSensitiveList); + break; + } + } + } +} +#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK