// EIKSBFRM.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//
		    
#include <eiksbfrm.h>
#include <eikpanic.h>

//
// TEikScrollBarFrameLayout
//

EXPORT_C TEikScrollBarFrameLayout::TEikScrollBarFrameLayout()
	//
	// default constructor
	//
	{
	iInclusiveMargin.iLeft=iInclusiveMargin.iRight=iInclusiveMargin.iTop=iInclusiveMargin.iBottom=0;
	iClientMargin.iLeft=iClientMargin.iRight=iClientMargin.iTop=iClientMargin.iBottom=0;
	iClientAreaGranularity=TSize(1,1);
	iTilingMode=EClientRectConstant;
	}

EXPORT_C void TEikScrollBarFrameLayout::SetInclusiveMargin(TInt aMargin)
	//
	// sets all inclusive margins to aMargin
	//
	{
	iInclusiveMargin.iLeft=iInclusiveMargin.iRight=iInclusiveMargin.iTop=iInclusiveMargin.iBottom=aMargin;
	}

EXPORT_C void TEikScrollBarFrameLayout::SetClientMargin(TInt aMargin)
	//
	// sets all client margins to aMargin
	//
	{
	iClientMargin.iLeft=iClientMargin.iRight=iClientMargin.iTop=iClientMargin.iBottom=aMargin;
	}

//
// CEikCornerWindow
//

class CEikCornerWindow : public CCoeControl
	{
public:
	void ConstructL(CCoeControl* aParentWindow);
	};

void CEikCornerWindow::ConstructL(CCoeControl* aParentWindow)
	{
	CreateWindowL(aParentWindow);
	SetBlank();
	}

//
// CEikScrollBarFrame
//

enum // private flags
	{
	EDisplayHScrollBar			=0x01,
	EDisplayVScrollBar			=0x02,
	EDoNotAdjustHorizontalModel	=0x04,
	EDoNotAdjustVerticalModel	=0x08,
	EPreAllocScrollBars			=0x10
	};

EXPORT_C CEikScrollBarFrame::~CEikScrollBarFrame()
	{
	delete(iHSbar);
	delete(iVSbar);
	delete(iCornerWindow);
	}

EXPORT_C CEikScrollBarFrame::CEikScrollBarFrame(CCoeControl* aParentWindow, MEikScrollBarObserver* aObserver, TBool aPreAlloc)
	: iObserver(aObserver),
	  iParentWindow(aParentWindow)
	{
	__DECLARE_NAME(_S("CEikScrollBarFrame"));
	iHScrollBarBreadth=CEikScrollBar::EScrollbarWidth;
	iVScrollBarBreadth=CEikScrollBar::EScrollbarWidth;
	if (aPreAlloc)
		iScrollBarFrameFlags=EPreAllocScrollBars;
	}

EXPORT_C void CEikScrollBarFrame::DrawScrollBarsNow() const
// Force a redraw of any scrollbars
	{
	if (iHSbar)
		iHSbar->DrawNow();
	if (iVSbar)
		iVSbar->DrawNow();
	if (iCornerWindow)
		iCornerWindow->DrawNow();
	}

const TInt KLongEnoughToCauseComponentsToBeCreated=200;

CEikScrollBar* CEikScrollBarFrame::CreateSBarLC(CEikScrollBar::TOrientation aOrientation,TInt aFlags) const
	{
	CEikScrollBar* bar=new(ELeave) CEikScrollBar;
	CleanupStack::PushL(bar);
	bar->ConstructL(iObserver,iParentWindow,aOrientation,KLongEnoughToCauseComponentsToBeCreated,aFlags);
	return(bar);
	}

CEikScrollBar* CEikScrollBarFrame::CreateHSBarLC() const
	{
	return CreateSBarLC(CEikScrollBar::EHorizontal,iHScrollBarFlags);
	}

CEikScrollBar* CEikScrollBarFrame::CreateVSBarLC() const
	{
	return CreateSBarLC(CEikScrollBar::EVertical,iVScrollBarFlags);
	}

