/*
* Copyright (c) 1997-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:
*
*/
#include <eikcapca.h>
#include <eikcapc.h>
#include <AknLayout2ScalableDef.h>
#include <AknUtils.h>
#include <aknlayoutscalable_avkon.cdl.h>
#include <AknsUtils.h>
#include <eikedwin.h>
#include "AknPanic.h"
#include "akntrace.h"
const TInt KVerticalSpacing=-2; // Avkon form controls overlap by two pixels!
const TInt KVerticalSpacingSquash=0;
const TInt KAknTopMargin = 0 ;
const TInt KAknNoTopMargin = 0 ;
class CEikCapCArrayExtension : public CBase
{
public:
TInt iCaptionWidth;
TRect iRect;
};
TBool IsPopupField(CEikCaptionedControl* aCC)
{
TInt ctrlType = aCC->iControlType;
if (ctrlType == EAknCtPopupField || ctrlType == EAknCtPopupFieldText)
{
CAknPopupField *field = (CAknPopupField*)aCC->iControl;
if (field->SelectionMode() != CAknPopupField::EAknPopupFieldLabelMode)
return ETrue;
return EFalse;
}
return EFalse;
}
// NOTE densePacking != iIsFormControl (i.e. Dialogs are dense packed)
EXPORT_C CEikCapCArray::CEikCapCArray(TInt aGranularity)
: CArrayPtrFlat<CEikCaptionedControl>(aGranularity)
{
__DECLARE_NAME(_S("CEikCapCArray"));
}
EXPORT_C CEikCapCArray::~CEikCapCArray()
{
delete iExtension;
ResetAndDestroy();
}
EXPORT_C void CEikCapCArray::DeleteLine(TInt aIndex)
{
// Changed the deleting order to fix TSW Error EKMA-7ES98G
// so that now the item is removed from the array first,
// and only after that actually deleted.
CBase* tmp = ((*this)[aIndex]);
Delete(aIndex);
delete tmp;
}
EXPORT_C void CEikCapCArray::SetDensePacking(TBool aDensePacking)
{
iDensePacking=aDensePacking;
}
void CEikCapCArray::CreateExtensionL()
{
iExtension = new(ELeave) CEikCapCArrayExtension;
}
CEikCapCArrayExtension *CEikCapCArray::ExtensionOrNull() const
{
if (!iExtension)
{
TRAP_IGNORE(const_cast<CEikCapCArray*>(this)->CreateExtensionL());
}
return iExtension;
}
TInt CEikCapCArray::NumberOfTextLines() const
{
_AKNTRACE_FUNC_ENTER;
CEikCaptionedControl *firstCapCC = Count() > 0 ? (*this)[0] : NULL;
const CEikDialogPage *dialogPage = firstCapCC ? firstCapCC->DialogPage() : NULL;
CEikDialogPage::TFormLayoutSelection ret = CEikDialogPage::ESingle;
if ( dialogPage )
ret = dialogPage->FormLayout();
TInt lines = 0;
for(TInt i = 0; i<Count(); i++)
{
TInt num_of_lines = 0;
CEikCaptionedControl *line = (*this)[i];
if (ret == CEikDialogPage::ESingle)
{
//line->MinimumSize(); // ensures NumberOfLines() is valid.
TInt h = line->NumberOfLinesForScrollBar();
num_of_lines = h;
}
else if (ret == CEikDialogPage::EDouble)
{
//line->MinimumSize(); // ensures NumberOfLines() is valid.
TInt h = line->NumberOfLinesForScrollBar();
num_of_lines = h; // one for title
}
lines += num_of_lines;
}
_AKNTRACE( "lines: [%d]", lines );
_AKNTRACE_FUNC_EXIT;
return lines;
}
TInt CEikCapCArray::NumberOfTextLinesBeforeLine(TInt aLine) const
{
_AKNTRACE_FUNC_ENTER;
CEikCaptionedControl *firstCapCC = Count() > 0 ? (*this)[0] : NULL;
const CEikDialogPage *dialogPage = firstCapCC ? firstCapCC->DialogPage() : NULL;
CEikDialogPage::TFormLayoutSelection ret = CEikDialogPage::ESingle;
if ( dialogPage )
ret = dialogPage->FormLayout();
TInt lines = 0;
for(TInt i = 0; i<aLine; i++)
{
TInt num_of_lines = 0;
CEikCaptionedControl *line = (*this)[i];
if (ret == CEikDialogPage::ESingle)
{
//line->MinimumSize(); // ensures NumberOfLines() is valid.
TInt h = line->NumberOfLinesForScrollBar();
num_of_lines = h;
}
else if (ret == CEikDialogPage::EDouble)
{
//line->MinimumSize(); // ensures NumberOfLines() is valid.
TInt h = line->NumberOfLinesForScrollBar();
num_of_lines = h; // one for title
}
lines += num_of_lines;
}
_AKNTRACE( "lines: [%d]", lines );
_AKNTRACE_FUNC_EXIT;
return lines;
}
TInt CEikCapCArray::FindItemFromTextLine(TInt aTextLine) const
{
CEikCaptionedControl *firstCapCC = Count() > 0 ? (*this)[0] : NULL;
const CEikDialogPage *dialogPage = firstCapCC ? firstCapCC->DialogPage() : NULL;
CEikDialogPage::TFormLayoutSelection ret = CEikDialogPage::ESingle;
if ( dialogPage )
ret = dialogPage->FormLayout();
TInt lines = 0;
TInt i = 0;
for(i = 0; i<Count(); i++)
{
TInt num_of_lines = 0;
CEikCaptionedControl *line = (*this)[i];
if (ret == CEikDialogPage::ESingle)
{
//line->MinimumSize(); // ensures NumberOfLines() is valid.
TInt h = line->NumberOfLinesForScrollBar();
num_of_lines = h;
}
else if (ret == CEikDialogPage::EDouble)
{
//line->MinimumSize(); // ensures NumberOfLines() is valid.
TInt h = line->NumberOfLinesForScrollBar();
num_of_lines = h; // one for title
}
lines += num_of_lines;
if (lines > aTextLine)
break;
}
return i;
}
/**
* Calculate the size of all the component controls stacked vertically.
* For Avkon Forms the controls overlap by 2 pixels (hard coded)
*/
EXPORT_C TSize CEikCapCArray::MinimumSize()
{
_AKNTRACE_FUNC_ENTER;
TSize size(0,0);
CEikCaptionedControl *firstCapCC = Count() > 0 ? (*this)[0] : NULL;
if (firstCapCC && firstCapCC->iIsFormControl)
{ // we're inside form
// Minimumsize needs to be called even though we wouldnt use the result (at least it calculates the number of lines)
// TP HACK START (made because MinimumSize() is not good name for situations where content dimensions are desired)
TInt height = 0;
TInt width = 0;
TRect parentRect = Rect();
TSize lineSize; // absolute size, minimumSize = maximumSize = LAF size
for(TInt i=0;i<Count();i++)
{
TInt gap = 0; // not possible to get this from LAF. (should be 0.5u or something)
CEikCaptionedControl *line = (*this)[i];
TAknWindowLineLayout layout;
const CEikDialogPage *dialogPage = firstCapCC->DialogPage();
CEikDialogPage::TFormLayoutSelection ret = CEikDialogPage::ESingle;
if ( dialogPage )
ret = dialogPage->FormLayout();
if (ret == CEikDialogPage::ESingle)
{
lineSize = line->MinimumSize(); // ensures NumberOfLines() is valid.
}
else if (ret == CEikDialogPage::EDouble)
{
lineSize = line->MinimumSize(); // ensures NumberOfLines() is valid.
}
height += lineSize.iHeight + gap;
width = lineSize.iWidth;
}
size = TSize(width,height);
}
else
{
TInt wholeWidth=0;
const TInt count=Count();
const TInt topMargin=iDensePacking ? KAknNoTopMargin : KAknTopMargin;
const TInt bottomMargin = iDensePacking ? KAknNoTopMargin : KAknTopMargin ;
const TInt verticalSpacing=iDensePacking ? KVerticalSpacingSquash : KVerticalSpacing;
TInt deltaHeight=0;
CEikCapCArrayExtension *extension = ExtensionOrNull();
if (!extension) return TSize(30,30); // OOM
for (TInt ii=0;ii<count;++ii)
{
CEikCaptionedControl* line=(*this)[ii];
TSize thisSize=line->MinimumSize();
TInt thisDeltaHeight=thisSize.iHeight+verticalSpacing;
if (deltaHeight<thisDeltaHeight)
deltaHeight=thisDeltaHeight;
const TInt thisCaptionWidth=line->iCaptionWidth;
if (!(line->LatentGroupLineFollows()))
{
size.iHeight+=deltaHeight;
deltaHeight=0;
}
if (!thisCaptionWidth)
{
if (wholeWidth<thisSize.iWidth)
wholeWidth=thisSize.iWidth;
}
else
{
thisSize.iWidth-=thisCaptionWidth;
if (extension->iCaptionWidth<thisCaptionWidth)
extension->iCaptionWidth=thisCaptionWidth;
if (size.iWidth<thisSize.iWidth)
size.iWidth=thisSize.iWidth;
}
}
size.iWidth+=extension->iCaptionWidth;
if (size.iWidth<wholeWidth)
size.iWidth=wholeWidth;
// If the total height is zero don't bother adding a top margin
if ( size.iHeight > 0 )
{
size.iHeight+=( topMargin + bottomMargin ) ;
size.iHeight+=2 ; // (we have included one too many '-2's)
}
}
_AKNTRACE( "The Size : ( %d, %d ) ", size.iHeight, size.iWidth );
_AKNTRACE_FUNC_EXIT;
return size;
}
/**
* Position all of the component controls in a vertical pile.
* For Avkon Forms the controls overlap by two pixels. (hard coded)
*/
TRect CEikCapCArray::Rect() const
{
CEikCapCArrayExtension *ext = ExtensionOrNull();
if (ext)
return ext->iRect;
else
return TRect(0,0, 100,100); // oom
}
TBool CEikCapCArray::CalcItemIndexes(TInt &aTopItemIndex, TInt &aMiddleItemIndex, TInt &aBottomItemIndex, TSize aAreaSize)
{ // calculates last item that still fits to the area's size
TBool switchDir = EFalse;
TInt i;
TInt reservedHeight = 0;
TInt newTop = aTopItemIndex, newMiddle = 1, newBottom = aBottomItemIndex;
const TInt count = Count();
CEikCaptionedControl *firstCapCC = Count() > 0 ? (*this)[0] : NULL;
TInt num = 0;
if (newTop == -1) { newTop = Count() - newMiddle - newBottom; }
if (newBottom == -1) { newBottom = Count() - newTop - newMiddle; }
if (aTopItemIndex != -1)
{
num = count - aTopItemIndex;
}
if (aBottomItemIndex != -1)
{
num = count - aBottomItemIndex;
}
for (i = 0; i < num; i++)
{
TInt index = 0;
if (aTopItemIndex != -1)
{
index = i + aTopItemIndex;
}
else if (aBottomItemIndex != -1)
{
index = count - 1 - aBottomItemIndex - i;
}
CEikCaptionedControl* line=(*this)[index];
const CEikDialogPage *dialogPage = firstCapCC->DialogPage();
CEikDialogPage::TFormLayoutSelection ret = CEikDialogPage::ESingle;
if ( dialogPage )
ret = dialogPage->FormLayout();
TAknWindowLineLayout layout;
if (ret == CEikDialogPage::ESingle)
{
TInt height = line->NumberOfLines();
if (height < 1) height = 1;
if (IsPopupField(line))
{
layout = AknLayoutScalable_Avkon::form_field_popup_pane(height-1).LayoutLine();
}
else
{
layout = AknLayoutScalable_Avkon::form_field_data_pane(height-1).LayoutLine();
}
}
else if (ret == CEikDialogPage::EDouble)
{
TInt height = line->NumberOfLines();
if (height < 1) height = 1;
if (IsPopupField(line))
{
layout = AknLayoutScalable_Avkon::form_field_popup_wide_pane(height-1).LayoutLine();
}
else
{
layout = AknLayoutScalable_Avkon::form_field_data_wide_pane(height-1).LayoutLine();
}
}
else
{
// should Panic() here
}
TRect rect = TRect(TPoint(0,0), aAreaSize);
TAknLayoutRect layoutRect;
layoutRect.LayoutRect(rect, layout);
TInt height2 = layoutRect.Rect().Height();
reservedHeight += height2;
if (reservedHeight > aAreaSize.iHeight)
{
break;
}
newMiddle = i + 1;
if (aTopItemIndex == -1)
{
newTop = count - i - 1 - aBottomItemIndex;
}
if (aBottomItemIndex == -1)
{
newBottom = count - i - 1 - aTopItemIndex;
}
}
if (reservedHeight <= aAreaSize.iHeight)
{
// 2nd loop for case where item sizes have changed; makes sure screen is full.
for(int i = 0; newBottom > 0 && aTopItemIndex == -1 || newTop > 0 && aBottomItemIndex == -1; i++)
{
switchDir = ETrue;
TInt index = 0;
if (aBottomItemIndex == -1)
{
newMiddle ++;
newTop --;
index = newTop;
}
if (aTopItemIndex == -1)
{
newMiddle ++;
newBottom --;
index = count - 1 - newBottom;
}
CEikCaptionedControl* line=(*this)[index];
const CEikDialogPage *dialogPage = firstCapCC->DialogPage();
CEikDialogPage::TFormLayoutSelection ret = CEikDialogPage::ESingle;
if ( dialogPage )
ret = dialogPage->FormLayout();
TAknWindowLineLayout layout;
if (ret == CEikDialogPage::ESingle)
{
TInt height = line->NumberOfLines();
if (height < 1) height = 1;
if (IsPopupField(line))
{
layout = AknLayoutScalable_Avkon::form_field_popup_pane(height-1).LayoutLine();
}
else
{
layout = AknLayoutScalable_Avkon::form_field_data_pane(height-1).LayoutLine();
}
}
else if (ret == CEikDialogPage::EDouble)
{
TInt height = line->NumberOfLines();
if (height < 1) height = 1;
if (IsPopupField(line))
{
layout = AknLayoutScalable_Avkon::form_field_popup_wide_pane(height-1).LayoutLine();
}
else
{
layout = AknLayoutScalable_Avkon::form_field_data_wide_pane(height-1).LayoutLine();
}
}
else
{
// should Panic() here
}
TRect rect = TRect(TPoint(0,0), aAreaSize);
TAknLayoutRect layoutRect;
layoutRect.LayoutRect(rect, layout);
TInt height2 = layoutRect.Rect().Height();
reservedHeight += height2;
if (reservedHeight > aAreaSize.iHeight)
break;
}
}
aTopItemIndex = newTop;
aMiddleItemIndex = newMiddle;
aBottomItemIndex = newBottom;
return switchDir;
}
EXPORT_C void CEikCapCArray::SetRect(const TRect &aRect)
{
SetRect(aRect, 0, -1, -1);
}
/**
* If aControl is an edwin, set its clipping rect to empty. This
* will disable the text hiding.
*/
static void ResetHides(CEikCaptionedControl *aControl)
{
aControl->SetPartiallyVisible( EFalse );
if (aControl->ControlIsAnEdwin(aControl->iControlType))
{
CEikEdwin *edwin = (CEikEdwin*)aControl->iControl;
edwin->SetTextLinesRect(TRect());
}
}
/**
* Sets a clipping rectangle for hiding the whole or a part of edwin's text.
*
* The reason for using this function is the multiline edwins. The text inside
* an edwin can be broken to two or more lines, which must be hidden or shown
* independently from each other. That is why it is not enough just to move
* the whole edwin out of the screen.
*
* @param aClipRect The clipping rect for edwin's text. An empty rect disables
* hiding.
*
* @return How many subcontrols were hidden
*/
static TInt HideLines_Edwin(CEikEdwin *aEdwin, TRect aClipRect)
{
aEdwin->SetTextLinesRect(aClipRect);
// Create rects of the first and last edwin lines
TPoint edwinTl( aEdwin->Rect().iTl );
TPoint edwinBr( aEdwin->Rect().iBr );
TRect textFirstLine;
aEdwin->TextLayout()->GetLineRect(edwinTl.iY, textFirstLine);
textFirstLine.Move( edwinTl.iX, edwinTl.iY + aEdwin->Margins().iTop );
TRect textLastLine;
aEdwin->TextLayout()->GetLineRect(edwinBr.iY, textLastLine);
textLastLine.Move( edwinBr.iX, edwinBr.iY - aEdwin->Margins().iTop - textLastLine.Height() );
// Check if at least one line fits to the clipping rect
if( aClipRect.Contains(textFirstLine.iTl) &&
aClipRect.iBr.iY >= textFirstLine.iBr.iY ) // The first line fits
return 0;
if( aClipRect.Contains(textLastLine.iTl) &&
aClipRect.iBr.iY >= textLastLine.iBr.iY ) // The last line fits
return 0;
return 1;
}
/**
* Tries to hide the specified control. The control will be hidden, if it doesn't
* fit to the specified clipping rectangle. Checks if the control exists.
*
* @return How many subcontrols were hidden
*/
static TInt HideLines_Ctrl(CCoeControl *aControl, TRect aClipRect)
{
if ( !aControl )
return 1; // It doesn't exist and hence not visible
TRect rect( aControl->Rect() );
if ( !aClipRect.Contains(rect.iTl) || aClipRect.iBr.iY <= rect.iBr.iY )
// Never use TRect::Contains() for checking the bottom right corner, see documentation
{
// hide it
aControl->SetPosition( TPoint(-666,-666) );
return 1;
}
else
return 0;
}
/**
* Get vertically minimal rectangle of the two given.
*
* Vertically reduces aRect1 by aRect2's dangling part, if aRect2
* doesn't fit to aRect1.
*
* Sets aRect1 to the resulting minimal rectangle.
*/
static void GetVertMinRect( TRect& aRect1, const TRect aRect2 )
{
// If aRect2's top doesn't fit, lower aRect1's top
if( aRect2.iTl.iY < aRect1.iTl.iY )
aRect1.iTl.iY = Max( aRect1.iTl.iY, aRect2.iBr.iY );
// If aRect2's bottom doesn't fit, raise aRect1's bottom
if( aRect2.iBr.iY > aRect1.iBr.iY )
aRect1.iBr.iY = Min( aRect1.iBr.iY, aRect2.iTl.iY );
}
/**
* Hides the specified form line, if it does not fit to the specified clipping rectangle.
* The function never hides focused editable lines. If the form layout is single, the whole
* captioned control is hidden.
*
* @param aControl The form line to be hidden
* @param aClipRect The clipping rectangle
*
* @return How many subcontrols remained visible
*/
static TInt HideLines(CEikCaptionedControl *aControl, TRect aClipRect)
{
TInt visibleCtrls = 3; // Visible subcontrols after hiding
CEikCaptionedControl *currentdLine = aControl->DialogPage()->CurrentLine();
if( ( aControl == currentdLine ) && aControl->iIsEditable )
{
return visibleCtrls;
}
TBool isEdwin = aControl->ControlIsAnEdwin(aControl->iControlType);
CEikEdwin* edwin( NULL );
if( isEdwin )
edwin = (CEikEdwin*)aControl->iControl;
TRect ctrlRect( aControl->iControl->Rect() );
if( isEdwin )
{
// Adjust rectangle only to the first line (with edwin's top margin)
TRect textFirstLine;
edwin->TextLayout()->GetLineRect(ctrlRect.iTl.iY, textFirstLine);
ctrlRect.iBr.iY = ctrlRect.iTl.iY + edwin->Margins().iTop + textFirstLine.Height();
}
// Find the minimal clipping rectangle
if( aControl->iBitmap )
GetVertMinRect( aClipRect, aControl->iBitmap->Rect() );
if( aControl->iCaption )
GetVertMinRect( aClipRect, aControl->iCaption->Rect() );
GetVertMinRect( aClipRect, ctrlRect );
// Try to hide all controls on the current line
aControl->SetPartiallyVisible( ETrue );
visibleCtrls -= HideLines_Ctrl( aControl->iBitmap, aClipRect );
visibleCtrls -= HideLines_Ctrl( aControl->iCaption, aClipRect );
if( isEdwin )
visibleCtrls -= HideLines_Edwin( edwin, aClipRect );
else
visibleCtrls -= HideLines_Ctrl( aControl->iControl, aClipRect );
return visibleCtrls;
}
/**
* Places the dialog items according to the current visible window position.
* When hiding items, uses SetExtent() instead of just SetPosition() in order to call
* SizeChanged() of the moved item. For example, this is needed for the editor to
* change its cursor accordingly.
*
* @param aRect Rectangle of the form window
* @param aTop Number of items above the window
* @param aMiddle Number of items inside the window
* @param aBottom Number of items below the window
*
* Parameter value equal to -1 means that the parameter is not defined and will be
* calculated automatically. Normally, either the top number or the bottom one
* is only defined. If @a aTop is defined, the items will be placed from top downwards, leaving
* @a aTop items above the window. If @a aBottom is defined, the items will be placed from bottom
* upwards, leaving @a aBottom items below the window.
*
* This function panics, if neither @a aTop nor @a aBottom are defined.
*
* The function first checks if the provided @a aTop and @a aBottom are consistent with
* the item sizes and the given window rectangle. If they are not, they will be
* corrected. Usually, @a aTop and @a aBottom come out of sync with the item sizes
* after the dynamic layout change.
*/
void CEikCapCArray::SetRect(const TRect& aRect, TInt aTop, TInt /*aMiddle*/, TInt aBottom)
{
_AKNTRACE_FUNC_ENTER;
TAknLayoutRect formtLayoutRect;
formtLayoutRect.LayoutRect(aRect, AknLayoutScalable_Avkon::listscroll_form_pane().LayoutLine());
formtLayoutRect.LayoutRect(formtLayoutRect.Rect(), AknLayoutScalable_Avkon::list_form_gen_pane().LayoutLine());
TRect formRect = formtLayoutRect.Rect();
CEikCapCArrayExtension *extension_or_null = ExtensionOrNull();
if (extension_or_null)
{
extension_or_null->iRect = formRect;
}
// controls need to be placed in real locations if physics is enabled
if ( Count() > 0 )
{
CEikCaptionedControl* control = (*this)[0];
if ( control->DialogPage()->IsForm() )
{
SetRealRect( aRect, aTop, aBottom );
_AKNTRACE_FUNC_EXIT;
return;
}
}
TBool topDefined = EFalse; // top or bottom number defined?
if( aTop > -1 )
{
topDefined = ETrue;
}
else if( aBottom > -1 )
{
topDefined = EFalse;
}
else // aBottom == aTop == -1
{
User::Panic( _L("CEikCapCArray::SetRect(): Neither top nor bottom items number defined"), EAknPanicInvalidValue );
}
const TInt count = Count();
const TInt rectHeight = aRect.Height();
/**
* Special invisible points are used for placing the items that are
* outside the window. CCoeControl's invisible flag cannot be used,
* as it is controlled by third-party applications.
*/
const TPoint topInvisPoint( -10000, -10000 );
const TPoint bottomInvisPoint( 10000, 10000 );
CEikCaptionedControl *firstCapCC = count > 0 ? (*this)[0] : NULL;
if( firstCapCC && firstCapCC->iIsFormControl ) // Forms
{
CEikCaptionedControl *selectedLine( NULL );
if( firstCapCC->DialogPage())
selectedLine = firstCapCC->DialogPage()->CurrentLine();
// Check height of items and the input parameters aTop and aBottom.
TInt rest = 0; // number of the rest items without aTop or aBottom
TInt index = 0;
if( topDefined )
{
rest = count - aTop;
index = aTop;
}
else
{
rest = count - aBottom;
index = rest - 1;
}
TInt height = 0;
for( TInt ii = 0; ii < rest; ii++ )
{
CEikCaptionedControl* line = (*this)[index];
height += line->MinimumSize().iHeight; // Use MinimumSize() here as a protection from dynamic layout change
if( height >= rectHeight )
break; // Input params are OK
topDefined? index++ : index--;
}
/**
* If the window contains too few items inside and there are still items outside,
* correct the input parameters @a aTop and @a aBottom to fill up the window.
*/
if( height < rectHeight )
{
if( topDefined && aTop > 0 ) // For top-down placement and there are items above the window
{
// Calculate height of controls above the window also
for( TInt ii = 0; ii < aTop; ii++ )
{
CEikCaptionedControl* line = (*this)[ii];
height += line->MinimumSize().iHeight;
if( height >= rectHeight ) // All items don't fit to the window anyway
{
topDefined = EFalse; // Reverse direction to bottom-up
aBottom = 0;
break;
}
}
if( height < rectHeight ) // All items fit to the window
{
aTop = 0; // Just place them from the first item
}
}
else if( !topDefined ) // For bottom-up placement
{
topDefined = ETrue; // Reverse direction to top-down
aTop = 0;
}
}
// Hiding items that are explicitly defined to be outside the window
TInt start;
TInt end;
TPoint invisPoint; // current invisible point, depends on placement direction
if( topDefined )
{
start = 0;
end = aTop;
invisPoint = topInvisPoint;
}
else
{
start = count - aBottom;
end = count;
invisPoint = bottomInvisPoint;
}
for( TInt ii = start; ii < end; ii++ )
{
CEikCaptionedControl* line = (*this)[ii];
line->SetPosition( invisPoint );
}
// Setting rects for the rest of the items
if( topDefined )
{
rest = count - aTop;
invisPoint = bottomInvisPoint;
index = aTop;
}
else
{
rest = count - aBottom;
invisPoint = topInvisPoint;
index = rest - 1;
}
TInt reservedHeight = 0; // in pixels
TBool insideWindow = ETrue; // The current item is still inside the window
TInt topY = 0;
for( TInt ii = 0; ii < rest; ii++ )
{
CEikCaptionedControl* line = (*this)[index];
TSize lineSize( line->Size() );
if( insideWindow )
{
ResetHides( line );
if( topDefined )
{ // Top-down placement
topY = aRect.iTl.iY + reservedHeight;
}
else
{ // Bottom-up placement
topY = aRect.iBr.iY - reservedHeight - lineSize.iHeight;
}
line->SetExtent( TPoint( formRect.iTl.iX, topY ), lineSize );
AknsUtils::RegisterControlPosition( line );
AknsUtils::RegisterControlPosition( line->iCaption );
AknsUtils::RegisterControlPosition( line->iControl );
AknsUtils::RegisterControlPosition( line->iTrailer );
AknsUtils::RegisterControlPosition( line->iBitmap );
reservedHeight += lineSize.iHeight;
/**
* The control at a window edge is considered as partially-visible.
* Its subcontrols must be checked for visibility individually.
*/
if( reservedHeight > rectHeight )
{
TInt visibleSubctrls = HideLines( line, aRect ); // Check how many subcontrols stayed visible
insideWindow = EFalse;
/**
* For the bottom-up placement:
* if the window contains only an empty "partially-visible" control and a
* a selected popup field, make the popup to hang at the top alone.
*/
if( !topDefined && index < count - 1 ) // bottom-up and not last
{
CEikCaptionedControl* lineBelow = (*this)[index+1];
if( visibleSubctrls == 0 && ii == 1 &&
IsPopupField( lineBelow ) && lineBelow == selectedLine )
{
TRect popupRect( lineBelow->Rect() );
TInt diff = aRect.iTl.iY - popupRect.iTl.iY; // negative
popupRect.Move( 0, diff );
lineBelow->SetRect( popupRect );
}
}
}
}
else
{
line->SetPosition( invisPoint );
}
topDefined? index++ : index--;
}
}
else // Dialogs other than forms:
{
TRect rect=aRect;
const TInt fullWidth=rect.iBr.iX-rect.iTl.iX;
const TInt count=Count();
const TInt topMargin=iDensePacking ? KAknNoTopMargin : KAknTopMargin;
const TInt verticalSpacing=iDensePacking ? KVerticalSpacingSquash : KVerticalSpacing;
rect.iTl.iY+=topMargin;
TInt deltaHeight=0;
for (TInt ii=0;ii<count;++ii)
{
CEikCaptionedControl* line=(*this)[ii];
TSize thisSize=line->MinimumSize();
TInt thisDeltaHeight=thisSize.iHeight+verticalSpacing;
if (deltaHeight<thisDeltaHeight)
deltaHeight=thisDeltaHeight;
if (!(line->iCaptionWidth))
thisSize.iWidth=fullWidth;
else
{
CEikCapCArrayExtension *ext = ExtensionOrNull();
TInt deltaWidth = 0;
if (ext)
deltaWidth = ext->iCaptionWidth-line->iCaptionWidth;
thisSize.iWidth+=deltaWidth;
if (ext)
line->iCaptionWidth=ext->iCaptionWidth;
else
line->iCaptionWidth = 0;
line->iMinSize.iWidth+=deltaWidth;
}
line->iFullWidth=fullWidth;
line->SetExtent(rect.iTl,thisSize);
if (!(line->LatentGroupLineFollows()))
{
rect.iTl.iY+=deltaHeight;
deltaHeight=0;
}
}
}
_AKNTRACE_FUNC_EXIT;
}
TInt CEikCapCArray::YPosToLine(const TRect &aRect,
TInt aTop, TInt aMiddle, TInt aBottom,
TInt aYCoord)
{
TInt top = aTop;
TInt middle = aMiddle;
TInt bottom = aBottom;
CalcItemIndexes(top, middle, bottom, aRect.Size());
for(int i = top ; i < top+middle; i++)
{
CEikCaptionedControl *fst = (*this)[i];
if (aYCoord < fst->Rect().iTl.iY)
{
if (i > 0)
return i-1;
else
return KErrNotFound;
}
}
if (Count() == 0) return -1;
TInt ii = top+middle-1;
CEikCaptionedControl *last = (*this)[ii];
if ( aYCoord < last->Rect().iBr.iY )
{
return ii;
}
else
{
if ( ii+1 < Count() )
{
return ii+1;
}
else if ( aYCoord > last->Rect().iBr.iY )
{
return KErrNotFound;
}
else
{
return ii;
}
}
}
EXPORT_C void CEikCapCArray::ResetMinimumSizes()
{
const TInt count=Count();
for (TInt ii=0;ii<count;++ii)
(*this)[ii]->ResetMinimumSizes();
}
EXPORT_C TInt CEikCapCArray::LineIndexFromId(TInt aControlId) const
{
const TInt count=Count();
TInt ii=0;
FOREVER
{
if (ii==count)
return(KErrNotFound);
if ((*this)[ii]->iId==aControlId)
break;
ii++;
}
return(ii);
}
EXPORT_C void CEikCapCArray::AdjustAllIds(TInt aControlIdDelta)
{
const TInt count=Count();
for (TInt ii=0; ii<count; ii++)
(*this)[ii]->iId+=aControlIdDelta;
}
EXPORT_C TInt CEikCapCArray::FindLineIndex(const CCoeControl* aControl) const
{
const TInt count=Count();
TInt ii=0;
while (ii<count)
{
CEikCaptionedControl* line=(*this)[ii];
if (line==aControl || line->iControl==aControl)
return(ii);
ii++;
}
return(KErrNotFound);
}
// ---------------------------------------------------------------------------
// CEikCapCArray::ScrollByPixels
// ---------------------------------------------------------------------------
//
TInt CEikCapCArray::ScrollByPixels( TInt aDelta )
{
_AKNTRACE_FUNC_ENTER;
TInt count = Count();
// Top and bottom of lines
TInt topY = (*this)[0]->Rect().iTl.iY;
TInt bottomY = (*this)[count - 1]->Rect().iBr.iY;
TRect formRect( Rect() );
if ( aDelta )
{
for( TInt i = 0; i < count; ++i )
{
CEikCaptionedControl* line = (*this)[i];
TBool onDisplay = line->Rect().Intersects( formRect );
TPoint position( line->Position() );
position.iY += aDelta;
line->SetPosition( position );
onDisplay = onDisplay || line->Rect().Intersects( formRect );
// Line is or was on display
if ( ETrue /*onDisplay*/ )
// Some controls, eg. slider and edwin don't handle
// SetPosition properly. Workaround is to use SetRect,
// which is slow as it does a whole layout for the control.
// If form panning is ever speed optimized, captioned
// control should me made to support SetPosition() correctly.
{
if ( line->ControlIsAPopfield( line->iControlType ) )
{
// Have to layout whole captioned control, otherwise
// text doesn't move. Fix later popup field to move
// properly.
line->SetRect( line->Rect() );
}
else
{
line->iControl->SetRect( line->iControl->Rect() );
}
line->DrawDeferred();
//line->DrawNow();
}
}
}
_AKNTRACE_FUNC_EXIT;
return aDelta;
}
// ---------------------------------------------------------------------------
// CEikCapCArray::SetRealRect
// ---------------------------------------------------------------------------
//
void CEikCapCArray::SetRealRect(
const TRect& /*aRect*/, TInt /*aTop*/, TInt /*aBottom*/ )
{
_AKNTRACE_FUNC_ENTER;
TInt count = Count();
const TPoint virtualTl( 1000, 0 );
TPoint skinLocation( 0, 0 );
CEikCaptionedControl* currentLine = (*this)[0]->DialogPage()->CurrentLine();
for ( TInt i = 0; i < count; ++i )
{
CEikCaptionedControl* line = (*this)[i];
if ( line != currentLine )
{
line->SetExtent( virtualTl, line->Size() );
}
else
{
line->SetExtent( line->Position(), line->Size() );
}
skinLocation.iY += line->Size().iHeight;
AknsUtils::RegisterControlPosition( line, skinLocation );
AknsUtils::RegisterControlPosition( line->iCaption, skinLocation );
AknsUtils::RegisterControlPosition( line->iControl, skinLocation );
AknsUtils::RegisterControlPosition( line->iTrailer, skinLocation );
AknsUtils::RegisterControlPosition( line->iBitmap, skinLocation );
}
_AKNTRACE_FUNC_EXIT;
}
// ---------------------------------------------------------------------------
// CEikCapCArray::YPositionToLineIndex
// ---------------------------------------------------------------------------
//
TInt CEikCapCArray::YPositionToLineIndex( TInt aYPos ) const
{
TInt count = Count();
TInt topY = 0;
TInt bottomY = 0;
for ( TInt i = 0; i < count; ++i )
{
topY = bottomY;
bottomY = topY + (*this)[i]->Size().iHeight;
if ( aYPos >= topY && aYPos < bottomY )
{
return i;
}
}
return KErrNotFound;
}
// ---------------------------------------------------------------------------
// CEikCapCArray::LineIndexToYPosition
// ---------------------------------------------------------------------------
//
TInt CEikCapCArray::LineIndexToYPosition( TInt aLine, TInt aTopY ) const
{
TInt topY = 0;
TInt bottomY = 0;
for ( TInt i = 0; i <= aLine; ++i )
{
topY = bottomY;
bottomY = topY + (*this)[i]->Size().iHeight;
}
return topY - aTopY;
}
// ---------------------------------------------------------------------------
// CEikCapCArray::MoveLineToScreen
// ---------------------------------------------------------------------------
//
void CEikCapCArray::MoveLineToScreen( TInt aLine, TInt aTopY, TBool aVisible )
{
_AKNTRACE_FUNC_ENTER;
_AKNTRACE( "aLine: [%d], aTopY: [%d], aVisible: [%d]", aLine, aTopY, aVisible );
if ( aLine != -1 && aLine < Count() )
{
CEikCaptionedControl* line = (*this)[aLine];
TPoint topLeft( 1000, 0 );
if ( aVisible )
{
topLeft.SetXY( Rect().iTl.iX, LineIndexToYPosition( aLine, aTopY ) );
}
line->SetRect( TRect( topLeft, line->Size() ) );
}
_AKNTRACE_FUNC_EXIT;
}
// ---------------------------------------------------------------------------
// CEikCapCArray::FocusableLine
// ---------------------------------------------------------------------------
//
TInt CEikCapCArray::FocusableLine( TInt aCurrentLine, TInt aTopY )
{
_AKNTRACE_FUNC_ENTER;
_AKNTRACE( "aCurrentLine: [%d]", aCurrentLine );
TInt focusableLine = KErrNotFound;
if ( aCurrentLine >= 0 && aCurrentLine < Count() )
{
TInt y = LineIndexToYPosition( aCurrentLine, aTopY );
// if current line is visible on the screen the focus that
if ( y >= 0 && y <= Rect().Height() )
{
focusableLine = aCurrentLine;
}
else // otherwise return the topmost totally visible line
{
TInt topLine = YPositionToLineIndex( aTopY );
if ( topLine != KErrNotFound &&
(*this)[topLine]->Position().iY < aTopY )
{
if ( ( topLine + 1 ) < Count() )
{
++topLine;
}
focusableLine = topLine;
}
}
}
_AKNTRACE_FUNC_EXIT;
return focusableLine;
}