--- /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