CEikCornerWindow* CEikScrollBarFrame::CreateCornerWindowLC() const
	{
	CEikCornerWindow* cornerWindow=new(ELeave) CEikCornerWindow;
	CleanupStack::PushL(cornerWindow);
	cornerWindow->ConstructL(iParentWindow);
	cornerWindow->SetSizeL(TSize(0,0));
	return(cornerWindow);
	}

EXPORT_C void CEikScrollBarFrame::SetScrollBarVisibilityL(TScrollBarVisibility aHVisibility, TScrollBarVisibility aVVisibility)
// Set the visibility state for both scrollbars to be used when tiling
	{
	if (iScrollBarFrameFlags&EPreAllocScrollBars)
		{
		CEikScrollBar* hSBar=NULL;
		CEikScrollBar* vSBar=NULL;
		CEikCornerWindow* cornerWindow=NULL;
		if (aHVisibility!=EOff && !iHSbar)
			{
			hSBar=CreateHSBarLC();
			hSBar->MakeVisible(EFalse);
			}
		if (aVVisibility!=EOff && !iVSbar)
			{
			vSBar=CreateVSBarLC();
			vSBar->MakeVisible(EFalse);
			}
		if (aVVisibility!=EOff && aHVisibility!=EOff && !iCornerWindow)
			{
			cornerWindow=CreateCornerWindowLC();
			cornerWindow->MakeVisible(EFalse);
			}
		// can't fail from here on
		if (cornerWindow)
			{
			iCornerWindow=cornerWindow;
			CleanupStack::Pop();
			}
		if (vSBar)
			{
			iVSbar=vSBar;
			CleanupStack::Pop();
			}
		if (hSBar)
			{
			iHSbar=hSBar;
			CleanupStack::Pop();
			}
		// Any scrollbars no longer required will be removed at next call to TileL()
		}
	iHVisibility=aHVisibility;
	iVVisibility=aVVisibility;
	}

EXPORT_C void CEikScrollBarFrame::SetScrollBarsDimmed(TBool aHorizDimmed, TBool aVertDimmed) const
	{
	if (iHSbar)
		iHSbar->SetDimmed(aHorizDimmed);
	if (iVSbar)
		iVSbar->SetDimmed(aVertDimmed);
	}

EXPORT_C void CEikScrollBarFrame::SetAdjustsHorizontalModel(TBool aAdjusts)
	{
	if (aAdjusts)
		iScrollBarFrameFlags&=(~EDoNotAdjustHorizontalModel);
	else
		iScrollBarFrameFlags|=EDoNotAdjustHorizontalModel;
	}

EXPORT_C void CEikScrollBarFrame::SetAdjustsVerticalModel(TBool aAdjusts)
	{
	if (aAdjusts)
		iScrollBarFrameFlags&=(~EDoNotAdjustVerticalModel);
	else
		iScrollBarFrameFlags|=EDoNotAdjustVerticalModel;
	}

EXPORT_C TBool CEikScrollBarFrame::TileL(TEikScrollBarModel* aHModel, TEikScrollBarModel* aVModel,
			  TRect& aClientRect, TRect& aInclusiveRect, const TEikScrollBarFrameLayout& aLayout)
	//
	// The main function which provides the necessary scrollbars according to visibilities and size considerations
	// returns ETrue if the "output rect" was changed
	//
	{
	// output rect is client rect if tiling mode is EInclusiveSizeConstant and vice versa
	TRect& outputRect=(aLayout.iTilingMode==TEikScrollBarFrameLayout::EClientRectConstant)? aInclusiveRect : aClientRect;
	TRect oldOutputRect(outputRect);
	CalcTheoreticalScrollBarVisibility(aHModel, aVModel);
	switch (aLayout.iTilingMode)
		{
	case TEikScrollBarFrameLayout::EClientRectConstant:	// all cases "easy"
		// outputRect is reference for inclusive rect which expands for scrollbars
		outputRect=aClientRect;
		AdjustRectByMargins(EGrow, outputRect, aLayout.iInclusiveMargin);
		break;
	case TEikScrollBarFrameLayout::EInclusiveRectConstant:
		// outputRect is reference for client rect which shrinks for scrollbars
		outputRect=aInclusiveRect;
		AdjustRectByMargins(EShrink, outputRect, aLayout.iInclusiveMargin);
		break;
		}
	CheckPhysicalScrollBarVisibility(aHModel, aVModel, outputRect, aLayout);
	CreateOrRemoveRequiredScrollBarsL();
	AdjustRect(outputRect, aLayout.iTilingMode);
	TileScrollBarsWithClientL(aClientRect, aHModel, aVModel);
	if (iCornerWindow)
		{
		iCornerWindow->SetRectL(TRect(aClientRect.iBr+TPoint(1,1), aInclusiveRect.iBr-TPoint(aLayout.iInclusiveMargin.iRight, aLayout.iInclusiveMargin.iBottom)));
		iCornerWindow->ActivateL();
		}
	return (oldOutputRect!=outputRect);
	}

