javauis/lcdui_akn/lcdui/src/CMIDChoiceGroupControl.cpp
branchRCL_3
changeset 19 04becd199f91
child 23 98ccebc37403
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javauis/lcdui_akn/lcdui/src/CMIDChoiceGroupControl.cpp	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,1815 @@
+/*
+* Copyright (c) 2003-2006 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:  The CMIDChoiceGroup control container. Owns and manipulates
+*                the choicegroup model and creates / deletes the actual
+*                control as needed.
+*
+*/
+
+
+#include <e32def.h>
+#include <e32err.h>
+#include <w32std.h>
+#include <gdi.h>
+#include <gulutil.h>
+
+#include <eiktxlbm.h>
+// CColumnListBoxData API
+#include <eikclbd.h>
+
+#include <aknenv.h>
+#include <aknconsts.h>
+#include <avkon.mbg>
+#include <aknPopup.h>
+#include <AknLayoutDef.h>
+#include <AknBidiTextUtils.h>
+#include <gulcolor.h>
+
+#include <skinlayout.cdl.h>
+#include <AknLayoutFont.h>
+#include <aknlayoutscalable_avkon.cdl.h> // LAF
+#include <AknsDrawUtils.h>// skin
+#include <AknsBasicBackgroundControlContext.h> //skin
+
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+#include <akntranseffect.h>
+#include <akntransitionutils.h>
+#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+
+// using MMIDBitmapImage API in NewElement and SetElement functions - setting icon to element
+#include <lcdgr.h>
+// macros definitions for resources
+#include <lcdui.rsg>
+#ifndef RD_SCALABLE_UI_V2
+#include "CMIDForm.h"
+#endif
+
+// API used in HandlePointerEventL function
+#include "CMIDDisplayable.h"
+
+#include "CMIDFont.h"
+// using CMIDUtils::CopyBitmapL in NewElement and SetElement functions - setting icon to element
+#include "CMIDUtils.h"
+#include "CMIDChoiceGroupControl.h"
+// needed for CMIDChoiceGroupItem API in several places
+#include "CMIDChoiceGroupItem.h"
+// using SetTitleL in DoPopupL function - setting popup title
+#include "CMIDItemLabel.h"
+// using GetPopupNoteController in ShowInfoPopup function - showing popup with text
+#include "CMIDPopupNoteController.h"
+
+
+#include <j2me/jdebug.h>
+
+// For disabling transition effects
+#include <aknlistloadertfx.h>
+#include <aknlistboxtfx.h>
+
+
+/** This macro is executed each time a trapped call returns an error code different than KErrNone */
+#undef  TRAP_INSTRUMENTATION_LEAVE
+#define TRAP_INSTRUMENTATION_LEAVE(aResult) DEBUG_INT2("In CMIDChoiceGroupControl.cpp, trapped method was called at line %D and got exception %D", __LINE__, aResult);
+
+
+// Constant used for calculation of ChoiceGroup, which is not in Form yet
+// (from eiklbx.cpp)
+const TInt KEikListBoxItemVGap = 6;
+
+
+CMIDChoiceGroupPopupList* CMIDChoiceGroupPopupList::NewL(CEikListBox* aListBox, TInt aCbaResource,
+        AknPopupLayouts::TAknPopupLayouts aType)
+{
+    CMIDChoiceGroupPopupList* self = new(ELeave) CMIDChoiceGroupPopupList;
+    CleanupStack::PushL(self);
+    self->ConstructL(aListBox, aCbaResource, aType);
+    CleanupStack::Pop(self);
+    return self;
+}
+
+CMIDChoiceGroupPopupList::~CMIDChoiceGroupPopupList()
+{
+}
+
+void CMIDChoiceGroupPopupList::ResizeList()
+{
+    CAknPopupList::SetupWindowLayout(AknPopupLayouts::EMenuUnknownColumnWindow);
+}
+
+#ifdef RD_SCALABLE_UI_V2
+void CMIDChoiceGroupPopupList::HandlePointerEventL(const TPointerEvent& aPointerEvent)
+{
+    if (aPointerEvent.iType == TPointerEvent::EButton1Down &&
+            !Rect().Contains(aPointerEvent.iPosition))
+    {
+        AttemptExitL(EFalse);
+    }
+    else
+    {
+        CAknPopupList::HandlePointerEventL(aPointerEvent);
+    }
+}
+#endif //RD_SCALABLE_UI_V2
+
+CMIDChoiceGroupPopupList::CMIDChoiceGroupPopupList()
+{
+}
+
+CMIDChoiceGroupControl::CMIDChoiceGroupControl(
+    MMIDChoiceGroup::TChoiceType aType,
+    CMIDChoiceGroupItem* aItem)
+        : iType(aType),
+        iItem(aItem)
+{
+}
+
+
+CMIDChoiceGroupControl::~CMIDChoiceGroupControl()
+{
+    // Do not allow the listbox data to delete the icon
+    // array. It will be deleted by the model.
+    DeleteListBox(iListBox);
+
+    // No need to delete iPopupList here. The popup list is created and deleted in DoPopupL().
+    delete iModel;
+}
+
+
+// Constructs the model and adds any given elements into it.
+// NOTE that the actual control is not yet created, it comes
+// to life when the item is added to a form (that has a window
+// for the control to draw in). Constructing and storing the model
+// outside the actual control enables elements to be manipulated
+// (in Java code) even when the ChoiceGroup is not (yet) added to a form.
+void CMIDChoiceGroupControl::ConstructL(
+    RArray<TPtrC>& aStringArray,
+    RArray<MMIDImage*>& aImageArray)
+{
+    // Create the listbox model
+    iModel = new(ELeave) CMIDChoiceGroupModel(iType);
+    iModel->ConstructL(iEikonEnv);
+
+    // Start observing model events
+    iModel->SetObserver(this);
+
+    // Stuff any given arrays in the model
+    PopulateModelL(aStringArray, aImageArray);
+
+    UpdateMargins();
+
+    SetComponentsToInheritVisibility(ETrue);
+
+#ifdef RD_SCALABLE_UI_V2
+    SetAllowStrayPointers();
+#endif
+
+#ifdef RD_TACTILE_FEEDBACK
+    iFeedback = MTouchFeedback::Instance();
+    // Disabling default tactile feedback to prevent a 'double feedback' while tapping on focused item
+    iFeedback->EnableFeedbackForControl(this, FALSE);
+#endif
+
+}
+
+
+// Creates the actual choicegroup control, sets all kinds of
+// properties and associates the model with the created control
+void CMIDChoiceGroupControl::CreateControlL(
+    const CCoeControl* aParent,
+    const TRect& aRect)
+{
+    // Model should be created before the control (this is true for
+    // popup as well)
+    ASSERT(iModel);
+
+    // In dynamic resolution case store the current item index, so that it can be
+    // restored once the listbox is recreated
+    TInt currentItem = KErrNotFound;
+    if (iListBox)
+    {
+        currentItem = iListBox->View()->CurrentItemIndex();
+    }
+
+    DeleteControl();
+    ASSERT(!iListBox);
+
+    // Set parent to the existing (form) window and use the rect given
+    SetContainerWindowL(*aParent);
+    SetRect(aRect);
+
+    UpdateMargins();
+
+    // If this is a popup, there's nothing to else create really
+    if (IsPopup())
+    {
+        return;
+    }
+
+    //in case skin or resolution has changed
+    iModel->ReConstructSelectionIconsL();
+    iModel->UpdateIconArrayL();
+
+    iListBox = CreateListBoxL(aParent);
+
+    // Disable transition effects for iListBox, this is needed to get
+    // rid of artifacts caused by transition effects when list box controls
+    // are scrolled up/down.
+    CWindowGc* gc = iListBox->View()->ItemDrawer()->Gc();
+    MAknListBoxTfx* transApi = CAknListLoader::TfxApi(gc);
+    if (transApi)
+    {
+        transApi->EnableEffects(EFalse);
+    }
+
+    // setup listbox
+    iListBox->SetListBoxObserver(this);
+    iListBox->SetRect(ListBoxRect(Rect()));
+
+    if (currentItem != KErrNotFound)
+    {
+        iListBox->View()->SetCurrentItemIndex(currentItem);
+    }
+
+    // Ready to draw
+    ActivateL();
+}
+
+
+// Deletes the actual listbox control (when item is removed from a form)
+// and disassociates the model from the control.
+// Maintains the model so that elements can be accessed even
+// if the item is not attached to a form
+void CMIDChoiceGroupControl::DeleteControl()
+{
+    ClosePopup();
+
+    // Gets rid of the listbox when choicegroup is removed from
+    // a form, but does not yet kill the model (ie elements that
+    // may have been added)
+    DeleteListBox(iListBox);
+
+    // Set to null, otherwise destructor will double-delete
+    iListBox = NULL;
+}
+
+
+void CMIDChoiceGroupControl::HiliteFirstElement(TBool aEnableHighlight)
+{
+    if (iListBox)
+    {
+        iListBox->View()->SetCurrentItemIndex(0);
+        EnableListHighlight(aEnableHighlight);
+    }
+}
+
+void CMIDChoiceGroupControl::HiliteLastElement(TBool aEnableHighlight)
+{
+    if (iListBox)
+    {
+        iListBox->View()->SetCurrentItemIndex(iListBox->View()->BottomItemIndex());
+        EnableListHighlight(aEnableHighlight);
+    }
+}
+
+void CMIDChoiceGroupControl::EnableListHighlight(TBool aEnable)
+{
+    if (iListBox)
+    {
+        CListItemDrawer* drawer = iListBox->View()->ItemDrawer();
+
+        if (aEnable)
+        {
+            drawer->ClearFlags(CListItemDrawer::EDisableHighlight);
+        }
+        else
+        {
+            drawer->SetFlags(CListItemDrawer::EDisableHighlight);
+
+#ifdef RD_SCALABLE_UI_V2
+            // If the focus is dragged to another form item, the highlight is
+            // disabled in this cg. Set the flag to true to be sure that selection is not
+            // changed if the pointer up happens on same cg element where the drag started.
+            iPointerDragHasStarted = ETrue;
+#endif
+        }
+
+        TRAP_IGNORE(iListBox->View()->UpdateSelectionL(CListBoxView::ESingleSelection));
+    }
+}
+
+// msk
+void CMIDChoiceGroupControl::CurrentItemChanged()
+{
+    if (iItem)
+    {
+        // delegate the information to the item; commands may need to be updated
+        iItem->UpdateCommands();
+    }
+    // redraw
+    DrawNow();
+}
+
+// msk
+TBool CMIDChoiceGroupControl::IsCurrentItemSelected()
+{
+    if (iListBox)
+    {
+        return IsSelected(iListBox->View()->CurrentItemIndex());
+    }
+
+    return EFalse;
+}
+
+// msk
+// Posts always the java event
+void CMIDChoiceGroupControl::ToggleCurrentItemSelectionL()
+{
+    if (iListBox)
+    {
+        // SelectElementL must not be called if the unselection of the selected element is
+        // attempted in exclusive choice group because it would cause the itemStateChanged event.
+        if (iType != MMIDChoiceGroup::EExclusive || !IsCurrentItemSelected())
+        {
+#ifdef RD_JAVA_S60_RELEASE_9_2
+            if (Form() && !Form()->PhysicsScrolling())
+            {
+#endif // RD_JAVA_S60_RELEASE_9_2             
+                TInt currentIndex = iListBox->View()->CurrentItemIndex();
+                SelectElementL(currentIndex, !IsSelected(currentIndex), ETrue);
+#ifdef RD_JAVA_S60_RELEASE_9_2
+            }
+#endif // RD_JAVA_S60_RELEASE_9_2
+        }
+    }
+}
+
+// returns the numbers of elements in this choice group
+TInt CMIDChoiceGroupControl::NumberOfElements() const
+{
+    ASSERT(iModel);
+    return iModel->NumberOfItems();
+}
+
+// --- CMIDChoiceGroupItem delegated methods (java Choice interface)
+
+
+// Inserts a new element in the choice
+void CMIDChoiceGroupControl::InsertElementL(TInt aIndex, const TDesC& aText, MMIDImage* aImage)
+{
+    ASSERT(iModel);
+
+    CMIDChoiceGroupElement* pElement = NewElementLC(aText, aImage);
+
+    // Reserve space for the new element
+    iModel->SetReserveL(iModel->NumberOfItems() + 1);
+
+    // Stuff it in
+    iModel->InsertElementL(aIndex, pElement);
+
+    CListBoxView* view = NULL;
+
+    if (iListBox)
+    {
+        view = iListBox->View();
+    }
+    else if (iPopupList)
+    {
+        view = iPopupList->ListBox()->View();
+    }
+
+    if (view)
+    {
+        TInt current = view->CurrentItemIndex();
+
+        if (current >= aIndex && current < (iModel->NumberOfItems() - 1))
+        {
+            // If element was inserted before the highlighted element,
+            // move the highlight so that it stays in the same element
+            // as before insertion.
+            view->SetCurrentItemIndex(++current);
+        }
+    }
+
+    CleanupStack::Pop(pElement);
+}
+
+
+// Deletes an element from the choice
+void CMIDChoiceGroupControl::DeleteElementL(TInt aIndex)
+{
+    ASSERT(iModel);
+    TInt current = 0;
+    if (iListBox)
+    {
+        current = iListBox->View()->CurrentItemIndex();
+    }
+    iModel->DeleteElementL(aIndex);
+
+    if (iListBox && current != KErrNotFound &&
+            iModel->NumberOfItems() > 0 &&
+            aIndex <= current)
+    {
+        if (current < iModel->NumberOfItems())
+        {
+            if (current > 0 && aIndex < current)
+            {
+                // Element was deleted before the current item, decrease
+                // current so that highlight stays in the same element
+                // as before the deletion
+                --current;
+            }
+            iListBox->SetCurrentItemIndex(current);
+        }
+        else
+        {
+            iListBox->SetCurrentItemIndex(iModel->NumberOfItems()-1);
+        }
+    }
+
+}
+
+
+// Deletes all elements
+void CMIDChoiceGroupControl::DeleteAllL()
+{
+    ASSERT(iModel);
+    if (iListBox)
+    {
+        iListBox->SetCurrentItemIndex(0);
+    }
+    iModel->DeleteAllL();
+}
+
+
+// Sets properties of element at <aIndex>
+void CMIDChoiceGroupControl::SetElementL(TInt aIndex, const TDesC& aText, MMIDImage* aImage)
+{
+    ASSERT(iModel);
+
+    if (aImage)
+    {
+        MMIDBitmapImage* bitmapImage = aImage->BitmapImage();
+
+        CFbsBitmap* bitmap = CMIDUtils::CopyBitmapL(bitmapImage->ColorBitmap());
+        CleanupStack::PushL(bitmap);
+
+        CFbsBitmap* mask = NULL;
+        if (bitmapImage->AlphaBitmap())
+        {
+            TDisplayMode alphaDisplayMode = bitmapImage->AlphaBitmap()->DisplayMode();
+            TBool invert = alphaDisplayMode == EGray2 ? ETrue : EFalse;
+            mask = bitmapImage->CreateAlphaBitmapL(alphaDisplayMode, invert);
+        }
+
+        CleanupStack::PushL(mask);
+
+        iModel->SetElementL(aIndex, aText, bitmap, mask);
+
+        CleanupStack::Pop(mask);
+        CleanupStack::Pop(bitmap);
+
+        bitmapImage->RemoveRef();
+    }
+    else
+    {
+        iModel->SetElementL(aIndex, aText, NULL, NULL);
+    }
+}
+
+
+// Sets selection state of element at <aIndex>
+// This method is also declared in MMIDChoiceGroup. It's implementation
+// in CMIDChoiceGroupItem just simply calls this method.
+void CMIDChoiceGroupControl::SelectElementL(TInt aIndex, TBool aSelected)
+{
+    SelectElementL(aIndex, aSelected, EFalse);
+}
+
+
+// Returns selection state of element at <aIndex>
+TBool CMIDChoiceGroupControl::IsSelected(TInt aIndex)
+{
+    ASSERT(iModel);
+    CMIDChoiceGroupElement* pElement = iModel->ElementAt(aIndex);
+
+    if (pElement)
+    {
+        return pElement->IsSelected();
+    }
+
+    return EFalse;
+}
+
+
+// Sets element text font
+void CMIDChoiceGroupControl::SetFontL(TInt /*aIndex*/, MMIDFont* /*aFont*/)
+{
+    ASSERT(iModel);
+}
+
+
+// Sets element text fit policy
+void CMIDChoiceGroupControl::SetFitPolicyL(TInt /*aFitPolicy*/)
+{
+    ASSERT(iModel);
+}
+
+
+// --- from MMIDChoiceGroupModelObserver ---
+
+// Called when the model has changed. Based on the change, will cause
+// just a redraw of the control, or size recalculation
+void CMIDChoiceGroupControl::HandleChoiceGroupModelEventL(
+    CMIDChoiceGroupModel* aModel,
+    TChoiceGroupModelEvent aEvent)
+{
+    ASSERT(iModel);
+
+    // If not from our model, ignore
+    if (aModel != iModel)
+    {
+        return;
+    }
+
+    // Assume everything needs to be done (makes the switch shorter)
+    TBool bRedraw = ETrue;
+    TBool bResize = ETrue;
+
+    // Act upon the event, resize and redraw control as necessary
+    switch (aEvent)
+    {
+    case EElementAdded:
+        if (iListBox)
+        {
+            iListBox->HandleItemAdditionL();
+        }
+        break;
+    case EElementDeleted:
+        if (iListBox)
+        {
+            iListBox->HandleItemRemovalL();
+        }
+        break;
+    case EUpdateEnded:
+        if (iListBox)
+        {
+            iListBox->Reset();
+        }
+        break;
+    case EElementModified:
+        // after the LAF changes the icon column is dynamic, so redo the setup for the listbox
+        // the columns by calling SizeChanged(). bResize is set to false because the relayout of
+        // of the whole form is not needed.
+        SizeChanged();
+        bResize = EFalse;
+        break;
+    case EElementSelected:
+        // Just a redraw will do
+        bResize = EFalse;
+        break;
+    case EUpdateStarted:
+    default:
+        // No redraw, no resize. Do nothing.
+        bRedraw = EFalse;
+        bResize = EFalse;
+        break;
+    }
+
+    // If popup is opened, it is handled separately
+    if (iPopupList)
+    {
+        UpdatePopupListAppearanceL(aEvent);
+    }
+
+    // Reporting ESizeChanged event to iItem is not needed since the request
+    // to update the form comes from java side (CMIDForm::RefreshItem(TInt) is called)
+    // when items are added to or removed from choice group. See ChoiceGroup.java.
+    // Redraw if needed and size has not changed, size change will
+    // cause a form layout and redraw anyway.
+    if (bRedraw && !bResize)
+    {
+        // Do safe redraw
+        if (!IsPopup() && iListBox && iListBox->DrawableWindow())
+        {
+            iListBox->DrawDeferred();
+        }
+        else
+        {
+            // Seems to be a popup - redraw myself
+            if (DrawableWindow())
+            {
+                DrawDeferred();
+            }
+        }
+    }
+}
+
+
+// --- from MEikListBoxObserver ---
+// Handles choice listbox events (as in selection)
+void CMIDChoiceGroupControl::HandleListBoxEventL(CEikListBox* aControl, TListBoxEvent aEventType)
+{
+    if (aControl != iListBox)
+    {
+        return;
+    }
+
+    ASSERT(iModel);
+
+    switch (aEventType)
+    {
+        // These should all cause a selection change
+    case MEikListBoxObserver::EEventEnterKeyPressed:
+    case MEikListBoxObserver::EEventItemActioned:
+        ToggleCurrentItemSelectionL();
+        break;
+
+    default:
+        break;
+    }
+}
+
+
+// Offers key events to the listbox, or handles popup events here
+TKeyResponse CMIDChoiceGroupControl::OfferKeyEventL(
+    const TKeyEvent& aKeyEvent,
+    TEventCode aType)
+{
+    TKeyResponse resp = EKeyWasNotConsumed;
+
+    // Pop popup if needed, otherwise do nothing (if popup)
+    if (IsPopup())
+    {
+        if ((aKeyEvent.iCode == EKeyOK) || (aKeyEvent.iCode == EKeyEnter))
+        {
+            // It's a popup, and the OK key was pressed on it - start poppin'
+            DoPopupL();
+            resp = EKeyWasConsumed;
+        }
+
+        return resp;
+    }
+
+    // Not a popup then, handle some keys in clever ways
+
+    // Save the currently selected item (if exclusive choice)
+    TInt oldSelected = iModel->SelectedElement();
+    TInt oldCurrentIndex = -1;
+
+    if (iListBox)
+    {
+        oldCurrentIndex = iListBox->View()->CurrentItemIndex();
+        // Let the listbox take a shot at the key
+        resp = iListBox->OfferKeyEventL(aKeyEvent, aType);
+    }
+
+    // If click (enter) on an already selected item in an exclusive choice,
+    // do not consume the key. This allows the form to display a context menu
+    if ((iType == MMIDChoiceGroup::EExclusive) &&
+            ((aKeyEvent.iCode == EKeyOK) || (aKeyEvent.iCode == EKeyEnter)) &&
+            ((oldSelected != -1) || (oldSelected == -1 && oldCurrentIndex == -1)) &&
+            (oldSelected == iModel->SelectedElement()))
+    {
+        // Do not consume the key, so that form can pop a menu
+        CMIDDisplayable& displayable = iItem->Form()->CurrentDisplayable();
+        TInt cntOpt = displayable.NumCommandsForOkOptionsMenu();
+
+        // set first command from command list,
+        // if command list <= 0 then command sets NULL
+        const CMIDCommand* command = (iItem->CommandList()->Count() > 0 ?
+                                      iItem->CommandList()->At(0).iCommand :
+                                      NULL);
+
+        // for default command will run ProcessCommandL( KItemCommandIdBase + 1 )
+        if (iItem->DefaultCommand())
+        {
+            displayable.ProcessCommandL(iItem->CommandList()->CommandOffset() +
+                                        iItem->CommandList()->FindCommandIndex(iItem->DefaultCommand()));
+
+            resp =  EKeyWasConsumed;
+        }
+        else
+        {
+            // if ( cntOpt > 1 ) will run menu, else execute ProcessCommandL( CommandOffset )
+            if (cntOpt > 1)
+            {
+                displayable.MenuHandler()->ShowMenuL(CMIDMenuHandler::EOkMenu);
+                resp = EKeyWasConsumed;
+            }
+            else if (command && command->CommandType() != MMIDCommand::EBack &&
+                     command->CommandType() != MMIDCommand::ECancel)
+            {
+                displayable.ProcessCommandL(iItem->CommandList()->CommandOffset());
+                resp =  EKeyWasConsumed;
+            }
+            else
+            {
+                displayable.ProcessCommandL(displayable.MainCommandList()->CommandOffset());
+                resp =  EKeyWasConsumed;
+            }
+        }
+    }
+    // If the event caused current item change, and the new current item
+    // is outside the visible area, should request the form to scroll a bit.
+    // Current item has not changed, if the key was not consumed
+    if (((aKeyEvent.iCode == EKeyUpArrow) ||
+            (aKeyEvent.iCode == EKeyDownArrow)) &&
+            (resp != EKeyWasNotConsumed))
+    {
+        // Calc scroll need and request one, if necessary
+        RequestScrollIfNeededL();
+    }
+
+    if (iListBox && (oldCurrentIndex != iListBox->View()->CurrentItemIndex()))
+    {
+        CurrentItemChanged();
+#ifdef RD_JAVA_S60_RELEASE_9_2
+        ShowInfoPopup();
+#endif // RD_JAVA_S60_RELEASE_9_2        
+    }
+    return resp;
+}
+
+#ifdef RD_SCALABLE_UI_V2
+void CMIDChoiceGroupControl::HandlePointerEventL(const TPointerEvent& aPointerEvent)
+{
+    if (!AknLayoutUtils::PenEnabled() || !iListBox)
+    {
+
+#ifdef RD_TACTILE_FEEDBACK
+        // this is to give tactile feedback when a POPUP type CG is focused
+        // and user taps it to open the popup list
+        // if focus is changing, tactile feedback is given already in Form
+        if (IsPopup() &&
+                aPointerEvent.iType == TPointerEvent::EButton1Down &&
+                !iItem->Form()->IsFocusChangingWithPen())
+        {
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+            iFeedback->InstantFeedback(ETouchFeedbackList);
+#else
+            iFeedback->InstantFeedback(ETouchFeedbackBasic);
+#endif // RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+        }
+#endif // RD_TACTILE_FEEDBACK
+
+        return;
+    }
+
+    TInt nOldCurrent = iListBox->View()->CurrentItemIndex();
+    TBool oldSelected = IsSelected(nOldCurrent);
+
+    switch (aPointerEvent.iType)
+    {
+    case TPointerEvent::EDrag:
+        // In order to enable highlight moving inside the choice group when the focus is dragged
+        // on the form, forward drag events directly to listbox. This is needed beacuse it is not
+        // always possible to set the wanted grabbing state for CMIDChoiceGroupListBox.
+        // See also CCoeControl implementation of pointer event handling.
+        iListBox->HandlePointerEventL(aPointerEvent);
+        break;
+    case TPointerEvent::EButtonRepeat:
+        // EButtonRepeat events are requested by form. These events come only when pointer
+        // is dragged out of main pane area.
+        TimedScroll(aPointerEvent.iPosition.iY < 0 ? CMIDForm::EUp : CMIDForm::EDown);
+        break;
+    case TPointerEvent::EButton1Down:
+    {
+#ifdef RD_JAVA_S60_RELEASE_9_2
+        // Hide pop-up note if visible.
+        CMIDForm* form = iItem->Form();
+        if (form)
+        {
+            form->GetPopupNoteController()->HidePopup();
+        }
+#endif // RD_JAVA_S60_RELEASE_9_2
+
+        iPointerDragHasStarted = EFalse;
+
+#ifdef RD_TACTILE_FEEDBACK
+        // give tactile feedback here on ListBox only if it is already focused
+        // if focus is changing, tactile feedback is given already in Form
+        if (!iItem->Form()->IsFocusChangingWithPen())
+        {
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+            iFeedback->InstantFeedback(ETouchFeedbackList);
+#else
+            iFeedback->InstantFeedback(ETouchFeedbackBasic);
+#endif // RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+        }
+#endif // RD_TACTILE_FEEDBACK
+        CCoeControl::HandlePointerEventL(aPointerEvent);
+
+        if (iListBox->Rect().Contains(aPointerEvent.iPosition) &&
+                !iItem->Form()->IsScrolledOnPointerDown())
+        {
+            TRect mainPane;
+            AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainPane);
+
+            TRect rect = iListBox->HighlightRect(); // highlight rect is in propotion to screen,
+            rect.Move(0, -mainPane.iTl.iY);         // move it so that it's in propotion to main pane
+
+            // Scroll the form so that highlighted list element becomes fully visible
+            TInt scroll = 0;
+            if (rect.iBr.iY > mainPane.Height())
+            {
+                scroll = mainPane.Height() - rect.iBr.iY;
+            }
+            else if (rect.iTl.iY < 0)
+            {
+                scroll = -rect.iTl.iY;
+            }
+
+            if (scroll != 0)
+            {
+                ReportEventL(MMIDChoiceGroupControlObserver::EScrollRequest,
+                             (TAny*)scroll);
+            }
+        }
+        break;
+    }
+    case TPointerEvent::EButton1Up:
+    {
+#ifdef RD_JAVA_S60_RELEASE_9_2
+        // If up event comes when long tap has been detected, then
+        // just forward the pointer event. In that case we do not
+        // want to change selection, but update the listbox visually.
+        // This is performed when pop-up menu is closed and we want to
+        // hide listbox highlight.
+        if (iItem->LongTapDetected())
+        {
+            CCoeControl::HandlePointerEventL(aPointerEvent);
+            return;
+        }
+#endif // RD_JAVA_S60_RELEASE_9_2
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+        switch (iType)
+        {
+        case MMIDChoiceGroup::EMultiple:
+            iFeedback->InstantFeedback(ETouchFeedbackCheckbox);
+            break;
+        default:
+            iFeedback->InstantFeedback(ETouchFeedbackList);
+            break;
+        }
+#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+
+        CCoeControl::HandlePointerEventL(aPointerEvent);
+
+        TInt idx;
+
+        // Allow toggling the selection also in a case when form has been scrolled on pointer down event,
+        // if the pointer up happens still on cg item area.
+        TBool toggleAfterScroll = iItem->Form()->IsScrolledOnPointerDown() &&
+                                  iItem->Rect().Contains(aPointerEvent.iPosition);
+
+        if (!iPointerDragHasStarted &&
+                (iListBox->View()->XYPosToItemIndex(aPointerEvent.iPosition, idx) ||
+                 toggleAfterScroll))
+        {
+            ToggleCurrentItemSelectionL();
+        }
+
+        // If selected item is tapped in the Exclusive ChoiceGroup
+        // then possible default Command is executed.
+        if (iItem->DefaultCommand() && !iItem->Form()->PhysicsScrolling())
+        {
+            if (iType == MMIDChoiceGroup::EExclusive && oldSelected
+                    && !iFocusChangingWithPen && !iItem->Form()->IsFocusChangingWithPen())
+            {
+                iItem->Form()->CurrentDisplayable().ProcessCommandL(iItem->DefaultCommand());
+            }
+        }
+
+        iFocusChangingWithPen = EFalse;
+        break;
+    }
+    default:
+        CCoeControl::HandlePointerEventL(aPointerEvent);
+        break;
+    }
+
+    // msk: notify if the current index has changed
+    if (nOldCurrent != iListBox->View()->CurrentItemIndex())
+    {
+        iFocusChangingWithPen = ETrue;
+        CurrentItemChanged();
+        if (aPointerEvent.iType == TPointerEvent::EDrag)
+        {
+            iPointerDragHasStarted = ETrue;
+        }
+    }
+}
+
+#endif //RD_SCALABLE_UI_V2
+
+// Count component controls in this compound control
+TInt CMIDChoiceGroupControl::CountComponentControls() const
+{
+    if (iListBox)
+        return 1;
+
+    return 0;
+}
+
+
+// Return requested component control
+CCoeControl* CMIDChoiceGroupControl::ComponentControl(TInt /*aIndex*/) const
+{
+    return iListBox;
+}
+
+
+/** Called when the size adjustment need is detected, the item notified and
+    the item caused the form to relayout. The form will calc the new
+    position of the item and cause sizechanged to be called - this is
+    this controls chance to update its position.
+
+*/
+void CMIDChoiceGroupControl::SizeChanged()
+{
+    if (iListBox)
+    {
+        iListBox->SetRect(ListBoxRect(Rect()));
+        TRAP_IGNORE(SetupColumnsL(iListBox->ItemDrawer()->ColumnData()));
+    }
+
+    if (IsPopup())
+    {
+        TAknLayoutRect layoutRect; // used only temporarily for getting the layout for popup item's icons and text
+
+        // Rect() includes also the margins so it cannot be used directly when layouting the popup icons and text.
+        // popupRect is the rect that is available for the closed popup item (i.e. it does not include the margins).
+        // popupRect is used as a parent rect when updating the rects of the popup icons and text.
+        layoutRect.LayoutRect(Rect(),
+                              AknLayoutScalable_Avkon::form2_midp_field_choice_group_pane().LayoutLine());
+        TRect popupRect = layoutRect.Rect();
+
+        // Layout for the element icon
+        layoutRect.LayoutRect(popupRect,
+                              AknLayoutScalable_Avkon::list_single_midp_graphic_pane_g4_cp(1).LayoutLine());
+        iPopupIconRect = layoutRect.Rect();
+
+        // Layout for the text
+
+        iPopupTextLayout.LayoutText(popupRect,
+                                    AknLayoutScalable_Avkon::list_single_midp_graphic_pane_t1_cp(1).LayoutLine());
+    }
+}
+
+void CMIDChoiceGroupControl::PositionChanged()
+{
+    if (IsPopup())
+    {
+        SizeChanged();
+    }
+}
+
+// Calculates and returns the minimum size required by the control
+// The size is based on the number of items in the listbox, so that
+// the listbox does not need to be scrolled. In popup mode the height
+// is fixed.
+TSize CMIDChoiceGroupControl::MinimumSize() const
+{
+    // Start with the current size
+    TSize size = Size();
+
+    TInt height = 0;
+
+    // Get height of list box, if not popup, and there is one
+    if (!IsPopup())
+    {
+        // Get current number of list box items.
+        TInt numberOfItems = iModel->NumberOfItems();
+
+        if (iListBox)
+        {
+            // Use item count to calculate listbox height
+            height += iListBox->CalcHeightBasedOnNumOfItems(numberOfItems);
+        }
+        else
+        {
+            // If there is no iListBox, this cg is not added to form yet.
+            // In this case calculate the best estimate for the list box height
+            // here.
+            // Because none of layout seems to be suitable for this
+            // calculation, we have to do it in the same way as CEikLisBox
+            // (see CEikListBox::CalcHeightBasedOnNumOfItems in eiklbx.cpp).
+            height += (iEikonEnv->NormalFont()->HeightInPixels()
+                       + KEikListBoxItemVGap) * numberOfItems;
+        }
+
+        height += (numberOfItems > 0)
+                  ? (iContentMargins.iTop + iContentMargins.iBottom)
+                  : iContentMargins.iBottom;
+    }
+    else // It is a popup
+    {
+        TAknLayoutRect layoutRect;
+        // layout in respect of the screen as only the height is needed
+        layoutRect.LayoutRect(iEikonEnv->ScreenDevice()->SizeInPixels(),
+                              AknLayoutScalable_Avkon::
+                              list_single_midp_graphic_pane_cp(1, 0).LayoutLine());
+        height += layoutRect.Rect().Height()
+                  + iContentMargins.iTop + iContentMargins.iBottom;
+    }
+
+    // Adjust the size (height only) accordingly
+    size.iHeight = height;
+
+    return size;
+}
+
+void CMIDChoiceGroupControl::Draw(const TRect& /*aRect*/) const
+{
+    if (IsPopup())
+    {
+        CMIDChoiceGroupElement* pElement = iModel->ElementAt(iModel->SelectedElement());
+
+        if (pElement)
+        {
+            DrawIcon(pElement->Icon());
+            DrawText(pElement->Text());
+        }
+    }
+}
+
+// Responds to focus change.
+// From CCoeControl.
+void CMIDChoiceGroupControl::FocusChanged(TDrawNow aDrawNow)
+{
+    // call default CCoeControl's implementation
+    CCoeControl::FocusChanged(aDrawNow);
+
+    // update focus state of listbox regarding to current focus state
+    if (iListBox)
+    {
+        iListBox->SetFocus(IsFocused(), aDrawNow);
+    }
+}
+
+// Handles resource change.
+// From CCoeControl.
+void CMIDChoiceGroupControl::HandleResourceChange(TInt aType)
+{
+    // call default CCoeControl's implementation
+    CCoeControl::HandleResourceChange(aType);
+
+    // update listbox focus state when layout is switched
+    if (aType == KEikDynamicLayoutVariantSwitch && iListBox)
+    {
+        iListBox->SetFocus(IsFocused());
+    }
+}
+
+TTypeUid::Ptr CMIDChoiceGroupControl::MopSupplyObject(TTypeUid aId)
+{
+    return iItem->MopSupplyObject(aId);
+}
+
+// Closes the popup if it is open
+void CMIDChoiceGroupControl::ClosePopup()
+{
+    if (iPopupList)
+    {
+        iPopupList->CancelPopup();
+    }
+}
+
+// --- private functions ---
+
+// Draws an icon in the popup control using LAF as much as possible
+void CMIDChoiceGroupControl::DrawIcon(CGulIcon* aIcon) const
+{
+    // Sanity check
+    if (!aIcon)
+    {
+        return;
+    }
+
+    // Extract parts - they're needed later anyway
+    CFbsBitmap* bmp = aIcon->Bitmap();
+    CFbsBitmap* mask = aIcon->Mask();
+
+    CWindowGc& gc = SystemGc();
+    TRect iconRect = iPopupIconRect;
+
+    // Draw the icon centered in its rect
+
+    // Position in icon is (0,0), if the image fits. Else there's a
+    // need for a bit of recalc
+    TPoint posInIcon = TPoint(0, 0) + (bmp->SizeInPixels() - iconRect.Size());
+    posInIcon.iX /= 2;
+    posInIcon.iY /= 2;
+
+    // If the thing is negative, the icon fits. Need to move the drawing
+    // rect a bit to make the icon draw in the middle
+    if (posInIcon.iX < 0)
+    {
+        // Shift right by amount left over
+        iconRect.iTl.iX -= posInIcon.iX;
+        posInIcon.iX = 0;
+    }
+
+    if (posInIcon.iY < 0)
+    {
+        // Shift down by amount left over
+        iconRect.iTl.iY -= posInIcon.iY;
+        posInIcon.iY = 0;
+    }
+
+    // Set the part of icon that is to be drawn
+    TRect drawRect(posInIcon, iconRect.Size());
+
+    // Draw it
+    if (mask)
+    {
+        gc.BitBltMasked(iconRect.iTl, bmp, drawRect, mask, ETrue);
+    }
+    else
+    {
+        gc.BitBlt(iconRect.iTl, bmp, drawRect);
+    }
+}
+
+
+/** Draws the selected item text in the popup control */
+void CMIDChoiceGroupControl::DrawText(const TDesC& aText) const
+{
+    if (aText.Length())
+    {
+        TRgb rgb = AKN_LAF_COLOR(215);
+        AknsUtils::GetCachedColor(AknsUtils::SkinInstance(),
+                                  rgb,KAknsIIDQsnTextColors, EAknsCIQsnTextColorsCG8);
+        iPopupTextLayout.DrawText(SystemGc(), aText, ETrue, rgb);
+    }
+}
+
+
+// Populates the listbox model from given arrays
+void CMIDChoiceGroupControl::PopulateModelL(
+    RArray<TPtrC>& aStringArray,
+    RArray<MMIDImage*>& aImageArray)
+{
+    if (!iModel)
+    {
+        // Should not happen.
+        ASSERT(EFalse);
+        return;
+    }
+
+    // Begin model update. The model defers updating
+    // some of its internal stuff until EndUpdate()
+    iModel->BeginUpdate();
+
+    // Loop through the string & image arrays and add elements,
+    // pass model from here so that it doesn't need to be retrieved
+    // from the box all the time
+    TInt nCount = aStringArray.Count();
+    for (TInt i = 0; i < nCount; i++)
+    {
+        iModel->AppendElementL(NewElementLC(aStringArray[i], aImageArray[i]));
+        CleanupStack::Pop();  // NewElementLC
+    }
+
+    // End model update
+    iModel->EndUpdate();
+}
+
+// Create a new element, initialise with string & image
+CMIDChoiceGroupElement* CMIDChoiceGroupControl::NewElementLC(
+    const TDesC& aString, MMIDImage* aImage)
+{
+    CMIDChoiceGroupElement* pElement = CMIDChoiceGroupElement::NewL();
+    CleanupStack::PushL(pElement);
+
+    pElement->SetTextL(aString);
+
+    if (aImage)
+    {
+        MMIDBitmapImage* bitmapImage = aImage->BitmapImage();
+
+        CFbsBitmap* bitmap = CMIDUtils::CopyBitmapL(bitmapImage->ColorBitmap());
+        CleanupStack::PushL(bitmap);
+
+        CFbsBitmap* mask = NULL;
+        if (bitmapImage->AlphaBitmap())
+        {
+            TDisplayMode alphaDisplayMode = bitmapImage->AlphaBitmap()->DisplayMode();
+            TBool invert = alphaDisplayMode == EGray2 ? ETrue : EFalse;
+            mask = bitmapImage->CreateAlphaBitmapL(alphaDisplayMode, invert);
+        }
+
+        CleanupStack::PushL(mask);
+
+        pElement->SetIconL(*bitmap, mask);
+        CleanupStack::Pop(mask);
+        CleanupStack::Pop(bitmap);
+
+        bitmapImage->RemoveRef();
+    }
+
+    return pElement;
+}
+
+
+// Reports control events to observer
+void CMIDChoiceGroupControl::ReportEventL(
+    MMIDChoiceGroupControlObserver::TChoiceGroupControlEvent aEvent,
+    TAny* aParam /* == NULL */)
+{
+    if (iObserver)
+    {
+        iObserver->HandleChoiceGroupControlEventL(this, aEvent, aParam);
+    }
+}
+
+
+// Calculates whether scrolling the current lbox item into view
+// is in order, and requests a scroll from the form (informs item
+// of a svcroll request event) if necessary
+void CMIDChoiceGroupControl::RequestScrollIfNeededL()
+{
+    // Scroll should never be needed in a popup CG, and definitely
+    // not, if there is no listbox
+    if ((IsPopup()) || (!iListBox))
+    {
+        return;
+    }
+
+    // Start with the form rect
+    TRect formRect = iItem->FormRect();
+
+    if (formRect.IsEmpty())
+    {
+        return;
+    }
+
+    // Calculate current listbox item rect
+    TInt currentItem = iListBox->CurrentItemIndex();
+    TRect lbitemRect = TRect(
+                           iListBox->View()->ItemPos(currentItem),
+                           iListBox->View()->ItemSize(currentItem));
+
+    // CG item rect is also needed
+    TRect cgitemRect = iItem->Rect();
+
+    // Amount to be scrolled
+    TInt nScroll = 0;
+
+    // If the current item is even partly outside the form, should
+    // scroll. Scroll amount is either the selected item border to
+    // middle of form rect, or the rest of the CG visible, whichever is
+    // smaller.
+    if (lbitemRect.iBr.iY > formRect.iBr.iY)
+    {
+        // Items bottom is below the form bottom - need to scroll up (-)
+        nScroll = Min((lbitemRect.iTl.iY - (formRect.Height() / 2)),
+                      ((cgitemRect.iBr.iY - formRect.iBr.iY) + 1));
+        // Scroll up is negative
+        nScroll = -nScroll;
+    }
+    else if (lbitemRect.iTl.iY < formRect.iTl.iY)
+    {
+        // Items top is above the form top - need to scroll down (+)
+        nScroll = Min((((formRect.Height() / 2) - lbitemRect.iBr.iY) + 1),
+                      (formRect.iTl.iY - cgitemRect.iTl.iY));
+    }
+    else
+    {
+        // There should be no need to scroll
+        return;
+    }
+
+    // Report a scrolling request
+    ReportEventL(
+        MMIDChoiceGroupControlObserver::EScrollRequest,
+        (TAny*) nScroll);
+}
+
+
+void CMIDChoiceGroupControl::RestoreFocus()
+{
+    // The sub-item focus restore is done by scrolling the form
+    // Exceptions can be ignored, because failing of RequestScrollIfNeededL
+    // may cause only a cosmetic visual problems
+    TRAP_IGNORE(RequestScrollIfNeededL());
+}
+
+
+// Pops up the popup choice (creates a listbox, gives it to the
+// popup for content, gives the popup in turn as parent to the
+// listbox construction, sets title, creates scrollbars and
+// pop goes the weasel
+void CMIDChoiceGroupControl::DoPopupL()
+{
+    //in case skin or resolution has changed
+    iModel->ReConstructSelectionIconsL();
+    iModel->UpdateIconArrayL();
+
+    // In order for the listbox to have popup as parent, it
+    // needs to be allocated and constructed separately
+    CMIDChoiceGroupListBox* pListBox = AllocateListBoxL();
+    CleanupStack::PushL(pListBox);
+
+#ifdef RD_JAVA_S60_RELEASE_9_2
+    // Create the popup with the listbox as popup content
+    CMIDChoiceGroupPopupList* pPop = CMIDChoiceGroupPopupList::NewL(
+                                         pListBox,
+                                         R_MIDP_SOFTKEYS_CANCEL,
+                                         AknPopupLayouts::EMenuUnknownColumnWindow);
+#else
+
+    // Create the popup with the listbox as popup content
+    CMIDChoiceGroupPopupList* pPop = CMIDChoiceGroupPopupList::NewL(
+                                         pListBox,
+                                         R_MIDP_SOFTKEYS_OK_BACK,
+                                         AknPopupLayouts::EMenuUnknownColumnWindow);
+#endif // RD_JAVA_S60_RELEASE_9_2
+    CleanupStack::PushL(pPop);
+
+    // Now actually construct the listbox with the popup as parent
+    ConstructListBoxL(pListBox, pPop);
+
+    // We need some internal vertical scroll stuff in popup - set it up
+    pListBox->CreateScrollBarFrameL(ETrue);
+    pListBox->ScrollBarFrame()->SetScrollBarVisibilityL(
+        CEikScrollBarFrame::EOff,
+        CEikScrollBarFrame::EAuto);
+    // Drawing of scrollbar background is disabled - avoid improper
+    // drawing in landscape mode and appearance is consistent
+    // with native side
+    pListBox->ScrollBarFrame()->DrawBackground(EFalse, EFalse);
+
+    // Set the popup title to be the same as the choicegroup label
+    pPop->SetTitleL(*(iItem->LabelControl()->Text()));
+
+#ifdef RD_JAVA_S60_RELEASE_9_2
+    // Show highlight in pop-up ChoiceGroup.
+    pListBox->DisableSingleClick(ETrue);
+#endif // RD_JAVA_S60_RELEASE_9_2
+
+    // set the current item to the one we currently have selected
+    if (iModel->SelectedElement() >= 0)
+    {
+        pListBox->SetCurrentItemIndex(iModel->SelectedElement());
+    }
+
+    iPopupList = pPop;
+
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+    //When popup list is opening, do the feedback
+    if (CAknTransitionUtils::TransitionsEnabled(
+                AknTransEffect::EComponentTransitionsOff))
+    {
+        iFeedback->InstantFeedback(ETouchFeedbackIncreasingPopUp);
+    }
+    else
+    {
+        iFeedback->InstantFeedback(ETouchFeedbackPopUp);
+    }
+#endif //RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+
+    TInt popOK = pPop->ExecuteLD();
+    iPopupList = NULL;
+
+    CleanupStack::Pop(pPop);
+
+    // Examine result
+    if (popOK)
+    {
+        // Retrieve the selection, and set in model
+        TInt nSelected = pListBox->CurrentItemIndex();
+        if (nSelected != iModel->SelectedElement())
+        {
+            SelectElementL(nSelected, ETrue, ETrue);
+        }
+    }
+#ifdef RD_JAVA_ADVANCED_TACTILE_FEEDBACK
+    else
+    {
+        //Popup list was closed without any selection:
+        //-user canceled it
+        //-user tapped outside of choice list dialog
+        //Do the closing feedback for these two cases
+        if (CAknTransitionUtils::TransitionsEnabled(
+                    AknTransEffect::EComponentTransitionsOff))
+        {
+            iFeedback->InstantFeedback(ETouchFeedbackDecreasingPopUp);
+        }
+        else
+        {
+            iFeedback->InstantFeedback(ETouchFeedbackPopUp);
+        }
+    }
+#endif //RD_RD_ADVANCED_TACTILE_FEEDBACK
+
+    // The icon column may need to be added or removed,
+    // SizeChanged does the relayout
+    SizeChanged();
+
+    // Cleanup
+    CleanupStack::Pop(pListBox);
+    DeleteListBox(pListBox);
+
+    iItem->DrawDeferred();
+}
+
+
+// Creates the choice listbox
+CMIDChoiceGroupListBox* CMIDChoiceGroupControl::CreateListBoxL(
+    const CCoeControl* aParent)
+{
+    // Allocate
+    CMIDChoiceGroupListBox* pListBox = AllocateListBoxL();
+    CleanupStack::PushL(pListBox);
+
+    // Construct
+    ConstructListBoxL(pListBox, aParent);
+    CleanupStack::Pop(pListBox);
+
+    return pListBox;
+}
+
+
+// Allocates the choice listbox.
+CMIDChoiceGroupListBox* CMIDChoiceGroupControl::AllocateListBoxL()
+{
+    return (new(ELeave) CMIDChoiceGroupListBox(this));
+}
+
+
+// Constructs the choice listbox with proper flags and parent
+void CMIDChoiceGroupControl::ConstructListBoxL(
+    CMIDChoiceGroupListBox* aListBox,
+    const CCoeControl* aParent)
+{
+    TInt flags(CEikListBox::ELeftDownInViewRect |
+               CEikListBox::EPaintedSelection |
+               CEikListBox::EKeepModel);
+#ifdef RD_JAVA_S60_RELEASE_9_2
+    flags |= CEikListBox::ENoExtendedSelection;
+#endif // RD_JAVA_S60_RELEASE_9_2
+
+    // Construct with the model passed in (do not allow the listbox
+    // to create its own model)
+    aListBox->ConstructL(aParent, flags, iModel);
+
+    // Grab the column data
+    CColumnListBoxData* columnData = aListBox->ItemDrawer()->ColumnData();
+
+    // Set up columns
+    SetupColumnsL(columnData);
+
+    // Set icon array
+    // NOTE that the listbox (columndata) does not own the
+    // icon array. Some things are needed in deletion to make
+    // sure that the array is not deleted with the listbox
+    columnData->SetIconArray(iModel->IconArray());
+}
+
+
+// Deletes the choice listbox (taking care not to destroy the icon array)
+void CMIDChoiceGroupControl::DeleteListBox(CMIDChoiceGroupListBox* aListBox)
+{
+    // NOTE that the iconarray in the listbox data needs to be
+    // set to NULL, otherwise the array contents will be deleted, which
+    // is not good, since it is actually the model that owns the icons
+    if (aListBox)
+    {
+        CColumnListBoxData* columnData = aListBox->ItemDrawer()->ColumnData();
+        columnData->SetIconArray(NULL);
+
+        delete aListBox;
+    }
+}
+
+/**
+* Functions sets and layouts column data (part of list box used for choicegroup):
+* Column 0 (ERadioButtonOrCheckBoxColumn) is used for radiobuton/checkbox icons
+* Column 1 (EUserDefinedImagesColumn) is used for user defined images
+* Column 2 (ETextColumn) is used for text
+* Each colum has specific layout.
+*
+* @param aColumnData pointer to CColumnListBoxData
+*/
+void CMIDChoiceGroupControl::SetupColumnsL(CColumnListBoxData* aColumnData)
+{
+    ASSERT(aColumnData);
+
+    if (iModel->HasIcons())
+    { //ChoiceGroup has user defined images
+        TAknTextLineLayout textlayout;
+        TAknWindowLineLayout graphiclayout;
+
+        if (iType != MMIDChoiceGroup::EPopup)
+        { //ChoiceGroup
+            //Normal ChoiceGroup must have radiobuton/checkbox icons
+            //if MULTIPLE or EXCLUSIVE type is set.
+            //We set and layout 0. column for those icons.
+            aColumnData->SetGraphicSubCellL(ERadioButtonOrCheckBoxColumn,
+                                            AknLayoutScalable_Avkon::
+                                            list_single_2graphic_pane_g2_cp4().LayoutLine());
+            //layout for text
+            textlayout = AknLayoutScalable_Avkon::
+                         list_single_2graphic_pane_t1_cp4().LayoutLine();
+            //layout for user defined image
+            graphiclayout = AknLayoutScalable_Avkon::
+                            list_single_2graphic_pane_g1_cp4().LayoutLine();
+        }
+        else
+        { //Popup ChoiceGroup - must not have radiobuton/checkbox icons,
+            // so that 0. column is not layouted.
+            //text layout
+            textlayout = AknLayoutScalable_Avkon::
+                         list_single_graphic_pane_t1_cp2(0).LayoutLine();
+            //layout for user defined image
+            graphiclayout = AknLayoutScalable_Avkon::
+                            list_single_graphic_pane_g1_cp2(0).LayoutLine();
+        }
+        //set and layout remaining columns
+        aColumnData->SetTextSubCellL(ETextColumn, textlayout);
+        aColumnData->SetGraphicSubCellL(EUserDefinedImagesColumn,
+                                        graphiclayout);
+    }
+    else
+    { //ChoiceGroup doesn't have user defined images
+        if (iType != MMIDChoiceGroup::EPopup)
+        { //ChoiceGroup
+            //Normal ChoiceGroup must have radiobuton/checkbox icons
+            //if MULTIPLE or EXCLUSIVE type is set.
+            //We set and layout 0. column for those icons.
+            aColumnData->SetGraphicSubCellL(ERadioButtonOrCheckBoxColumn,
+                                            AknLayoutScalable_Avkon::
+                                            list_single_midp_graphic_pane_g4_cp().LayoutLine());
+            // Set and layout text column.
+            aColumnData->SetTextSubCellL(ETextColumn,
+                                         AknLayoutScalable_Avkon::
+                                         list_single_midp_graphic_pane_t1_cp().LayoutLine());
+        }
+        else
+        { //Popup ChoiceGroup - must not have radiobuton/checkbox icons,
+            // so that 0. column is not layouted.
+            // Set and layout text column.
+            aColumnData->SetTextSubCellL(ETextColumn,
+                                         AknLayoutScalable_Avkon::
+                                         list_single_pane_t1_cp2(0).LayoutLine());
+        }
+        // Hide the 1. graphic column when there are no user defined images
+        TAknWindowLineLayout layout =
+            AknLayoutScalable_Avkon::
+            list_single_2graphic_pane_g1_cp4().LayoutLine();
+        layout.iW = 0;  // zero width
+        aColumnData->SetGraphicSubCellL(EUserDefinedImagesColumn, layout);
+    }
+}
+
+// Calculate the rect to assign to the listbox.
+TRect CMIDChoiceGroupControl::ListBoxRect(const TRect& aRect) const
+{
+    TRect rect(aRect);
+
+    if (iListBox)
+    {
+        TAknLayoutRect layoutRect;
+        layoutRect.LayoutRect(rect, AknLayoutScalable_Avkon::
+                              form2_midp_field_choice_group_pane().LayoutLine());
+        rect = layoutRect.Rect();
+    }
+
+    return rect;
+}
+
+
+// Sets selection state of element at <aIndex>
+// This a private method. There is also a public method
+// CMIDChoiceGroupControl::SelectElementL(TInt aIndex, TBool aSelected)
+// which calls this one.
+void CMIDChoiceGroupControl::SelectElementL(TInt aIndex, TBool aSelected, TBool aPostEvent)
+{
+    ASSERT(iModel);
+    iModel->SelectElementL(aIndex, aSelected);
+    iItem->UpdateCommands();
+
+    if (aPostEvent)
+    {
+        iItem->PostItemStateChangedEventL();
+    }
+}
+
+
+void CMIDChoiceGroupControl::UpdateMargins()
+{
+    TAknLayoutRect layoutRect;
+    // use the screen as a parent only the margins are calculated
+    TRect screenRect(iEikonEnv->ScreenDevice()->SizeInPixels());
+    layoutRect.LayoutRect(screenRect, AknLayoutScalable_Avkon::
+                          form2_midp_field_choice_group_pane().LayoutLine());
+
+    TRect contentRect = layoutRect.Rect();
+
+    iContentMargins.iTop    = contentRect.iTl.iY;
+    iContentMargins.iBottom = screenRect.iBr.iY - contentRect.iBr.iY;
+    iContentMargins.iLeft   = contentRect.iTl.iX;
+    iContentMargins.iRight  = screenRect.iBr.iX - contentRect.iBr.iX;
+}
+
+void CMIDChoiceGroupControl::UpdatePopupListAppearanceL(TChoiceGroupModelEvent aEvent)
+{
+    if (!iPopupList)
+    {
+        return;
+    }
+
+    // resize is done by default
+    TBool bResize = ETrue;
+
+    // Act upon the event, resize and redraw the popup list as necessary
+    // EUpdateEnded and EUpdateStarted events are not possible in this
+    // function as they occur only when choice group is constructed
+    switch (aEvent)
+    {
+    case EElementAdded:
+        iPopupList->ListBox()->HandleItemAdditionL();
+        break;
+    case EElementDeleted:
+        iPopupList->ListBox()->HandleItemRemovalL();
+
+        // Popup list loses focus if the highlighted is in the last element and
+        // an element is removed. Set focus to last element in this case.
+        if (iPopupList->ListBox()->View()->CurrentItemIndex() == KErrNotFound &&
+                iModel->NumberOfItems() > 0)
+        {
+            iPopupList->ListBox()->View()->SetCurrentItemIndex(iModel->NumberOfItems() - 1);
+        }
+        break;
+    case EElementModified:
+        // after the LAF changes the icon column is dynamic in popup list, so resize
+        // may be needed after image has been added to or removed from element
+        break;
+    case EElementSelected:
+        // Just a redraw will do
+        bResize = EFalse;
+        break;
+    default:
+        bResize = EFalse;
+        break;
+    }
+
+    if (bResize)
+    {
+        // cast is safe as CMIDChoiceGroupListBox is inherited from CAknColumnListBox
+        SetupColumnsL(static_cast<CEikColumnListBox*>(
+                          iPopupList->ListBox())->ItemDrawer()->ColumnData());
+        iPopupList->ResizeList();
+    }
+
+    iPopupList->DrawDeferred();
+}
+
+void CMIDChoiceGroupControl::ShowInfoPopup()
+{
+    if (!iListBox || !IsVisible())
+    {
+        return;
+    }
+
+    TInt index = iListBox->View()->CurrentItemIndex();
+    if (index >= 0)
+    {
+        CMIDForm* form = iItem->Form();
+        if (form)
+        {
+            form->GetPopupNoteController()->HidePopup();
+
+            if (static_cast<CEikColumnListBox*>(iListBox)->ItemDrawer()->ColumnData()->
+                    CurrentItemTextWasClipped())
+            {
+                CMIDChoiceGroupElement* pElement = iModel->ElementAt(index);
+                if (pElement)
+                {
+                    TRect mainPane;
+                    AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainPane);
+
+                    // HighLight rect returns the rect in propotion to whole screen,
+                    // move it in propotion to the main pane to check if its visible.
+                    TRect rect = iListBox->HighlightRect();
+                    TRect temp = rect;
+                    temp.Move(0, -mainPane.iTl.iY);
+                    if (form->RectPartiallyVisible(temp))
+                    {
+                        TRAP_IGNORE(form->GetPopupNoteController()->ShowPopupL(pElement->Text(),
+                                    rect));
+                    }
+                }
+            }
+            else
+            {
+                DEBUG("CMIDChoiceGroupControl::ShowInfoPopup(): text not clipped");
+            }
+        }
+    }
+}
+
+#ifdef RD_SCALABLE_UI_V2
+void CMIDChoiceGroupControl::TimedScroll(CMIDForm::TDirection aDirection)
+{
+    ASSERT(iListBox);
+
+    TRect itemRect = iItem->Rect();
+    TInt formHeight = iItem->FormRect().Height();
+
+    // There must be something to scroll
+    if ((aDirection == CMIDForm::EDown && itemRect.iBr.iY <= formHeight) ||
+            (aDirection == CMIDForm::EUp   && itemRect.iTl.iY >= 0))
+    {
+        return;
+    }
+
+    iPointerDragHasStarted = ETrue;
+
+    TInt scroll = 0;
+    TInt elementHeight = iListBox->ItemHeight();
+    TInt index = FirstInvisibleElement(aDirection);
+
+    // If valid index was found, adjust scroll so that the element at index will be fully visible.
+    // Otherwise scroll height of one element or to the bottom/top of the item.
+    if (aDirection == CMIDForm::EDown)
+    {
+        if (index != KErrNotFound)
+        {
+            scroll = -(iListBox->View()->ItemPos(index).iY + elementHeight - formHeight);
+        }
+        else
+        {
+            scroll = Max(-elementHeight, -(itemRect.iBr.iY - formHeight));
+        }
+    }
+    else if (aDirection == CMIDForm::EUp)
+    {
+        if (index != KErrNotFound)
+        {
+            scroll = -iListBox->View()->ItemPos(index).iY;
+        }
+        else
+        {
+            scroll = Min(elementHeight, -itemRect.iTl.iY);
+        }
+    }
+
+    if (index != KErrNotFound)
+    {
+        iListBox->View()->SetCurrentItemIndex(index);
+    }
+
+    TRAP_IGNORE(ReportEventL(MMIDChoiceGroupControlObserver::EScrollRequest, (TAny*)scroll));
+}
+
+TInt CMIDChoiceGroupControl::FirstInvisibleElement(CMIDForm::TDirection aScrollDirection) const
+{
+    TInt index = KErrNotFound;
+    TInt formHeight = iItem->FormRect().Height();
+
+    // First find which element is currently right in the top or bottom edge of the form
+    TInt yPos = (aScrollDirection == CMIDForm::EDown) ? formHeight : 0;
+
+    TBool found = iListBox->View()->XYPosToItemIndex(
+                      TPoint(iListBox->Rect().Center().iX, yPos), index);
+
+    // Check if more than half of the element at index is already visible, move to next item or
+    // if already in the last/first element, return KErrNotFound
+    if (found && aScrollDirection == CMIDForm::EDown)
+    {
+        if (formHeight - iListBox->View()->ItemPos(index).iY > (iListBox->ItemHeight() / 2))
+        {
+            index = (index < (iModel->NumberOfItems() - 1)) ? index + 1 : KErrNotFound;
+        }
+    }
+    else if (found && aScrollDirection == CMIDForm::EUp)
+    {
+        if (-iListBox->View()->ItemPos(index).iY < (iListBox->ItemHeight() / 2))
+        {
+            index = (index > 0) ? index - 1 : KErrNotFound;
+        }
+    }
+
+    return index;
+}
+
+#endif
+
+#ifdef RD_JAVA_S60_RELEASE_9_2
+CMIDForm* CMIDChoiceGroupControl::Form() const
+{
+    return iItem->Form();
+}
+#endif // RD_JAVA_S60_RELEASE_9_2