--- /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 <bldvariant.hrh>
+// using CEikScrollBarFrame API for iSBFrame
+#include <eiksbfrm.h>
+
+// API used for retrieving layout for initial alignment (in ConstructL)
+#include <centralrepository.h>
+// constants used in retrieving layout for initial alignment (in ConstructL)
+#include <AvkonInternalCRKeys.h>
+
+// control context that provides a layout background
+#include <AknsListBoxBackgroundControlContext.h>
+// highlight background
+#include <AknsFrameBackgroundControlContext.h>
+// API for text formatting used in DrawNoDataStringL function
+#include <AknBidiTextUtils.h>
+
+#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 <applayout.cdl.h>
+// LAF
+#include <aknlayoutscalable_avkon.cdl.h>
+// LAF
+#include <layoutmetadata.cdl.h>
+
+// Api for skin layout
+#include <skinlayout.cdl.h>
+using namespace SkinLayout;
+
+#include <j2me/jdebug.h>
+
+#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<MMIDItem*>& aItems)
+{
+ DEBUG("CMIDForm::SetAllItemsL");
+
+ ASSERT(iItems.Count() == 0);
+ //
+ TInt count = aItems.Count();
+ for (TInt i=0; i<count; i++)
+ {
+ User::LeaveIfError(iItems.Append(aItems[i]));
+ CMIDControlItem& ci = ControlItem(i);
+
+ AddItemToFormL(ci);
+ ci.MakeVisible(ETrue);
+ }
+
+ RequestLayoutL();
+}
+
+/**
+ * Replaces an item. Keeps the same item focused which had focus before,
+ * and if that's the one that this is replacing, then this will be focused
+ * if possible.
+ */
+void CMIDForm::SetItemL(MMIDItem& aItem,TInt aIndex)
+{
+ DEBUG("CMIDForm::SetItemL");
+
+ CMIDControlItem& oldCi = ControlItem(aIndex);
+ RemoveItemFromForm(oldCi);
+
+ iItems[aIndex] = &aItem;
+ CMIDControlItem& ci = ControlItem(aIndex);
+ AddItemToFormL(ci);
+
+ ci.MakeVisible(ETrue);
+
+ if (iFocused == aIndex)
+ {
+ iDisplayable.MenuHandler()->HideMenuIfVisible();
+
+ 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<CMIDStringItem*>(&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<CMIDLabelContainerItem*>(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<CMIDLabelContainerItem*>(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<CMIDStringItem*>(&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<CMIDLabelContainerItem*>(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<CMIDStringItem*>(&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<CMIDStringItem*>(&controlItem);
+ idx = FirstFocusableItemBeforeStartOfUnconstrainedStringItemIdx(*si);
+ return idx;
+ }
+ }
+ else
+ {
+ // direction ie ERight
+ if (IsStringItem(controlItem))
+ {
+ CMIDStringItem* si = static_cast<CMIDStringItem*>(&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<CMIDDisplayable&>(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<CMIDImageItem*>(aMMidItem);
+ break;
+ case MMIDComponent::EStringItem:
+ base = static_cast<CMIDStringItem*>(aMMidItem);
+ break;
+ case MMIDComponent::ESpacer:
+ base = static_cast<CMIDSpacer*>(aMMidItem);
+ break;
+ case MMIDComponent::EGauge:
+ base = static_cast<CMIDGaugeItem*>(aMMidItem);
+ break;
+ case MMIDComponent::ETextField:
+ base = static_cast<CMIDTextFieldItem*>(aMMidItem);
+ break;
+ case MMIDComponent::EChoiceGroup:
+ base = static_cast<CMIDChoiceGroupItem*>(aMMidItem);
+ break;
+ case MMIDComponent::EDateField:
+ base = static_cast<CMIDDateFieldItem*>(aMMidItem);
+ break;
+ case MMIDComponent::ECustomItem:
+ base = static_cast<CMIDCustomItem*>(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<CCoeControl*>(&(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<CCoeControl*>(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; i<count; i++)
+ {
+ if (iItems[i]->Type() == 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<CMIDTextFieldItem*>(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<CMIDLabelContainerItem*>(&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<CMIDLabelContainerItem*>(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<CMIDLabelContainerItem*>(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<CMIDLabelContainerItem*>(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<CMIDLabelContainerItem*>(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<CMIDSpacer*>(&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<CMIDChoiceGroupItem*>(&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<CMIDLabelContainerItem*>(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<CMIDGaugeItem*>(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<CMIDForm*>(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<count; i++)
+ {
+ CMIDControlItem& ci = ControlItem(i);
+ ci.SetVisibilityInForm(RectPartiallyVisible(ci.Rect()));
+ }
+#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+
+ iScroll = iScroll + aScroll;
+ if (aScroll)
+ {
+ iHasScrolled = ETrue;
+ }
+ iScrollDelta = aScroll;
+ SetRowPositionsWithRespectToScrolledLocation();
+
+ if (aDrawNow)
+ {
+ RawScrollFinalize(ETrue, ETrue);
+ DrawNow();
+ }
+ else
+ {
+ RawScrollFinalize(ETrue, EFalse);
+ }
+
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+ //Tactile feedback on dragging/flicking.
+ if (aScroll > 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