EXPORT_C void CEikScrollBarFrame::MoveThumbsBy(TInt aDeltaX, TInt aDeltaY) const
	//
 	// Used to update any scrollbar(s) after an external scroll
	// values in terms of the model, presumed not to have changed spans.
	//
	{
	TInt thumbPos;
	if (aDeltaX && iHSbar)
		{
		thumbPos=iHSbar->ThumbPosition()+aDeltaX;
		iHSbar->SetModelThumbPosition(thumbPos);
		}
	if (aDeltaY && iVSbar)
		{
		thumbPos=iVSbar->ThumbPosition()+aDeltaY;
		iVSbar->SetModelThumbPosition(thumbPos);
		}
	}

EXPORT_C void CEikScrollBarFrame::MoveHorizThumbTo(TInt aHorizThumbPos) const
	//
	// Used to update horizontal scrollbar after an external scroll
	// value in terms of the model, presumed not to have changed spans.
	//
	{
	if (iHSbar)
		iHSbar->SetModelThumbPosition(aHorizThumbPos);
	}

EXPORT_C void CEikScrollBarFrame::MoveVertThumbTo(TInt aVertThumbPos) const
	//
	// Used to update vertical scrollbar after an external scroll
	// value in terms of the model, presumed not to have changed spans.
	//
	{
	if (iVSbar)
		iVSbar->SetModelThumbPosition(aVertThumbPos);
	}

EXPORT_C TInt CEikScrollBarFrame::ScrollBarBreadth(CEikScrollBar::TOrientation aOrientation) const
	//
	// return the appropriate scrollbar's breadth or zero if none exists
	//
	{
	CEikScrollBar* sBar=GetScrollBarHandle(aOrientation);
	if (!sBar)
		return 0;
	return sBar->ScrollBarBreadth(); 
	}

EXPORT_C TBool CEikScrollBarFrame::ScrollBarExists(CEikScrollBar::TOrientation aOrientation) const
	//
	// returns ETrue if the specified scrollbar exists
	//
	{
	if (GetScrollBarHandle(aOrientation))
		return ETrue;
	return EFalse;
	}

EXPORT_C TInt CEikScrollBarFrame::CountComponentControls() const
	{
	TInt count=0;
	if (iHSbar && iScrollBarFrameFlags&EDisplayHScrollBar)
		count++;
	if (iVSbar && iScrollBarFrameFlags&EDisplayVScrollBar)
		{
		count++;
		if (iCornerWindow)
			count++;
		}
	return count;
	}

EXPORT_C CCoeControl* CEikScrollBarFrame::ComponentControl(TInt aIndex) const
	{
	CEikScrollBar* hBar=GetScrollBarHandle(CEikScrollBar::EHorizontal);
	CEikScrollBar* vBar=GetScrollBarHandle(CEikScrollBar::EVertical);
	if (aIndex==0)
		{
		if (hBar)
			return hBar;
		if (vBar)
			return vBar;
		}
	else if (aIndex==1 && hBar && vBar)
		return vBar;
	return iCornerWindow;	
	}

EXPORT_C CEikScrollBar* CEikScrollBarFrame::GetScrollBarHandle(CEikScrollBar::TOrientation aOrientation) const
	//
	// returns the pointer to the appropriate scrollbar
	//
	{
	if (aOrientation==CEikScrollBar::EVertical)
		{
		if (iScrollBarFrameFlags&EDisplayVScrollBar)
			return(iVSbar);
		}
	else if (iScrollBarFrameFlags&EDisplayHScrollBar)
		return(iHSbar);
	return(NULL);
	}

