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

/*
* Copyright (c) 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