/*
* Copyright (c) 2002-2009 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: This is a concrete class for the handling of a grid.
* The class handles a rectangular grid arrangement of items
* held in any linear ordering i.e. cells ordered top to
* bottom and left, left to right and down etc.
*
*/
#include <AknGrid.h>
#include <avkon.hrh>
#include <avkon.rsg>
#include <barsread.h>
#include <AknGridView.h>
#include <AknGridM.h>
#include <eiklbm.h>
#include <eiklbi.h>
#include <eiklbx.h>
#include <eikenv.h>
#include <eiklbx.pan>
#include <eikkeys.h>
#include <AknsUtils.h>
//formatted listbox
#include <eikfrlbd.h>
#include <eikfrlb.h>
#include <AknDef.h>
#include <aknlayoutscalable_avkon.cdl.h>
#include <AknsControlContext.h>
#include "layoutmetadata.cdl.h"
#include <aknphysics.h>
#ifdef RD_UI_TRANSITION_EFFECTS_LIST
#include <aknlistloadertfx.h>
#include <aknlistboxtfxinternal.h>
#endif //RD_UI_TRANSITION_EFFECTS_LIST
#include <touchfeedback.h>
#include "akntrace.h"
const TInt KDefaultLineHeightForHorizontalGrid = 10;
const TInt KEikListBoxPointerRepeatInterval = 100000; // 0.1 seconds in micro seconds
const TInt KEikListBoxInvalidIndex = -1;
enum TGridOrientation
{
EGridOrientationHorizontal,
EGridOrientationVertical
};
enum TGridHorizontal
{
EGridHorizontalRightToLeft,
EGridHorizontalLeftToRight,
EGridHorizontalWritingDirection,
EGridHorizontalAntiWritingDirection
};
enum TGridVertical
{
EGridVerticalBottomToTop,
EGridVerticalTopToBottom
};
/**
* Local Panic Function and Panic Codes
*/
enum TAknGridPanicCodes
{
EAknPanicGridGeneralPanic,
EAknPanicGridModelAlreadyExists,
EAknPanicGridInvalidNumberOfPrimaryItems,
EAknPanicGridNoView
};
enum TAknGridStateFlags
{
EAknGridStateButton1DownInGrid = 0x01
};
// ================= Extension class ========================
NONSHARABLE_CLASS(CAknGridExtension) : public CBase
{
public:
CAknGridExtension();
~CAknGridExtension();
TInt GetScrollingSpeed( TBool aIsOverItem, TInt aItemIndex, CAknGridView& aView,
const TPoint& aPoint ) const;
public: // data
TInt iFlags;
// EMMA-7A8B9F.Ugly hack. Prevent MopSupplyObject being invoked
// from CEikListBox::MopGetObject()
TBool iIsFromBaseClass;
TPoint iLastPoint;
TBool iKineticScrolling;
TBool iSingleClickEnabled;
};
CAknGridExtension::CAknGridExtension()
:
iFlags(0),
iIsFromBaseClass( EFalse ),
iLastPoint( 0, 0 ),
iKineticScrolling( CAknPhysics::FeatureEnabled() ),
iSingleClickEnabled( iAvkonAppUi->IsSingleClickCompatible() )
{
}
CAknGridExtension::~CAknGridExtension()
{
}
TInt CAknGridExtension::GetScrollingSpeed( TBool aIsOverItem, TInt aItemIndex,
CAknGridView& aView, const TPoint& aPoint ) const
{
TInt itemCountInRow = aView.NumberOfColsInView();
TInt speed = 0;
TInt y = aPoint.iY - iLastPoint.iY;
if ( aIsOverItem )
{
if ( aView.BottomItemIndex() - aItemIndex < itemCountInRow
&& y> 0 )
{
speed = 1;
}
else if ( aItemIndex - aView.TopItemIndex() < itemCountInRow
&& y < 0 )
{
speed = -1;
}
}
else
{
speed = 0;
}
return speed;
}
// ================= CAknGrid class ========================
GLDEF_C void Panic(TAknGridPanicCodes aPanic)
{
_LIT(KPanicCat,"AknGrid");
User::Panic(KPanicCat, aPanic);
}
EXPORT_C CAknGrid::CAknGrid()
{
}
EXPORT_C CAknGrid::~CAknGrid()
{
delete iExtension;
}
/**
* This function must only be called before ConstructL()
*/
EXPORT_C void CAknGrid::SetModel(CAknGridM* aModel)
{
__ASSERT_ALWAYS(!iModel, Panic(EAknPanicGridModelAlreadyExists));
iModel = aModel;
}
EXPORT_C void CAknGrid::ConstructL(const CCoeControl* aParent, TInt aFlags)
{
_AKNTRACE_FUNC_ENTER;
if (!iModel)
{
iModel = new(ELeave) CAknGridM;
}
GridModel()->ConstructL();
CreateItemDrawerL();
ItemDrawer()->FormattedCellData()->SetControl(this);
CEikListBox::ConstructL(aParent, aFlags);
if ( !iExtension )
{
iExtension = new (ELeave) CAknGridExtension;
}
// the default scrolling method is follows items
SetPrimaryScrollingType( CAknGridView::EScrollFollowsItemsAndLoops );
SetSecondaryScrollingType( CAknGridView::EScrollFollowsItemsAndLoops );
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::ConstructFromResourceL(TResourceReader& aReader)
{
_AKNTRACE_FUNC_ENTER;
RestoreCommonListBoxPropertiesL(aReader);
TInt requiredCellCharWidth=aReader.ReadInt16();
if (!iModel)
iModel = new(ELeave) CAknGridM;
TInt items=aReader.ReadInt32();
if (!items)
{
STATIC_CAST(CAknGridM*,iModel)->ConstructL();
}
else
{
CDesCArray* desArray=iCoeEnv->ReadDesCArrayResourceL(items);
CleanupStack::PushL(desArray);
STATIC_CAST(CAknGridM*,iModel)->ConstructL(desArray);
CleanupStack::Pop( desArray );
}
CreateItemDrawerL();
iItemDrawer->SetDrawMark(EFalse);
ItemDrawer()->FormattedCellData()->SetControl(this);
STATIC_CAST(CFormattedCellListBoxItemDrawer*,iItemDrawer)->SetCellWidthInChars(requiredCellCharWidth);
CreateViewL();
// empty text
HBufC* emptyGridText = aReader.ReadHBufCL();
if (emptyGridText)
{
CleanupStack::PushL(emptyGridText);
SetEmptyGridTextL(*emptyGridText);
CleanupStack::PopAndDestroy( emptyGridText );
}
TInt gridStyle=aReader.ReadInt32();
TResourceReader reader;
if (!gridStyle)
{
iEikonEnv->CreateResourceReaderLC(reader,R_AVKON_GRID_STYLE_DEFAULT);
}
else
{
iEikonEnv->CreateResourceReaderLC(reader,gridStyle);
}
SetLayoutFromResourceL(reader);
CleanupStack::PopAndDestroy(); // reader
if ( !iExtension )
{
iExtension = new (ELeave) CAknGridExtension;
}
_AKNTRACE_FUNC_EXIT;
}
/**
* This routine assumes that SetLayoutL has been called to set
* up the grid.
*/
EXPORT_C void CAknGrid::SizeChanged()
{
_AKNTRACE_FUNC_ENTER;
TRect clientRect = iBorder.InnerRect(Rect());
TInt currentDataIndex = 0;
CAknGridView* gridView = GridView();
// reset item offset
gridView->SetItemOffsetInPixels( 0 );
if (iCurrentIsValid) // appshell does not call HandleItemAddition!
{ // so this is never called. See CAknAppStyleGrid::SizeChanged()
// remember current position
currentDataIndex = gridView->CurrentDataIndex();
}
TRect rect(clientRect);
rect.SetRect(rect.iTl.iX + ListBoxMargins().iLeft, rect.iTl.iY + ListBoxMargins().iTop,
rect.iBr.iX - ListBoxMargins().iRight, rect.iBr.iY - ListBoxMargins().iBottom);
iView->SetViewRect(rect);
_AKNTRACE( "The rect of grid are ( %d, %d ) ( %d, %d )",
rect.iTl.iX, rect.iTl.iY,
rect.iBr.iX, rect.iBr.iY );
TRAPD(err1, HandleViewRectSizeChangeL());
if (err1)
{
if (iSBFrameOwned == ENotOwnedExternally)
{
delete iSBFrame;
iSBFrame = 0;
}
}
// check grid orientated to show current focus (item height
// change may have moved the focus to a new page)
if (iCurrentIsValid)
{
gridView->SetCurrentDataIndex(currentDataIndex);
}
MAknsSkinInstance* skin = AknsUtils::SkinInstance();
TRgb color;
TInt error = AknsUtils::GetCachedColor( skin,
color,
KAknsIIDQsnTextColors,
EAknsCIQsnTextColorsCG11 );
if (!error)
{
ItemDrawer()->SetHighlightedTextColor( color );
}
_AKNTRACE_FUNC_EXIT;
}
/**
* Protected routine used by functions to check/alter the dimensions
* of the grid as data items are added or removed or the
* size of the items are altered.
* This routine assumes that SetLayoutL has been called.
* This will not leave if scrollbars have both been turned off.
*/
EXPORT_C void CAknGrid::CalcGridSizeL()
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
CAknGridView* gridView = GridView();
TSize gridSize = gridView->GridCellDimensions();
TInt definedItems = GridModel()->IndexOfLastDataItem() + 1;
if (definedItems > 0)
{
// item height determines iHeight
if (iItemHeight < 1)
{
// item height must exist - so set to default value
// for now
CEikListBox::SetItemHeightL(KDefaultLineHeightForHorizontalGrid);
}
if(iOrientationFlags.IsSet(EGridOrientationVertical))
{
gridSize.iHeight = iNumOfRowsInView;
gridSize.iWidth = definedItems / gridSize.iHeight;
if (definedItems % gridSize.iHeight)
gridSize.iWidth++;
}
else
{
gridSize.iWidth = iNumOfColsInView;
gridSize.iHeight = definedItems / gridSize.iWidth;
if (definedItems % gridSize.iWidth)
gridSize.iHeight++;
}
}
else
{
//no defined items so grid empty
gridSize.iWidth = 1;
gridSize.iHeight = 1;
if (iOrientationFlags.IsSet(EGridOrientationVertical))
gridSize.iHeight = iNumOfRowsInView;
else
gridSize.iWidth = iNumOfColsInView;
}
// set number of cells in grid
gridView->SetGridCellDimensions(gridSize);
_AKNTRACE_FUNC_EXIT;
}
/**
* Creates a formatted list item drawer.
*/
EXPORT_C void CAknGrid::CreateItemDrawerL()
{
CFormattedCellListBoxData* data=CFormattedCellGridData::NewL();
CleanupStack::PushL(data);
data->SetControl( this );
data->CreatePictographInterfaceL();
iItemDrawer=new(ELeave) CFormattedCellListBoxItemDrawer(Model(), iEikonEnv->NormalFont(), data);
CleanupStack::Pop( data );
}
/**
* Gives the formatted list item drawer being used by the grid.
*/
EXPORT_C CFormattedCellListBoxItemDrawer* CAknGrid::ItemDrawer() const
{
return STATIC_CAST(CFormattedCellListBoxItemDrawer*,iItemDrawer);
}
/**
* Informs the grid that the data in the model has altered.
* It is assumed that the grid has been initialised by a call to SetLayoutL
* before this function is called for the first time.
*/
EXPORT_C void CAknGrid::HandleItemAdditionL()
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
if (GridModel()->NumberOfData() <= 0)
return;
TInt currentDataIndex = 0;
CAknGridView* gridView = GridView();
if (iCurrentIsValid)
{
// remember current position
currentDataIndex = gridView->CurrentDataIndex();
}
CalcGridSizeL();
CEikListBox::HandleItemAdditionL();
// check the current marker is set to the first item in the
// grid if it hasn't been set yet otherwise ensure current marker still
// on cell item before new items were added.
if (!iCurrentIsValid)
SetStartPositionL(PositionAtIndex(0));
else
gridView->SetCurrentDataIndex(currentDataIndex);
iCurrentIsValid = ETrue;
// redraw everything visible since all/most will have been affected
DrawNow();
UpdateScrollBarsL();
_AKNTRACE_FUNC_EXIT;
}
/**
* Informs the grid that the data in the model has altered.
* It is assumed that the grid has been initialised by a call to SetLayoutL
* before this function is called for the first time.
*/
EXPORT_C void CAknGrid::HandleItemRemovalL()
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
if (!iCurrentIsValid)
return;
CAknGridView* gridView = GridView();
TInt currentDataIndex = gridView->CurrentDataIndex();
CalcGridSizeL();
CEikListBox::HandleItemRemovalL();
// check if need to reposition current index since may have been
// on one of those just deleted.
CAknGridM* gridModel = GridModel();
if (!gridModel->IndexContainsData(currentDataIndex))
currentDataIndex = gridModel->IndexOfLastDataItem();
gridView->SetCurrentDataIndex(currentDataIndex);
// redraw everything visible since all/most will have been affected
DrawNow();
UpdateScrollBarsL();
_AKNTRACE_FUNC_EXIT;
}
/**
* Sets the orientation of the grid, either vertical or horizontal, the
* ordering of the data and the size of the primary dimension of the
* grid.
* Note: The value for the parameter aNumOfItemsInPrimaryOrient must be
* greater than zero since this determines the number of items (be it rows
* or columns) in the primary orientation of the grid.
*/
EXPORT_C void CAknGrid::SetLayoutL(TBool aVerticalOrientation,
TBool aLeftToRight,
TBool aTopToBottom,
TInt aNumOfItemsInPrimaryOrient,
TInt aNumOfItemsInSecondaryOrient,
TSize aSizeOfItems,
TInt aWidthOfSpaceBetweenItems,
TInt aHeightOfSpaceBetweenItems)
{
_AKNTRACE_FUNC_ENTER;
TGridOrientation gridOrientation = EGridOrientationHorizontal;
if (aVerticalOrientation)
gridOrientation = EGridOrientationVertical;
TGridHorizontal gridHorizontal = EGridHorizontalWritingDirection;
if (aLeftToRight)
gridHorizontal = EGridHorizontalLeftToRight;
else
gridHorizontal = EGridHorizontalRightToLeft;
TGridVertical gridVertical = EGridVerticalBottomToTop;
if (aTopToBottom)
gridVertical = EGridVerticalTopToBottom;
DoSetLayoutL(gridOrientation,
gridHorizontal,
gridVertical,
aNumOfItemsInPrimaryOrient,
aNumOfItemsInSecondaryOrient,
aSizeOfItems,
aWidthOfSpaceBetweenItems,
aHeightOfSpaceBetweenItems);
_AKNTRACE_FUNC_EXIT;
}
void CAknGrid::DoSetLayoutL(TInt aOrientation,
TInt aHorizontal,
TInt aVertical,
TInt aNumOfItemsInPrimaryOrient,
TInt aNumOfItemsInSecondaryOrient,
TSize aSizeOfItems,
TInt aWidthOfSpaceBetweenItems,
TInt aHeightOfSpaceBetweenItems)
{
_AKNTRACE_FUNC_ENTER;
// this must be the first method called after the construction of
// the grid
__TEST_INVARIANT;
// number of primary items must be > 0
__ASSERT_ALWAYS((aNumOfItemsInPrimaryOrient > 0), Panic(EAknPanicGridInvalidNumberOfPrimaryItems));
// store number of items in primary in case of SizeChange
// never used iNumOfItemsInPrimaryOrient = aNumOfItemsInPrimaryOrient;
iSpaceBetweenItems.iWidth = aWidthOfSpaceBetweenItems;
iSpaceBetweenItems.iHeight = aHeightOfSpaceBetweenItems;
iOrientationFlags.ClearAll();
iHorizontalFlags.ClearAll();
iVerticalFlags.ClearAll();
TInt gridFlags = 0;
switch(aOrientation)
{
case EGridOrientationHorizontal:
iOrientationFlags.Set(EGridOrientationHorizontal);
iNumOfRowsInView = aNumOfItemsInSecondaryOrient;
iNumOfColsInView = aNumOfItemsInPrimaryOrient;
break;
case EGridOrientationVertical: // drop through to default
default: // use default to keep BC with old code
iOrientationFlags.Set(EGridOrientationVertical);
gridFlags |= CAknGridView::EPrimaryIsVertical;
iNumOfRowsInView = aNumOfItemsInPrimaryOrient;
iNumOfColsInView = aNumOfItemsInSecondaryOrient;
break;
}
switch(aHorizontal)
{
case EGridHorizontalRightToLeft: // drop through
case EGridHorizontalLeftToRight: // drop through
case EGridHorizontalWritingDirection: // drop through
case EGridHorizontalAntiWritingDirection:
iHorizontalFlags.Set(aHorizontal);
break;
default: // use default to keep BC with old code
iHorizontalFlags.Set(EGridHorizontalWritingDirection);
break;
}
TBool isMirrored = AknLayoutUtils::LayoutMirrored();
if (isMirrored)
{
if (iHorizontalFlags.IsSet(EGridHorizontalAntiWritingDirection)
|| iHorizontalFlags.IsSet(EGridHorizontalLeftToRight))
gridFlags |= CAknGridView::ELeftToRight;
}
else
{
if (iHorizontalFlags.IsSet(EGridHorizontalWritingDirection)
|| iHorizontalFlags.IsSet(EGridHorizontalLeftToRight))
gridFlags |= CAknGridView::ELeftToRight;
}
switch(aVertical)
{
case EGridVerticalBottomToTop:
iVerticalFlags.Set(EGridVerticalBottomToTop);
break;
case EGridVerticalTopToBottom: // drop through to default
default: // use default to keep BC with old code
iVerticalFlags.Set(EGridVerticalTopToBottom);
gridFlags |= CAknGridView::ETopToBottom;
break;
}
CAknGridView* gridView = GridView();
gridView->SetSpacesBetweenItems(iSpaceBetweenItems);
CAknGridView::SGrid gridDetails;
// define a single cell grid size to start with - the grid will only become
// real after we've added some data and called HandleItemAdditionL. Because
// this method is being called when dynamic layout switch is handled we
// cannot set gridDetails.iGridDimensions = TSize(1,1). Instead, we use the
// grid view's value -> a fully populated grid no longer loses its
// dimension information. Default value TSize(1,1) is now set in grid
// view's constructor.
gridDetails.iGridDimensions = gridView->GridCellDimensions();
gridDetails.iGridFlags = gridFlags;
gridDetails.iPageSize = iNumOfColsInView*iNumOfRowsInView;
gridDetails.iColsInView = iNumOfColsInView;
gridDetails.iRowsInView = iNumOfRowsInView;
gridDetails.iSizeBetweenItems = iSpaceBetweenItems;
gridDetails.iSizeOfItems = aSizeOfItems;
CalcGridSizeL();
gridView->SetGridDetails(gridDetails);
gridView->SetColumnWidth(aSizeOfItems.iWidth);
SetItemHeightL(aSizeOfItems.iHeight);
SetItemsInSingleLine(gridDetails.iColsInView);
// Create the scroll bars
_AKNTRACE( "Create scroll bar" );
CreateScrollBarFrameL(ETrue, EFalse, ETrue);
ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto);
UpdateScrollBarsL();
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::HandlePointerEventL(const TPointerEvent& aPointerEvent)
{
_AKNTRACE_FUNC_ENTER;
if ( AknLayoutUtils::PenEnabled() )
{
// Scroll bar
// Do not use iSBFrame->VerticalScrollBar()->Rect() to test,
// because of iSBFrame->VerticalScrollBar owning window.
if( iSBFrame && TRect(iSBFrame->VerticalScrollBar()->Position(),
iSBFrame->VerticalScrollBar()->Size()).Contains ( aPointerEvent.iPosition ))
{
if ( !ScrollingDisabled()
&& iExtension->iFlags & EAknGridStateButton1DownInGrid )
{
if ( aPointerEvent.iType == TPointerEvent::EButton1Up )
{
iExtension->iFlags &= ~EAknGridStateButton1DownInGrid;
}
CEikListBox::HandlePointerEventL( aPointerEvent );
}
else
{
CCoeControl::HandlePointerEventL( aPointerEvent );
}
return;
}
else
{
CAknGridView* gridView = GridView();
TInt itemIndex = 0;
TRect visibleItemsRect(gridView->ViewRect().iTl,
TSize(gridView->ItemSize(itemIndex).iWidth * iNumOfColsInView,
gridView->ItemSize(itemIndex).iHeight * iNumOfRowsInView));
// switch pointer event type and set button 1 down in grid flag on and off depending where event happened.
switch (aPointerEvent.iType)
{
case TPointerEvent::EButton1Down:
iExtension->iLastPoint = aPointerEvent.iPosition;
if ( visibleItemsRect.Contains(aPointerEvent.iPosition) )
{
iExtension->iFlags |= EAknGridStateButton1DownInGrid;
}
_AKNTRACE( "TPointerEvent::EButton1Down" );
break;
case TPointerEvent::EButton1Up:
{
iExtension->iFlags &= ~EAknGridStateButton1DownInGrid;
_AKNTRACE( "TPointerEvent::EButton1Up" );
break;
}
default:
{
break;
}
}
CEikListBox::HandlePointerEventL( aPointerEvent ); // eventually will call CAknGrid::HandleDragEventL if dragged
}
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void* CAknGrid::ExtensionInterface( TUid /*aInterface*/ )
{
return NULL;
}
/**
* Sets the layout from a resource. Layout includes orientation
* (either vertical or horizontal), horizontal and vertical direction
* of numbering, the number of items in the primary and secondary
* orientation, and the primary and secondary scrolling types.
*/
EXPORT_C void CAknGrid::SetLayoutFromResourceL(TResourceReader& aReader)
{
_AKNTRACE_FUNC_ENTER;
TInt layoutFlags = aReader.ReadInt16();
TInt primaryScroll=aReader.ReadInt16();
TInt secondaryScroll=aReader.ReadInt16();
TInt numItemsInPrimaryOrient=aReader.ReadInt16();
TInt numItemsInSecondaryOrient=aReader.ReadInt16();
TInt height = aReader.ReadInt16();
TInt width = aReader.ReadInt16();
TInt gapWidth = aReader.ReadInt16();
TInt gapHeight = aReader.ReadInt16();
TGridOrientation gridOrientation = EGridOrientationHorizontal;
if (layoutFlags & EAknGridVerticalOrientation)
gridOrientation = EGridOrientationVertical;
TGridHorizontal gridHorizontal = EGridHorizontalWritingDirection;
if (layoutFlags & EAknGridLeftToRight)
gridHorizontal = EGridHorizontalLeftToRight;
else if (layoutFlags & EAknGridRightToLeft)
gridHorizontal = EGridHorizontalRightToLeft;
// else if (layoutFlags & EAknGridHorizontalAntiWritingDirection)
// gridHorizontal = EGridHorizontalAntiWritingDirection;
else if (layoutFlags & EAknGridLanguageSpecificHorizontalDirection)
gridHorizontal = EGridHorizontalWritingDirection;
TGridVertical gridVertical = EGridVerticalTopToBottom;
if (layoutFlags & EAknGridBottomToTop)
gridVertical = EGridVerticalBottomToTop;
CAknGridView::TScrollingType prime = CAknGridView::EScrollFollowsItemsAndLoops;
CAknGridView::TScrollingType second = CAknGridView::EScrollFollowsItemsAndLoops;
switch(primaryScroll)
{
case EAknGridFollowsItemsAndStops:
prime = CAknGridView::EScrollFollowsItemsAndStops;
break;
case EAknGridFollowsItemsAndLoops:
prime = CAknGridView::EScrollFollowsItemsAndLoops;
break;
case EAknGridFollowsGrid:
prime = CAknGridView::EScrollFollowsGrid;
break;
case EAknGridStops:
prime = CAknGridView::EScrollStops;
break;
case EAknGridIncrementLineAndStops:
prime = CAknGridView::EScrollIncrementLineAndStops;
break;
case EAknGridIncrementLineAndLoops:
prime = CAknGridView::EScrollIncrementLineAndLoops;
break;
default:
break;
}
switch(secondaryScroll)
{
case EAknGridFollowsItemsAndStops:
second = CAknGridView::EScrollFollowsItemsAndStops;
break;
case EAknGridFollowsItemsAndLoops:
second = CAknGridView::EScrollFollowsItemsAndLoops;
break;
case EAknGridFollowsGrid:
second = CAknGridView::EScrollFollowsGrid;
break;
case EAknGridStops:
second = CAknGridView::EScrollStops;
break;
case EAknGridIncrementLineAndStops:
second = CAknGridView::EScrollIncrementLineAndStops;
break;
case EAknGridIncrementLineAndLoops:
second = CAknGridView::EScrollIncrementLineAndLoops;
break;
default:
break;
}
TSize itemSize(width,height);
DoSetLayoutL(gridOrientation, gridHorizontal, gridVertical, numItemsInPrimaryOrient, numItemsInSecondaryOrient, itemSize, gapWidth, gapHeight);
SetPrimaryScrollingType(prime);
SetSecondaryScrollingType(second);
_AKNTRACE_FUNC_EXIT;
}
/**
* Sets the movement of the cursor with respect to scrolling when the
* end item in the current row or column, whichever is the primary
* orientation of the data items, is encountered. The movement maybe
* either stop, loop back to same row or column or move onto the
* next logical data item in the sequence.
*/
EXPORT_C void CAknGrid::SetPrimaryScrollingType(CAknGridView::TScrollingType aScrollingType)
{
__TEST_INVARIANT;
GridView()->SetPrimaryScrollingType(aScrollingType);
}
/**
* Sets the movement of the cursor with respect to scrolling when the
* end item in the secondary dimension of the grid is encountered.
* The movement maybe either stop, loop back back to same row or column
* or move onto the next logical data item in the sequence.
*/
EXPORT_C void CAknGrid::SetSecondaryScrollingType(CAknGridView::TScrollingType aSecondaryScrolling)
{
__TEST_INVARIANT;
GridView()->SetSecondaryScrollingType(aSecondaryScrolling);
}
/**
* Returns the current grid data index.
* Returns -1 if the grid is empty.
*/
EXPORT_C TInt CAknGrid::CurrentDataIndex() const
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
if (GridModel()->NumberOfData() < 1)
{
_AKNTRACE( "no data in model, return -1" );
_AKNTRACE_FUNC_EXIT;
return -1;
}
TInt startOffset = GridModel()->IndexOfFirstDataItem();
_AKNTRACE_FUNC_EXIT;
return (GridView()->CurrentDataIndex() - startOffset);
}
/**
* Moves the cursor to the required grid data index.
*/
EXPORT_C void CAknGrid::SetCurrentDataIndex(TInt aDataIndex)
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
_AKNTRACE( "[%s] aDataIndex = %d",
__FUNCTION__, aDataIndex );
TInt startOffset = GridModel()->IndexOfFirstDataItem();
if (startOffset == -1)
{
_AKNTRACE_FUNC_EXIT;
return; // there is no data yet
}
TInt newIndex = aDataIndex + startOffset;
GridView()->SetCurrentDataIndex(newIndex);
UpdateScrollBarThumbs();
_AKNTRACE_FUNC_EXIT;
}
/**
* Sets the starting position of the data within the grid.
* <p>
* Note: a blank page cannot be accessed (since cannot move into empty cells)
* so a totally blank page is the same as if the page never existed since
* the user cannot scroll into it. For this reason it is suggested that
* the start position be no more than one page into the grid.
*/
EXPORT_C void CAknGrid::SetStartPositionL(TPoint aGridStartPosition)
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
TInt dataIndex;
CAknGridView* gridView = GridView();
gridView->DataIndexFromLogicalPos(dataIndex,aGridStartPosition.iY,aGridStartPosition.iX);
// set the number of blank start cells
GridModel()->SetStartCells(dataIndex);
// need to check grid size
CalcGridSizeL();
// check current item position
gridView->SetCurrentDataIndex(dataIndex);
iCurrentIsValid = ETrue;
_AKNTRACE_FUNC_EXIT;
}
/**
* Gives the data index at the grid position given.
* The position must be given with respect to the top left corner
* of the grid.
* An index value of -1 is given if the grid position given is
* invalid.
*/
EXPORT_C TInt CAknGrid::IndexOfPosition(TPoint aGridPosition) const
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
TInt indexValue = -1;
CAknGridView* gridView = GridView();
TSize gridSize = gridView->GridCellDimensions();
if ((aGridPosition.iY >= 0)&&
(aGridPosition.iX >= 0)&&
(aGridPosition.iX < gridSize.iWidth)&&
(aGridPosition.iY < gridSize.iHeight))
{
// grid position is valid
gridView->DataIndexFromLogicalPos(indexValue, aGridPosition.iY, aGridPosition.iX);
indexValue -= GridModel()->IndexOfFirstDataItem();
}
_AKNTRACE_FUNC_EXIT;
return indexValue;
}
/**
* Gives the position of the data item required.
* The logical position is given with respect to the top left
* hand corner of the grid.
* A position of (-1,-1) is given if the data item is not valid.
*/
EXPORT_C TPoint CAknGrid::PositionAtIndex(TInt aItemIndex) const
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
_AKNTRACE(" [%s] aItemIndex = %d",
__FUNCTION__, aItemIndex );
TPoint gridPosition(-1,-1);
CAknGridM* gridModel = GridModel();
TInt startOffset = gridModel->IndexOfFirstDataItem();
TInt newIndex = aItemIndex + startOffset;
if (ItemExists(newIndex))
{
if (gridModel->IndexContainsData(newIndex))
{
GridView()->
LogicalPosFromDataIndex(newIndex, gridPosition.iY, gridPosition.iX);
}
}
_AKNTRACE_FUNC_EXIT;
return gridPosition;
}
EXPORT_C CListBoxView* CAknGrid::MakeViewClassInstanceL()
{
return (new(ELeave) CAknGridView);
}
/**
* Sets the column width of the grid.
* Column width cannot be set in a horizontal grid since the number
* of columns in the grid is defined by the initialising call to
* SetLayoutL.
* The column width cannot be larger than the width of the viewing
* rectangle.
*/
EXPORT_C void CAknGrid::SetColumnWidth(TInt aColumnWidth)
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
_AKNTRACE(" [%s] aColumnWidth = %d",
__FUNCTION__, aColumnWidth );
if (aColumnWidth < 1)
return;
TInt currentDataIndex = 0;
CAknGridView* gridView = GridView();
if (iCurrentIsValid)
{
// remember current position
currentDataIndex = gridView->CurrentDataIndex();
}
// hold the users value in case needed in the method
// HandleItemAdditionL
iMinColWidth = aColumnWidth;
gridView->SetColumnWidth(aColumnWidth);
// check grid orientated to show current focus (item height
// change may have moved the focus to a new page)
if (iCurrentIsValid)
gridView->SetCurrentDataIndex(currentDataIndex);
_AKNTRACE_FUNC_EXIT;
}
/**
* Sets the row height of the grid.
* Row height cannot be set in a vertical grid since the number
* of rows in the grid is defined by the initialising call to
* SetLayoutL.
* The row height cannot be larger than the height of the viewing
* rectangle.
*/
EXPORT_C void CAknGrid::SetItemHeightL(TInt aHeight)
{
_AKNTRACE_FUNC_ENTER;
__TEST_INVARIANT;
_AKNTRACE(" [%s] aHeight = %d",
__FUNCTION__, aHeight );
if (aHeight < 1)
return;
TInt currentDataIndex = 0;
CAknGridView* gridView = GridView();
if (iCurrentIsValid)
{
// remember current position
currentDataIndex = gridView->CurrentDataIndex();
}
// this ensures that if user changes the item height then the
// grid details will change in line as well
iItemHeight = aHeight;
gridView->SetItemHeight(aHeight);
CalcGridSizeL();
// check grid orientated to show current focus (item height
// change may have moved the focus to a new page)
if (iCurrentIsValid)
gridView->SetCurrentDataIndex(currentDataIndex);
_AKNTRACE_FUNC_EXIT;
}
/**
* Processes the user key events or passes them onto the underlying
* listbox code.
*/
EXPORT_C TKeyResponse CAknGrid::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType)
{
_AKNTRACE_FUNC_ENTER;
// SERIES60 LAF
CListBoxView::TSelectionMode selectionMode = CListBoxView::ENoSelection;
TBool shiftKeyPressed = (aKeyEvent.iModifiers & EModifierShift);
if (iListBoxFlags & EMultipleSelection)
{
if (shiftKeyPressed && iListBoxFlags & EShiftEnterMarks)
{
View()->SetAnchor(View()->CurrentItemIndex());
selectionMode = CListBoxView::EDisjointMarkSelection;
}
else
selectionMode = CListBoxView::ENoSelection;
}
// With single click first key event enables highlight
if ( iExtension->iSingleClickEnabled
&& ItemDrawer()->Flags()
& CListItemDrawer::ESingleClickDisabledHighlight )
{
if ( aKeyEvent.iCode == EKeyUpArrow ||
aKeyEvent.iCode == EKeyDownArrow ||
aKeyEvent.iCode == EKeyLeftArrow ||
aKeyEvent.iCode == EKeyRightArrow ||
aKeyEvent.iCode == EKeyEnter ||
aKeyEvent.iCode == EKeyOK )
{
_AKNTRACE_FUNC_EXIT;
return CEikListBox::OfferKeyEventL( aKeyEvent, aType );
}
}
switch (aKeyEvent.iCode)
{
case EKeyUpArrow:
iView->MoveCursorL(CListBoxView::ECursorPreviousItem, selectionMode);
ClearMatchBuffer();
_AKNTRACE( "EKeyUpArrow" );
break;
case EKeyDownArrow:
iView->MoveCursorL(CListBoxView::ECursorNextItem, selectionMode);
ClearMatchBuffer();
_AKNTRACE( "EKeyDownArrow" );
break;
case EKeyPrevious:
{
const TBool disableRedraw = aKeyEvent.iRepeats;
TBool redrawDisabled = iView->RedrawDisabled();
if ( disableRedraw )
{
iView->SetDisableRedraw(ETrue);
}
((CAknGridView*)iView)->
MoveCursorWithRepeatsL( EFalse, selectionMode, 1 + aKeyEvent.iRepeats );
ClearMatchBuffer();
if ( disableRedraw )
{
iView->SetDisableRedraw(redrawDisabled);
if ( !redrawDisabled )
{
DrawNow();
}
}
}
_AKNTRACE( "EKeyPrevious" );
break;
case EKeyLeftArrow:
iView->MoveCursorL(CListBoxView::ECursorPreviousColumn, selectionMode);
ClearMatchBuffer();
_AKNTRACE( "EKeyLeftArrow" );
break;
case EKeyNext:
{
const TBool disableRedraw = aKeyEvent.iRepeats;
TBool redrawDisabled = iView->RedrawDisabled();
if ( disableRedraw )
{
iView->SetDisableRedraw(ETrue);
}
((CAknGridView*)iView)->
MoveCursorWithRepeatsL( ETrue, selectionMode, 1 + aKeyEvent.iRepeats );
ClearMatchBuffer();
if ( disableRedraw )
{
iView->SetDisableRedraw(redrawDisabled);
if ( !redrawDisabled )
{
DrawNow();
}
}
}
_AKNTRACE( "EKeyNext" );
break;
case EKeyRightArrow:
iView->MoveCursorL(CListBoxView::ECursorNextColumn, selectionMode);
ClearMatchBuffer();
_AKNTRACE( "EKeyRightArrow" );
break;
#if defined(_DEBUG) // only needed when debuging
case EKeyPageUp:
iView->MoveCursorL(CListBoxView::ECursorPreviousPage, selectionMode);
ClearMatchBuffer();
break;
case EKeyPageDown:
iView->MoveCursorL(CListBoxView::ECursorNextPage, selectionMode);
ClearMatchBuffer();
break;
case EKeyHome:
iView->MoveCursorL(CListBoxView::ECursorFirstItem, selectionMode);
ClearMatchBuffer();
break;
case EKeyEnd:
iView->MoveCursorL(CListBoxView::ECursorLastItem, selectionMode);
ClearMatchBuffer();
break;
#endif// end of debug code
default:
_AKNTRACE_FUNC_EXIT;
return CEikListBox::OfferKeyEventL(aKeyEvent,aType);
}
UpdateScrollBarThumbs();
// matcher cursor code maybe needed here if matcher cursors
// supported in future versions, refer to CEikListBox::OfferKeyEventL
if (iListBoxFlags & EStateChanged)
{
ReportEventL(MCoeControlObserver::EEventStateChanged);
iListBoxFlags &= (~EStateChanged);
}
_AKNTRACE_FUNC_EXIT;
return EKeyWasConsumed;
}
/**
* @return Model
*/
EXPORT_C CTextListBoxModel* CAknGrid::Model() const
{
return STATIC_CAST(CTextListBoxModel*,iModel);
}
/**
* Sets the viewable rect
*/
EXPORT_C void CAknGrid::SetRect(const TRect& aRect)
{
_AKNTRACE_FUNC_ENTER;
CCoeControl::SetRect(aRect);
TRect rect(aRect);
rect.SetRect(rect.iTl.iX + ListBoxMargins().iLeft, rect.iTl.iY + ListBoxMargins().iTop,
rect.iBr.iX - ListBoxMargins().iRight, rect.iBr.iY - ListBoxMargins().iBottom);
_AKNTRACE( "The rect of grid are ( %d, %d ) ( %d, %d )",
rect.iTl.iX, rect.iTl.iY,
rect.iBr.iX, rect.iBr.iY );
iView->SetViewRect(rect);
// basically, this is redundant call, but leaving it out breaks at
// least camcorder's colorburstgrid, because SizeChanged() trusts
// margin values
TRAP_IGNORE( HandleViewRectSizeChangeL() );
_AKNTRACE_FUNC_EXIT;
}
/**
* Called when the view rect changes size
*/
EXPORT_C void CAknGrid::HandleViewRectSizeChangeL()
{
_AKNTRACE_FUNC_ENTER;
iView->CalcBottomItemIndex();
TInt oldTopItemIndex = TopItemIndex();// store old top item index
TInt newTopItemIndex = oldTopItemIndex;// set new same as old
TInt currentItemIndex = CurrentItemIndex();// get the current item index
if (currentItemIndex < 0)// check valid
{
_AKNTRACE_FUNC_EXIT;
return;
}
iNumOfRowsInView = Max(1, iNumOfRowsInView);// make iNumOfRowsInView > 1
iNumOfColsInView = Max(1, iNumOfColsInView);// make iNumOfColsInView > 1
if ( iOrientationFlags.IsClear( EGridOrientationVertical )&&
( AknLayoutUtils::LayoutMirrored()
? iHorizontalFlags.IsClear( EGridHorizontalLeftToRight )
: iHorizontalFlags.IsSet( EGridHorizontalLeftToRight ) ) &&
iVerticalFlags.IsSet(EGridVerticalTopToBottom) )
{
// CAknAppStyleGrid uses this combination of flags
AdjustTopItemIndex();
}
else
{
// this version is likely to contain bugs.
if (currentItemIndex != oldTopItemIndex)
{
TInt colIndexOfTargetItem = currentItemIndex / iNumOfRowsInView;// iNumOfRowsInView != 0
TInt adjustment = newTopItemIndex % iNumOfRowsInView;// iNumOfRowsInView != 0
if (adjustment != 0)
{
// adjust newTopItemIndex till it refers to the index of an item at the top of a column
newTopItemIndex -= adjustment;
}
TInt newBottomItemIndex = newTopItemIndex + (iNumOfColsInView * iNumOfRowsInView) - 1;
if (currentItemIndex < newTopItemIndex)
newTopItemIndex = colIndexOfTargetItem * iNumOfRowsInView;
else if (currentItemIndex > newBottomItemIndex)
{
TInt colIndexOfNewBottomItem = colIndexOfTargetItem;
TInt colIndexOfNewTopItem = colIndexOfNewBottomItem - (iNumOfColsInView - 1);
newTopItemIndex = colIndexOfNewTopItem * iNumOfRowsInView;
}
}
else if ((newTopItemIndex != 0) && (iNumOfRowsInView != 0))
{
TInt adjustment = newTopItemIndex % iNumOfRowsInView;// iNumOfRowsInView != 0
if (adjustment != 0)
{// adjust newTopItemIndex till it refers to the index of an item at the top of a column
newTopItemIndex -= adjustment;
}
}
SetTopItemIndex(newTopItemIndex);
}
iView->CalcDataWidth();
UpdateScrollBarsL();
iView->CalcBottomItemIndex();
_AKNTRACE_FUNC_EXIT;
}
/**
* Set the empty grid text
*/
EXPORT_C void CAknGrid::SetEmptyGridTextL(const TDesC& aText)
{
GridView()->SetListEmptyTextL(aText);
}
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
EXPORT_C void CAknGrid::SetTopItemIndex(TInt aItemIndex) const
{
__ASSERT_DEBUG(iView, Panic(EAknPanicGridNoView));
iView->SetTopItemIndex(aItemIndex);
}
EXPORT_C void CAknGrid::HandleResourceChange(TInt aType)
{
_AKNTRACE_FUNC_ENTER;
// Need to do this to set up the scroll bar model
TRAP_IGNORE( UpdateScrollBarsL() );
if (aType==KEikDynamicLayoutVariantSwitch)
{
CAknGridView* gridView = GridView();
gridView->SetItemOffsetInPixels( 0 );
TSize sizeOfItems;
sizeOfItems.iWidth = ( Rect().iBr.iX - Rect().iTl.iX ) / iNumOfColsInView;
sizeOfItems.iHeight = ( Rect().iBr.iY - Rect().iTl.iY ) / iNumOfRowsInView;
CAknGridView::SGrid gridDetails;
gridDetails.iGridDimensions = gridView->GridCellDimensions();
gridDetails.iPageSize = iNumOfColsInView*iNumOfRowsInView;
gridDetails.iColsInView = iNumOfColsInView;
gridDetails.iRowsInView = iNumOfRowsInView;
gridDetails.iSizeBetweenItems = iSpaceBetweenItems;
//gridDetails.iSizeOfItems = gridView->ItemSize();
gridDetails.iSizeOfItems = sizeOfItems;
gridView->SetColumnWidth(gridDetails.iSizeOfItems.iWidth);
TRAP_IGNORE( SetItemHeightL(gridDetails.iSizeOfItems.iHeight) );
gridDetails.iGridFlags = 0;
if (iOrientationFlags.IsSet(EGridOrientationVertical))
gridDetails.iGridFlags |= CAknGridView::EPrimaryIsVertical;
if (iVerticalFlags.IsSet(EGridVerticalTopToBottom))
gridDetails.iGridFlags |= CAknGridView::ETopToBottom;
TBool isMirrored = AknLayoutUtils::LayoutMirrored();
if (isMirrored)
{
if (iHorizontalFlags.IsSet(EGridHorizontalAntiWritingDirection)
|| iHorizontalFlags.IsSet(EGridHorizontalLeftToRight))
gridDetails.iGridFlags |= CAknGridView::ELeftToRight;
}
else
{
if (iHorizontalFlags.IsSet(EGridHorizontalWritingDirection)
|| iHorizontalFlags.IsSet(EGridHorizontalLeftToRight))
gridDetails.iGridFlags |= CAknGridView::ELeftToRight;
}
gridView->SetGridDetails(gridDetails);
SetItemsInSingleLine( gridDetails.iColsInView );
}
CEikListBox::HandleResourceChange(aType);
TRAP_IGNORE( ItemDrawer()->FormattedCellData()->SetupSkinContextL());
// Data extension has animations which will change when skin changes.
ItemDrawer()->FormattedCellData()->HandleResourceChange( aType );
// Need to do this to set up the scroll bar model
TRAP_IGNORE( UpdateScrollBarsL() );
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::FocusChanged( TDrawNow aDrawNow )
{
_AKNTRACE_FUNC_ENTER;
CEikListBox::FocusChanged( aDrawNow );
// Grid data needs focus change information to control animations.
if( IsFocused() )
{
ItemDrawer()->FormattedCellData()->FocusGained();
}
else
{
ItemDrawer()->FormattedCellData()->FocusLost();
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C TInt CAknGrid::ColumnWidth() const
{
__ASSERT_DEBUG(iView, Panic(EAknPanicGridNoView));
return ((CAknGridView*)iView)->ColumnWidth();
}
EXPORT_C TInt CAknGrid::HorizontalNudgeValue() const
{
return 1; // scroll horizontal by one column when the left/right scroll arrows (i.e. the nudge buttons) are tapped
}
EXPORT_C TInt CAknGrid::HorizScrollGranularityInPixels() const
{
return ColumnWidth(); // horiz scrollbar model set in columns for snaking list box
}
EXPORT_C void CAknGrid::AdjustTopItemIndex() const
{
_AKNTRACE_FUNC_ENTER;
/*
* make sure, that topitemindex points to topmost leftmost visible item
* This hackish solution is only valid for appstyle grid & similar grids
*/
if ( iOrientationFlags.IsClear( EGridOrientationVertical )&&
( AknLayoutUtils::LayoutMirrored()
? iHorizontalFlags.IsClear( EGridHorizontalLeftToRight )
: iHorizontalFlags.IsSet( EGridHorizontalLeftToRight ) ) &&
iVerticalFlags.IsSet(EGridVerticalTopToBottom) )
{
if ( !iNumOfColsInView )
{ // just in case
_AKNTRACE_FUNC_EXIT;
return;
}
TPoint topIndexPoint = iView->ItemPos( TopItemIndex() );
TInt bottomOrdinate = topIndexPoint.iY + iItemHeight;
if ( TopItemIndex() % iNumOfColsInView == 0
&& ( bottomOrdinate > 0
&& bottomOrdinate <= ( iView->ViewRect().iTl.iY + iItemHeight ) )
&& iView->ViewRect().Contains( iView->ItemPos( CurrentItemIndex() ) ) )
{
_AKNTRACE_FUNC_EXIT;
return;
}
// get row of topmost item relative to the whole grid - 0 based indexing
TInt topRow = TopItemIndex() / iNumOfColsInView;
// get row of current item relative to the whole grid - 0 based indexing
TInt currRow = CurrentItemIndex() / iNumOfColsInView;
// top item will be col 0 in topmost row - find out new top item's row
TInt newTopRow( topRow ); // prefer minimum adjustment
TInt reminder = iView->ViewRect().Height() % ( iItemHeight + iSpaceBetweenItems.iHeight );
TInt rowsFitInView = iNumOfRowsInView;
if ( reminder > 0 )
{
rowsFitInView ++;
}
newTopRow = currRow - rowsFitInView + 1;
newTopRow = Max( 0, newTopRow );
// and calculate new top item index
TInt topItemIndex = newTopRow * iNumOfColsInView ;
iView->SetItemOffsetInPixels(0);
SetTopItemIndex(topItemIndex);
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::HandleDragEventL(TPoint aPointerPos)
{
_AKNTRACE_FUNC_ENTER;
// No drag event handling if kinetic scrolling is enabled
if ( !ScrollingDisabled() )
{
_AKNTRACE_FUNC_EXIT;
return;
}
if ( AknLayoutUtils::PenEnabled() )
{
if ( !(iExtension->iFlags & EAknGridStateButton1DownInGrid) )
{
_AKNTRACE_FUNC_EXIT;
return;
}
CAknGridView* gridView = GridView();
TRect maxRect(TPoint( KMinTInt, KMinTInt ), TPoint(KMaxTInt, KMaxTInt));
TRect ignoreDragRect;
TRect viewRect(gridView->ViewRect());
TInt itemIndex = KEikListBoxInvalidIndex ;
TBool pointerIsOverAnItem = gridView->XYPosToItemIndex(aPointerPos, itemIndex);
// SERIES60 LAF
CListBoxView::TSelectionMode selectionMode = CListBoxView::ENoSelection;
// CListBoxView::TSelectionMode selectionMode = (iListBoxFlags & EMultipleSelection) ? CListBoxView::EContiguousSelection : CListBoxView::ESingleSelection;
// END OF SERIES60 LAF
TInt speed = iExtension->GetScrollingSpeed( pointerIsOverAnItem, itemIndex,
*gridView, aPointerPos );
TInt oldCurrentItemIndex = CurrentItemIndex();
TRect currentItemRect(gridView->ItemPos(oldCurrentItemIndex), gridView->ItemSize(oldCurrentItemIndex));
TInt numOfRows = Max(GridView()->GridCellDimensions().iHeight,1);
const TInt placesInGrid = numOfRows * iNumOfColsInView;
TBool currItemIsInLastRow = ((placesInGrid - iNumOfColsInView) <= itemIndex) && (placesInGrid > itemIndex);
TBool currItemIsInFirstRow = itemIndex >= iNumOfColsInView;
TRect visibleItemsRect(viewRect.iTl,
TPoint(gridView->ItemSize(itemIndex).iWidth * iNumOfColsInView,
gridView->ItemSize(itemIndex).iHeight * iNumOfRowsInView));
if ( speed != 0 )
{
TInt itemCountInRow = iNumOfColsInView;
TInt dest = itemIndex + speed * itemCountInRow;
TInt offset = 0;
TInt newTopIndex = dest / itemCountInRow * itemCountInRow;
TInt upperBound = iModel->NumberOfItems() - 1;
if ( speed > 0 )
{
if ( gridView->ActualDataIndex( dest ) <= upperBound )
{
offset = -itemCountInRow;
}
else
{
dest = gridView->ListBoxIndex( upperBound );
offset = itemIndex - dest; // Don't make focus jump.
}
newTopIndex -= ( iNumOfRowsInView - 1) * itemCountInRow ;
}
else if ( speed < 0 )
{
if ( dest > 0 )
{
offset = itemCountInRow;
}
else
{
offset = itemIndex - dest;
newTopIndex = 0;
}
}
if ((iListBoxFlags & ES60StyleMarkable) &&
( (EventModifiers() & EModifierShift) ||
(EventModifiers() & EModifierCtrl) ))
{
selectionMode = CListBoxView::EPenMultiselection;
iListBoxFlags |= EStateChanged;
}
TInt oldtop = gridView->TopItemIndex();
if ( !gridView->ItemIsVisible( dest ))
{
gridView->VScrollTo( newTopIndex );
UpdateScrollBarThumbs();
if ( pointerIsOverAnItem )
{
Window().RequestPointerRepeatEvent(
KEikListBoxPointerRepeatInterval, viewRect );
}
}
itemIndex = dest + offset;
}
// if pointer is over some new item.
if ( pointerIsOverAnItem )
{
#ifdef RD_UI_TRANSITION_EFFECTS_LIST
MAknListBoxTfxInternal* transApi = CAknListLoader::TfxApiInternal(
iView->ItemDrawer()->Gc() );
if ( transApi && !transApi->EffectsDisabled() )
{
transApi->SetMoveType( itemIndex < oldCurrentItemIndex ?
MAknListBoxTfxInternal::EListMoveUp :
MAknListBoxTfxInternal::EListMoveDown );
}
#endif
if ((iListBoxFlags & ES60StyleMarkable) &&
( (EventModifiers() & EModifierShift) ||
(EventModifiers() & EModifierCtrl) ))
{
selectionMode = CListBoxView::EPenMultiselection;
iListBoxFlags |= EStateChanged;
}
gridView->MoveToItemIndexL(itemIndex, selectionMode);
}
else if ((aPointerPos.iY < visibleItemsRect.iTl.iY) &&
(aPointerPos.iX > visibleItemsRect.iTl.iX) &&
(aPointerPos.iX < visibleItemsRect.iBr.iX) &&
!currItemIsInFirstRow) // Dragged upwards from the grid.
{
ignoreDragRect = TRect(maxRect.iTl, TPoint(maxRect.iBr.iX, visibleItemsRect.iTl.iY));
MoveToNextOrPreviousItemL(aPointerPos);
if ( (iListBoxFlags & ES60StyleMarkable) &&
( (EventModifiers() & EModifierShift) ||
(EventModifiers() & EModifierCtrl) ))
{
iView->UpdateSelectionL(CListBoxView::EPenMultiselection);
iListBoxFlags |= EStateChanged;
}
UpdateScrollBarThumbs();
Window().RequestPointerRepeatEvent(KEikListBoxPointerRepeatInterval, ignoreDragRect);
}
else if (aPointerPos.iY > visibleItemsRect.iBr.iY &&
(aPointerPos.iX > visibleItemsRect.iTl.iX) &&
(aPointerPos.iX < visibleItemsRect.iBr.iX) &&
!currItemIsInLastRow) // Dragged downwards from the grid.
{
ignoreDragRect = TRect(TPoint( maxRect.iTl.iX, visibleItemsRect.iBr.iY), maxRect.iBr );
MoveToNextOrPreviousItemL(aPointerPos);
if ( (iListBoxFlags & ES60StyleMarkable) &&
( (EventModifiers() & EModifierShift) ||
(EventModifiers() & EModifierCtrl) ))
{
iView->UpdateSelectionL(CListBoxView::EPenMultiselection);
iListBoxFlags |= EStateChanged;
}
UpdateScrollBarThumbs();
Window().RequestPointerRepeatEvent(KEikListBoxPointerRepeatInterval, ignoreDragRect);
}
if (CurrentItemIndex() != oldCurrentItemIndex)
{
ReportListBoxEventL(MEikListBoxObserver::EEventItemDraggingActioned);
iListBoxFlags |= EStateChanged;
if (IsMatchBuffer())
{
ClearMatchBuffer();
DrawMatcherCursor();
}
}
}
else
{
if (!(iListBoxFlags & ELeftDownInViewRect))
{
_AKNTRACE_FUNC_EXIT;
return;
}
CAknGridView* gridView = GridView();
const TInt clickableBorder = 20; // distance in pixels of area around grid that can see pointer events.
TRect ignoreDragRect(TPoint(aPointerPos.iX-clickableBorder, aPointerPos.iY-clickableBorder), TPoint(aPointerPos.iX+clickableBorder, aPointerPos.iY+clickableBorder));
TRect viewRect(gridView->ViewRect());
TInt itemIndex;
TBool pointerIsOverAnItem = gridView->XYPosToItemIndex(aPointerPos, itemIndex);
// SERIES60 LAF
CListBoxView::TSelectionMode selectionMode = CListBoxView::ENoSelection;
// CListBoxView::TSelectionMode selectionMode = (iListBoxFlags & EMultipleSelection) ? CListBoxView::EContiguousSelection : CListBoxView::ESingleSelection;
// END OF SERIES60 LAF
TInt oldCurrentItemIndex = CurrentItemIndex();
TRect currentItemRect(gridView->ItemPos(oldCurrentItemIndex), gridView->ItemSize(oldCurrentItemIndex));
if (pointerIsOverAnItem)
{
gridView->MoveToItemIndexL(itemIndex, selectionMode);
}
else if ((aPointerPos.iX < viewRect.iTl.iX) || (aPointerPos.iX > viewRect.iBr.iX))
{
// drag event occurred outside the listbox's viewRect
if (aPointerPos.iX < viewRect.iTl.iX)
gridView->MoveCursorL(CListBoxView::ECursorPreviousColumn, selectionMode);
else if (aPointerPos.iX > viewRect.iBr.iX)
gridView->MoveCursorL(CListBoxView::ECursorNextColumn, selectionMode);
MoveToNextOrPreviousItemL(aPointerPos);
UpdateScrollBarThumbs();
Window().RequestPointerRepeatEvent(KEikListBoxPointerRepeatInterval, ignoreDragRect);
}
else
{
// find item nearest to the pointer pos and make that the current item
if (viewRect.Contains(aPointerPos))
{
}
else
{
if (aPointerPos.iX > currentItemRect.iBr.iX)
gridView->MoveCursorL(CListBoxView::ECursorNextColumn, selectionMode);
else if (aPointerPos.iX < currentItemRect.iTl.iX)
gridView->MoveCursorL(CListBoxView::ECursorPreviousColumn, selectionMode);
MoveToNextOrPreviousItemL(aPointerPos);
UpdateScrollBarThumbs();
Window().RequestPointerRepeatEvent(KEikListBoxPointerRepeatInterval, ignoreDragRect);
}
}
if (CurrentItemIndex() != oldCurrentItemIndex)
{
iListBoxFlags |= EStateChanged;
if (IsMatchBuffer())
{
ClearMatchBuffer();
DrawMatcherCursor();
}
}
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::RestoreClientRectFromViewRect(TRect& aClientRect) const
{
_AKNTRACE_FUNC_ENTER;
aClientRect=iView->ViewRect();
aClientRect.SetRect(aClientRect.iTl.iX - ListBoxMargins().iLeft, aClientRect.iTl.iY - ListBoxMargins().iTop,
aClientRect.iBr.iX + ListBoxMargins().iRight, aClientRect.iBr.iY + ListBoxMargins().iBottom);
if (!ViewRectHeightAdjustment())
{
_AKNTRACE_FUNC_EXIT;
return;
}
aClientRect.iBr.iY += ViewRectHeightAdjustment();
_AKNTRACE( "The rect of grid are ( %d, %d ) ( %d, %d )",
aClientRect.iTl.iX, aClientRect.iTl.iY,
aClientRect.iBr.iX, aClientRect.iBr.iY );
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C TInt CAknGrid::AdjustRectHeightToWholeNumberOfItems(TRect& aRect) const
{
_AKNTRACE_FUNC_ENTER;
TInt remainder = aRect.Height() % (iItemHeight + iSpaceBetweenItems.iHeight);
if (remainder >= iItemHeight)
remainder -= iItemHeight;
if (remainder != 0)
aRect.iBr.iY -= remainder;
_AKNTRACE_FUNC_EXIT;
return remainder;
}
EXPORT_C void CAknGrid::MoveToNextOrPreviousItemL(TPoint aPointerPos)
{
_AKNTRACE_FUNC_ENTER;
// SERIES60 LAF
CListBoxView::TSelectionMode selectionMode = CListBoxView::ENoSelection;
// CListBoxView::TSelectionMode selectionMode = (iListBoxFlags & EMultipleSelection) ? CListBoxView::EContiguousSelection : CListBoxView::ESingleSelection;
// END OF SERIES60 LAF
TInt cix = CurrentItemIndex();
TRect currentItemRect(iView->ItemPos(cix), iView->ItemSize(cix));
TInt numOfRows = Max(GridView()->GridCellDimensions().iHeight,1);
TBool currItemIsInLastRow;
TBool currItemIsInFirstRow;
if ( AknLayoutUtils::PenEnabled() )
{
const TInt placesInGrid = numOfRows * iNumOfColsInView;
// calculate is the item in first or last row.
currItemIsInLastRow = ((placesInGrid - iNumOfColsInView) <= cix) && (placesInGrid > cix);
currItemIsInFirstRow = (cix >= 0 && cix < iNumOfColsInView);
}
else
{
currItemIsInLastRow = ((cix % numOfRows) == (numOfRows-1));
currItemIsInFirstRow = ((cix % numOfRows) == 0);
}
TBool currItemIsLastItem = (cix == (iModel->NumberOfItems()-1));
if ((aPointerPos.iY > currentItemRect.iBr.iY) && (! (currItemIsInLastRow || currItemIsLastItem)))
{
iView->MoveCursorL(CListBoxView::ECursorNextItem, selectionMode);
}
else if ((aPointerPos.iY < currentItemRect.iTl.iY) && (! currItemIsInFirstRow))
{
iView->MoveCursorL(CListBoxView::ECursorPreviousItem, selectionMode);
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::UpdateScrollBarsL()
{
_AKNTRACE_FUNC_ENTER;
if (!iSBFrame)
{
_AKNTRACE_FUNC_EXIT;
return;
}
TEikScrollBarModel hSbarModel;
TEikScrollBarModel vSbarModel;
CAknGridView* gridView = GridView();
TRect rect=gridView->ViewRect();
if (!(iListBoxFlags & EScrollBarSizeExcluded))
{
// Ignore scrollbars presence to set the model, Scrollbar Frame will change it as required
rect = iBorder.InnerRect(Rect());
rect.SetRect(rect.iTl.iX + ListBoxMargins().iLeft, rect.iTl.iY + ListBoxMargins().iTop,
rect.iBr.iX - ListBoxMargins().iRight, rect.iBr.iY - ListBoxMargins().iBottom);
AdjustRectHeightToWholeNumberOfItems(rect);
// rect is now viewRect when ignoring scrollbars
}
if (iSBFrame->VScrollBarVisibility()!=CEikScrollBarFrame::EOff)
{
TInt currentIndex = CurrentItemIndex();
if (currentIndex < 0)
{
_AKNTRACE_FUNC_EXIT;
return; // current item is not defined
}
TInt row = 0;
TInt col = 0;
gridView->LogicalPosFromListBoxIndex(currentIndex, row, col);
row *= iView->ItemHeight();
row -= iView->ItemOffsetInPixels();
TSize gridSize = gridView->GridCellDimensions();
gridSize.iHeight = Max(gridSize.iHeight,1);//check gridSize != 0
vSbarModel.iThumbPosition = row;
// EHXA-7AQ8N4. Only set it to 0 can make scrollbar empty.
vSbarModel.iScrollSpan = GridModel()->NumberOfItems() >0 ?
gridSize.iHeight : 0;
vSbarModel.iThumbSpan = gridView->NumberOfRowsInView();
vSbarModel.iScrollSpan = GridModel()->NumberOfItems() >0 ?
gridSize.iHeight*iView->ItemHeight() : 0;
vSbarModel.iThumbSpan = rect.Height();
if (iSBFrame->TypeOfVScrollBar() == CEikScrollBarFrame::EDoubleSpan)
{
TInt topRow = 0;
TInt topCol = 0;
gridView->LogicalPosFromListBoxIndex(TopItemIndex(), topRow, topCol);
topRow *= iView->ItemHeight();
topRow -= iView->ItemOffsetInPixels();
vSbarModel.iThumbPosition = topRow;
}
if (vSbarModel.iScrollSpan-vSbarModel.iThumbPosition<vSbarModel.iThumbSpan)
{
vSbarModel.iThumbPosition=Max(0,vSbarModel.iScrollSpan-vSbarModel.iThumbSpan);
if ( !iExtension->iSingleClickEnabled )
{
// force a scroll if neccessary
gridView->MoveToItemIndexL( currentIndex,
CListBoxView::ENoSelection );
}
}
}
if (iSBFrame->ScrollBarVisibility(CEikScrollBar::EHorizontal)!=CEikScrollBarFrame::EOff)
{
GridView()->CalcDataWidth();
hSbarModel.iThumbPosition = gridView->HScrollOffset();
hSbarModel.iScrollSpan = gridView->DataWidth();
}
if (iSBFrame->TypeOfVScrollBar() == CEikScrollBarFrame::EDoubleSpan)
{
//same scrollbar data within landscape & portraid
TInt varietyIndex = Layout_Meta_Data::IsLandscapeOrientation() ? 1: 0;
TAknWindowComponentLayout layout = TAknWindowComponentLayout::Compose(
AknLayoutScalable_Avkon::listscroll_app_pane(0),
AknLayoutScalable_Avkon::scroll_pane_cp15(varietyIndex));
CEikAppUi* appUi = iEikonEnv->EikAppUi();
TRect clientRect = appUi->ClientRect();
TRect mainPaneRect;
AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainPaneRect);
TRect scrollBarParent = TRect( TPoint(0, 0), mainPaneRect.Size());
AknLayoutUtils::LayoutVerticalScrollBar(iSBFrame, scrollBarParent, layout.LayoutLine());
TRect inclusiveRect=Rect();
TEikScrollBarFrameLayout layoutSB;
layoutSB.SetClientMargin(0);
layoutSB.SetInclusiveMargin(0);
layoutSB.iTilingMode=TEikScrollBarFrameLayout::EInclusiveRectConstant;
iSBFrame->Tile(&hSbarModel, &vSbarModel );
}
else
{
TRect clientRect;
RestoreClientRectFromViewRect(clientRect);
TRect inclusiveRect=Rect();
TEikScrollBarFrameLayout layout;
CreateScrollBarFrameLayout(layout);
TBool sizeChanged=iSBFrame->TileL(&hSbarModel, &vSbarModel, clientRect, inclusiveRect, layout);
if (!sizeChanged)
{
_AKNTRACE_FUNC_EXIT;
return;
}
// else size of client/inclusive rect has changed
if (layout.iTilingMode==TEikScrollBarFrameLayout::EClientRectConstant)
SetSizeWithoutNotification(inclusiveRect.Size());
else
{
SetViewRectFromClientRect(clientRect);
ClearMargins();
}
}
AdjustTopItemIndex();
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C void CAknGrid::UpdateScrollBarThumbs() const
{
_AKNTRACE_FUNC_ENTER;
if (!iSBFrame)
{
_AKNTRACE_FUNC_EXIT;
return;
}
CAknGridView* gridView = GridView();
TInt currentDataIndex = gridView->CurrentDataIndex();
TInt row = 0;
TInt col = 0;
gridView->LogicalPosFromDataIndex(currentDataIndex, row, col);
iSBFrame->MoveHorizThumbTo(col);
if (iSBFrame->TypeOfVScrollBar() == CEikScrollBarFrame::EArrowHead)
{
iSBFrame->MoveVertThumbTo(row);
iSBFrame->DrawScrollBarsNow();
}
else
{
TInt topRow = 0;
TInt topCol = 0;
gridView->LogicalPosFromListBoxIndex(TopItemIndex(), topRow, topCol);
topRow *= iView->ItemHeight();
topRow -= iView->ItemOffsetInPixels();
iSBFrame->MoveVertThumbTo(topRow);
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C TInt CAknGrid::CountComponentControls() const
{
if (GridModel()->NumberOfItems()<=0) return 0;
return CEikListBox::CountComponentControls();
}
// debug only invariant function
EXPORT_C void CAknGrid::__DbgTestInvariant() const
{
#if defined(_DEBUG)
TBool invalid = EFalse;
if ( !iModel )
{
invalid = ETrue;
}
if ( !iView )
{
invalid = ETrue;
}
if ( invalid )
{
User::Invariant();
}
#endif
}
EXPORT_C TAny* CAknGrid::MListBoxModel_Reserved()
{
return NULL;
}
//----------------------------------------------------------------------------
// Handles scroll events received from the scroll bar.
// Function reads thumb position from model and updates view by these values.
// One thumb step is actually one row in list, not item in list.
//----------------------------------------------------------------------------
//
EXPORT_C void CAknGrid::HandleScrollEventL( CEikScrollBar* aScrollBar,
TEikScrollEvent aEventType )
{
_AKNTRACE_FUNC_ENTER;
if ( AknLayoutUtils::PenEnabled() )
{
// Read values from model.
TInt oldThumbPos = iView->TopItemIndex() / iNumOfColsInView * iView->ItemHeight() - iView->ItemOffsetInPixels();
TInt newThumbPos = aScrollBar->ThumbPosition();
TInt pageSize = aScrollBar->Model()->iThumbSpan;
TInt maxThumbPos = aScrollBar->Model()->MaxThumbPos();
TBool update = EFalse;
switch ( aEventType & KEikScrollEventBarMask )
{
case KEikScrollEventFromHBar:
{
switch ( aEventType )
{
case EEikScrollLeft:
{
newThumbPos -= HorizontalNudgeValue();
break;
}
case EEikScrollRight:
{
newThumbPos += HorizontalNudgeValue();
break;
}
case EEikScrollPageLeft:
{
newThumbPos -= pageSize;
break;
}
case EEikScrollPageRight:
{
newThumbPos += pageSize;
break;
}
case EEikScrollThumbDragVert:
{
// In the case of drag events, the scrollbar
// automatically updates its thumb pos...
SuspendEffects( ETrue );
break;
}
case EEikScrollThumbReleaseVert:
{
// In the case of drag events, the scrollbar
// automatically updates its thumb pos...
SuspendEffects( EFalse );
break;
}
default:
{
// Do nothing
break;
}
}
newThumbPos = Max( 0, Min( newThumbPos, maxThumbPos ) );
if ( aEventType != EEikScrollThumbDragHoriz )
{
iView->HScroll( newThumbPos - oldThumbPos );
aScrollBar->SetModelThumbPosition( iView->HScrollOffset() );
}
break;
}
case KEikScrollEventFromVBar:
{
switch ( aEventType )
{
case EEikScrollUp:
{
if ( oldThumbPos == 0 && (iListBoxFlags & ELoopScrolling) )
{
// move thumb to downmost site if current is upmost
newThumbPos = maxThumbPos;
update = ETrue;
}
break;
}
case EEikScrollDown:
{
if ( oldThumbPos == maxThumbPos && (iListBoxFlags & ELoopScrolling) )
{
// move thumb to upmost site if current is downmost
newThumbPos = 0;
update = ETrue;
}
break;
}
case EEikScrollThumbDragVert:
{
SuspendEffects( ETrue );
break;
}
case EEikScrollThumbReleaseVert:
{
SuspendEffects( EFalse );
break;
}
default:
{
// Do nothing
break;
}
}
#ifdef RD_UI_TRANSITION_EFFECTS_LIST
MAknListBoxTfxInternal* transApi =
CAknListLoader::TfxApiInternal(
iView->ItemDrawer()->Gc() );
TBool effects = newThumbPos != oldThumbPos &&
transApi && !transApi->EffectsDisabled();
if ( effects )
{
transApi->SetMoveType( newThumbPos < oldThumbPos ?
MAknListBoxTfxInternal::EListScrollUp :
MAknListBoxTfxInternal::EListScrollDown );
}
#endif // RD_UI_TRANSITION_EFFECTS_LIST
if ( iExtension && !ScrollingDisabled() )
{
HandlePhysicsScrollEventL( newThumbPos - oldThumbPos );
}
else
{
// Do normal scrolling if physics are not enabled.
iView->VScrollTo( newThumbPos/iView->ItemHeight() * iNumOfColsInView );
}
#ifdef RD_UI_TRANSITION_EFFECTS_LIST
if ( effects )
{
transApi->Draw( Rect() );
}
#endif // RD_UI_TRANSITION_EFFECTS_LIST
// If event has changed thumb position to different than in
// model do by default, then update scroll bar to
// correct values.
if ( update )
{
aScrollBar->SetModelThumbPosition(
iView->TopItemIndex() / iNumOfColsInView*iView->ItemHeight() - iView->ItemOffsetInPixels() );
UpdateScrollBarThumbs();
}
else
{
aScrollBar->DrawNow();
}
}
default:
{
// Do nothing
break;
}
}
}
else
{
CEikListBox::HandleScrollEventL( aScrollBar, aEventType );
}
_AKNTRACE_FUNC_EXIT;
}
EXPORT_C TTypeUid::Ptr CAknGrid::MopSupplyObject(TTypeUid aId)
{
if ( iExtension && iExtension->iIsFromBaseClass )
{
iExtension->iIsFromBaseClass = EFalse;
return CEikListBox::MopSupplyObject( aId );
}
if ( iExtension && aId.iUid == MAknsControlContext::ETypeId )
{
MAknsControlContext* cc = NULL;
iExtension->iIsFromBaseClass = ETrue;
if ( !CEikListBox::MopGetObject( cc ))
{
cc = ItemDrawer()->FormattedCellData()->SkinBackgroundContext();
}
return MAknsControlContext::SupplyMopObject( aId, cc);
}
return CEikListBox::MopSupplyObject( aId );
}