void CEikScrollBarFrame::CalcTheoreticalScrollBarVisibility(const TEikScrollBarModel* aHModel, const TEikScrollBarModel* aVModel)
	//
	// Determines which scrollbars are necessary before size considerations are taken into account
	//
	{
	// assume no scrollbars to start with
	iScrollBarFrameFlags&=(~(EDisplayHScrollBar|EDisplayVScrollBar));
	if ( (iHVisibility==EOn) || ( (iHVisibility==EAuto)&&(aHModel->ScrollBarUseful()) ) )
		iScrollBarFrameFlags|=EDisplayHScrollBar;
	if ( (iVVisibility==EOn) || ( (iVVisibility==EAuto)&&(aVModel->ScrollBarUseful()) ) )
		iScrollBarFrameFlags|=EDisplayVScrollBar;
	}

void CEikScrollBarFrame::CheckPhysicalScrollBarVisibility(TEikScrollBarModel* aHModel, TEikScrollBarModel* aVModel, const TRect& aOutputRect, const TEikScrollBarFrameLayout& aLayout)
	//
	// Checks if required scrollbars will fit and adjusts (internal) visibility if not enough room.
	//
	{
	TInt tilingWidth=aOutputRect.Width();
	TInt tilingHeight=aOutputRect.Height();
	if (aLayout.iTilingMode==TEikScrollBarFrameLayout::EClientRectConstant)
		{
		// aOutputRect is InclusiveRect which will expand
		if (iScrollBarFrameFlags&EDisplayHScrollBar)
			tilingHeight+=iHScrollBarBreadth;
		if (iScrollBarFrameFlags&EDisplayVScrollBar)
			tilingWidth+=iVScrollBarBreadth;
		}
	// Vertical scrollbar always takes priority
	if (iScrollBarFrameFlags&EDisplayVScrollBar)
		{
		if ((CEikScrollBar::MinVisibleLength(iVScrollBarFlags)>tilingHeight)||(iVScrollBarBreadth>tilingWidth))
			{
			// no room for vertical scrollbar
			iScrollBarFrameFlags&=(~EDisplayVScrollBar);
			}
		else
			{
			// can display a vertical scrollbar
			tilingWidth-=iVScrollBarBreadth;
			if (aLayout.iTilingMode==TEikScrollBarFrameLayout::EInclusiveRectConstant)
				{
				// modify horizontal model
				if (!(iScrollBarFrameFlags&EDoNotAdjustHorizontalModel))
					{
					AdjustScrollBarModel(CEikScrollBar::EHorizontal, aHModel, tilingWidth, aLayout);
					// turning on vertical scrollbar may mean we need a horizontal scrollbar so check this
					if ((!(iScrollBarFrameFlags&EDisplayHScrollBar))&&(aHModel->ScrollBarUseful())&&(iHVisibility==EAuto))
						iScrollBarFrameFlags|=EDisplayHScrollBar;
					}
				}
			}
		}
	if (iScrollBarFrameFlags&EDisplayHScrollBar)
		{
		if ((CEikScrollBar::MinVisibleLength(iHScrollBarFlags)>tilingWidth)||(iHScrollBarBreadth>tilingHeight))
			{
			// no room for horizontal scrollbar
			iScrollBarFrameFlags&=(~EDisplayHScrollBar);
			}
		else
			{
			// There is enough space to display horizontal scrollbar but need to check
			// that adding a horizontal scrollbar leaves enough space for the vertical scrollbar
			tilingHeight-=iHScrollBarBreadth;
			if (iScrollBarFrameFlags&EDisplayVScrollBar)	
				{	// also displaying vertical scrollbar
				if (CEikScrollBar::MinVisibleLength(iVScrollBarFlags)>tilingHeight)
					{
					// would leave not enough room for vertical scrollbar which has priority
					iScrollBarFrameFlags&=(~EDisplayHScrollBar);
					tilingHeight+=iHScrollBarBreadth;
					}
				}
			if ((iScrollBarFrameFlags&EDisplayHScrollBar)&&(aLayout.iTilingMode==TEikScrollBarFrameLayout::EInclusiveRectConstant))
				{
				// modify the vertical model
				if (!(iScrollBarFrameFlags&EDoNotAdjustVerticalModel))
					{
					AdjustScrollBarModel(CEikScrollBar::EVertical, aVModel, tilingHeight, aLayout);
					// turning on horizontal scrollbar may mean we need a vertical scrollbar so check this
					if ((!(iScrollBarFrameFlags&EDisplayVScrollBar))&&(aVModel->ScrollBarUseful())&&(iVVisibility==EAuto))
						{
						// Add the vertical scrollbar if there is enough room
						if ((CEikScrollBar::MinVisibleLength(iVScrollBarFlags)<tilingHeight)&&(iVScrollBarBreadth<tilingWidth))
							iScrollBarFrameFlags|=EDisplayVScrollBar;	
						}
					}
				}
			}
		}
	}

void CEikScrollBarFrame::AdjustRect(TRect& aOutputRect, TEikScrollBarFrameLayout::TTilingMode aTilingMode) const
	{
	// Shrinks/Grows the ClientRect/InclusiveRect respectively to account for adding scrollbars
	TBool vSbarOnRight=ETrue;	// for now
	if (iScrollBarFrameFlags&EDisplayVScrollBar) // display a vertical scrollbar
		{
		if (vSbarOnRight)
			aOutputRect.iBr.iX+=(aTilingMode==TEikScrollBarFrameLayout::EClientRectConstant)? iVScrollBarBreadth : -iVScrollBarBreadth;
// v2?	else
//			aOutputRect.iTl.iX-=(aTilingMode==EClientRectConstant)? iVScrollBarBreadth : -iVScrollBarBreadth;;
		}
	if (iScrollBarFrameFlags&EDisplayHScrollBar) // display a horizontal scrollbar
		{
		aOutputRect.iBr.iY+=(aTilingMode==TEikScrollBarFrameLayout::EClientRectConstant)? iHScrollBarBreadth : -iHScrollBarBreadth ;
		}
	}

void CEikScrollBarFrame::CheckVisible(CCoeControl* aComponent)
	{ // static
	if (!aComponent->IsVisible())
		aComponent->MakeVisible(ETrue);
	}

void CEikScrollBarFrame::CreateOrRemoveRequiredScrollBarsL()
	//
	// Creates/Removes any scrollbars and the "corner window" as required according to internal display flags
	//
	{
	TBool visible=iParentWindow->IsVisible();
	if (!(iScrollBarFrameFlags&EDisplayHScrollBar))
		RemoveScrollBar(iHSbar);
	else
		{
		if (!iHSbar)
			{
			iHSbar=CreateHSBarLC();
			CleanupStack::Pop();
			if (!visible)
				iHSbar->MakeVisible(EFalse);
			}
		else if (visible)
			CheckVisible(iHSbar);
		}
	if (!(iScrollBarFrameFlags&EDisplayVScrollBar))
		RemoveScrollBar(iVSbar);
	else
		{
		if (!iVSbar)
			{
			iVSbar=CreateVSBarLC();
			CleanupStack::Pop();
			if (!iParentWindow->IsVisible())
				iVSbar->MakeVisible(EFalse);
			}
		else if (visible)
			CheckVisible(iVSbar);
		}
	if (iHSbar && iVSbar)
		{ // have both scrollbars so need the Corner window
		if (!iCornerWindow)
			{
			iCornerWindow=CreateCornerWindowLC();
			CleanupStack::Pop();
			if (!iParentWindow->IsVisible())
				iCornerWindow->MakeVisible(EFalse);
			}
		else if (visible)
			CheckVisible(iCornerWindow);
		}
	}

void CEikScrollBarFrame::RemoveScrollBar(CEikScrollBar*& aScrollBar)
	// 
	// Destroy the appropriate scrollbar and the corner window
	//
	{
	if (!aScrollBar)
		return; // already removed
	if (!(iScrollBarFrameFlags&EPreAllocScrollBars))
		{
		delete(aScrollBar);
		aScrollBar=NULL;
		// no longer need the Corner window since can't have two scrollbars at this point
		delete(iCornerWindow);
		iCornerWindow=NULL;
		return;
		}
	if (!(aScrollBar->IsVisible()))
		return; // already hidden
	aScrollBar->MakeVisible(EFalse);
	if (iCornerWindow)
		iCornerWindow->MakeVisible(EFalse);
	}

void CEikScrollBarFrame::TileScrollBarsWithClientL(const TRect& aClientRect, const TEikScrollBarModel* aHModel, const TEikScrollBarModel* aVModel)
	//
	// Sets any scrollbars position, length and model
	//
	{
	TBool vSbarOnRight=ETrue;	// hard-wired for v1
	if (iHSbar && iHSbar->IsVisible())
		{
		TPoint hSbarPos=aClientRect.iTl;
		hSbarPos.iY=aClientRect.iBr.iY;	
		TInt hSbarLength=aClientRect.Width(); 
		if (iVSbar)
			hSbarLength+=1;	// tidy up corner when we have two scrollbars 
		iHSbar->SetPosition(hSbarPos);
		iHSbar->SetLengthAndModelL(hSbarLength, aHModel); 
		iHSbar->ActivateL();
		}
	if (iVSbar && iVSbar->IsVisible())
		{
		TPoint vSbarPos=aClientRect.iTl;
		if (vSbarOnRight)
			vSbarPos.iX=aClientRect.iBr.iX;	
//		else
//			vSbarPos.iX-=scrollBarWidth;
		TInt vSbarLength=aClientRect.Height();
		if (iHSbar)
			vSbarLength+=1;	// tidy up corner when we have two scrollbars 
		iVSbar->SetPosition(vSbarPos);
		iVSbar->SetLengthAndModelL(vSbarLength, aVModel);
		iVSbar->ActivateL();
		}
	}

TBool CEikScrollBarFrame::AdjustScrollBarModel(CEikScrollBar::TOrientation aOrientation, TEikScrollBarModel* aModel, TInt aNewPixelThumbSpan, const TEikScrollBarFrameLayout& aLayout) const
	//
	// Adjusts the model in accordance to the new pixel span and the info in aLayout
	// returns true if change results in need to add/remove scrollbar in auto visible cases
	//
	{
	TBool scrollbarWasUseful=aModel->ScrollBarUseful();
	TInt granularity=1;
	switch (aOrientation)
		{
	case CEikScrollBar::EHorizontal:
		granularity=aLayout.iClientAreaGranularity.iWidth;
		aNewPixelThumbSpan-=(aLayout.iClientMargin.iTop+aLayout.iClientMargin.iBottom);
		break;
	case CEikScrollBar::EVertical:
		granularity=aLayout.iClientAreaGranularity.iHeight;
		aNewPixelThumbSpan-=(aLayout.iClientMargin.iLeft+aLayout.iClientMargin.iRight);
		break;
		}
	aModel->iThumbSpan=aNewPixelThumbSpan/granularity;
	return (scrollbarWasUseful!=aModel->ScrollBarUseful());
	}

void CEikScrollBarFrame::AdjustRectByMargins(TMarginsAdjustmentMode aMode, TRect& aRect, const TMargins& aMargins) const
	//
	// Grow/Shrink the rectangle my the given margins
	// 
	{
	aRect.iTl.iX-=(aMode==EGrow)? aMargins.iLeft : -aMargins.iLeft;
	aRect.iTl.iY-=(aMode==EGrow)? aMargins.iTop : -aMargins.iTop;
	aRect.iBr.iX+=(aMode==EGrow)? aMargins.iRight : -aMargins.iRight;
	aRect.iBr.iY+=(aMode==EGrow)? aMargins.iBottom : -aMargins.iBottom;
	}

EXPORT_C TBool CEikScrollBarFrame::AdjustsHorizontalModel() const
	{
	return !(iScrollBarFrameFlags&EDoNotAdjustHorizontalModel);
	}

EXPORT_C TBool CEikScrollBarFrame::AdjustsVerticalModel() const
	{
	return !(iScrollBarFrameFlags&EDoNotAdjustVerticalModel);
	}
