// Scroller.cpp : Implementation of cScroller
#include "stdafx.h"
#include "DecalControls.h"
#include "Scroller.h"

enum eScrollChildren
{
	eScrollerUp = 1,
	eScrollerDown = 2,
   eScrollerRight = 3,
   eScrollerLeft = 4,
	eScrollerPager = 5,
   eScrollerPageUp = 6,
   eScrollerPageDown = 7,
   eScrollerPageLeft = 8,
   eScrollerPageRight = 9,
   eScrollerClient = 10,
   eScrollerHThumb = 11,
   eScrollerVThumb = 12
};

/////////////////////////////////////////////////////////////////////////////
// cScroller

void cScroller::onCreate()
{
   // Initialize our layout options
   m_szArea.cx = 0;
   m_szArea.cy = 0;

   m_szIncrement.cx = 100;
   m_szIncrement.cy = 20;

   m_ptThumb.x = 0;
   m_ptThumb.y = 0;

   m_bHScroll = VARIANT_FALSE;
   m_bVScroll = VARIANT_FALSE;

	CComPtr< ILayer > pLayerPager;
	HRESULT hRes = ::CoCreateInstance( __uuidof( Pager ), NULL, CLSCTX_INPROC_SERVER, __uuidof( ILayer ),
		reinterpret_cast< void ** >( &pLayerPager ) );
	_ASSERTE( SUCCEEDED( hRes ) );

	LayerParams p = { eScrollerPager, { 0, 0, 0, 0 }, eRenderClipped };

	m_pSite->CreateChild( &p, pLayerPager );
	pLayerPager->QueryInterface( &m_pPager );
   IPagerEventsImpl< PAGER_CLIENT, cScroller >::advise( m_pPager );

	CComPtr< ILayer > pLayerUp;
	hRes = ::CoCreateInstance( __uuidof( Button ), NULL, CLSCTX_INPROC_SERVER, __uuidof( ILayer ),
		reinterpret_cast< void ** >( &pLayerUp ) );
	_ASSERTE( SUCCEEDED( hRes ) );

	p.ID = eScrollerUp;

   CComPtr< IButton > pUp;
	m_pSite->CreateChild( &p, pLayerUp );
	pLayerUp->QueryInterface( &pUp);
   ICommandEventsImpl< BUTTON_UP, cScroller >::advise( pUp );
	pUp->SetImages( 0, 0x06001261, 0x06001262 );
   pUp->put_Matte( RGB( 0, 0, 0 ) );

	CComPtr< ILayer > pLayerDown;
	hRes = ::CoCreateInstance( __uuidof( Button ), NULL, CLSCTX_INPROC_SERVER, __uuidof( ILayer ),
		reinterpret_cast< void ** >( &pLayerDown ) );
	_ASSERTE( SUCCEEDED( hRes ) );

	p.ID = eScrollerDown;

   CComPtr< IButton > pDown;
	m_pSite->CreateChild( &p, pLayerDown );
	pLayerDown->QueryInterface( &pDown );
   ICommandEventsImpl< BUTTON_DOWN, cScroller >::advise( pDown );
	pDown->SetImages( 0, 0x0600125E, 0x0600125F );
   pDown->put_Matte( RGB( 0, 0, 0 ) );

	CComPtr< ILayer > pLayerLeft;
	hRes = ::CoCreateInstance( __uuidof( Button ), NULL, CLSCTX_INPROC_SERVER, __uuidof( ILayer ),
		reinterpret_cast< void ** >( &pLayerLeft ) );
	_ASSERTE( SUCCEEDED( hRes ) );

	p.ID = eScrollerLeft;

   CComPtr< IButton > pLeft;
	m_pSite->CreateChild( &p, pLayerLeft );
	pLayerLeft->QueryInterface( &pLeft );
   ICommandEventsImpl< BUTTON_LEFT, cScroller >::advise( pLeft );
	pLeft->SetImages( 0, 0x06001295, 0x06001296 );
   pLeft->put_Matte( RGB( 0, 0, 0 ) );

	CComPtr< ILayer > pLayerRight;
	hRes = ::CoCreateInstance( __uuidof( Button ), NULL, CLSCTX_INPROC_SERVER, __uuidof( ILayer ),
		reinterpret_cast< void ** >( &pLayerRight ) );
	_ASSERTE( SUCCEEDED( hRes ) );

	p.ID = eScrollerRight;

   CComPtr< IButton > pRight;
	m_pSite->CreateChild( &p, pLayerRight );
	pLayerRight->QueryInterface( &pRight );
   ICommandEventsImpl< BUTTON_RIGHT, cScroller >::advise( pRight );
	pRight->SetImages( 0, 0x06001298, 0x06001299 );
   pRight->put_Matte( RGB( 0, 0, 0 ) );

	// Positioning will occur during Reformat

   // Load the images
   CComPtr< IPluginSite > pPlugin;
   m_pSite->get_PluginSite( &pPlugin );

   pPlugin->LoadBitmapPortal( 0x06001260, &m_pVScrollBack );
   pPlugin->LoadBitmapPortal( 0x06001297, &m_pHScrollBack );
}

void cScroller::onDestroy()
{
   m_pPager.Release();
}

void cScroller::constrainOffset( LPPOINT ppt )
{
   // First check for overflow, furthest scroll should be allowed is less a 
   // page size
   POINT ptMax = { m_szArea.cx, m_szArea.cy };

   SIZE szViewport;
   get_Viewport( &szViewport );

   if((szViewport.cx % m_szIncrement.cx)==0)
  	   ptMax.x -= szViewport.cx;
   else
	   ptMax.x -= ( szViewport.cx / m_szIncrement.cx + 1) * m_szIncrement.cx;

   if((szViewport.cy % m_szIncrement.cy)==0)
	   ptMax.y -= szViewport.cy;
   else
	   ptMax.y -= ( szViewport.cy / m_szIncrement.cy + 1) * m_szIncrement.cy;

   // substract the viewport (rounded up a notch) from the max scroll area
  // ptMax.x -= ( szViewport.cx / m_szIncrement.cx + 1) * m_szIncrement.cx;
  // ptMax.y -= ( szViewport.cy / m_szIncrement.cy + 1) * m_szIncrement.cy;

   if( ppt->x > ptMax.x )
      ppt->x = ptMax.x;
   if( ppt->y > ptMax.y )
      ppt->y = ptMax.y;

   // Check for underflow (either because it was assigned too low
   // or or max is too low)
   if( ppt->x < 0 )
      ppt->x = 0;
   if( ppt->y < 0 )
      ppt->y = 0;

}

#define BTN_SIZE  16

long cScroller::hitTest( MouseState *pMS )
{
   if( pMS->over != this )
      return -1;

   SIZE szViewport;

   get_Viewport( &szViewport );

   RECT rcScroll = { BTN_SIZE, BTN_SIZE, szViewport.cx - BTN_SIZE, szViewport.cy - BTN_SIZE };

   // Check if it's on the horizontal bar
   if( m_bHScroll )
   {
      if( pMS->client.x > rcScroll.left && pMS->client.x < rcScroll.right && pMS->client.y > szViewport.cy )
      {
         if( m_szArea.cx == szViewport.cx )
            // Special case, no scrolling if the area is the minimum size
            return -1;

         // We hit the horizontal bar
         if( pMS->client.x < ( m_ptThumb.x + BTN_SIZE ) )
            // Hit before the thumb
            return eScrollerPageLeft;
         else if( pMS->client.x > ( m_ptThumb.x + BTN_SIZE * 2 ) )
            // Hit after the thumb
            return eScrollerPageRight;

         // Hit the thumb
         return eScrollerHThumb;
      }
   }

   // Check if it's on the vertical bar
   if( m_bVScroll )
   {
      if( pMS->client.y > rcScroll.top && pMS->client.y < rcScroll.bottom && pMS->client.x > szViewport.cx )
      {
         if( m_szArea.cy == szViewport.cy )
            // Special case, no scrolling if the area is the minimum size
            return -1;

         // We hit the vertical bar
         if( pMS->client.y < ( m_ptThumb.y + BTN_SIZE ) )
            // Hit above the thumb
            return eScrollerPageUp;
         else if( pMS->client.y > ( m_ptThumb.y + BTN_SIZE * 2 ) )
            // Hit below thumb
            return eScrollerPageDown;

         // Hit the v thumb
         return eScrollerVThumb;
      }
   }

   return -1;
}

STDMETHODIMP cScroller::Render(ICanvas *pCanvas)
{
   SIZE szViewport;
   get_Viewport( &szViewport );

   // Fill in the scrollbars as neccessary
   static POINT ptOff = { 0, 0 };

   CComPtr< IPluginSite > pPluginSite;
   m_pSite->get_PluginSite( &pPluginSite );

   SIZE szIcon = { BTN_SIZE, BTN_SIZE };
   CComPtr< IIconCache > pIconCache;
   pPluginSite->GetIconCache( &szIcon, &pIconCache );

   if( m_bHScroll )
   {
      RECT rcHScroll = { BTN_SIZE, szViewport.cy, szViewport.cx - BTN_SIZE, szViewport.cy + BTN_SIZE };
      m_pHScrollBack->PatBlt( pCanvas, &rcHScroll, &ptOff );

      if( m_szArea.cx != szViewport.cx )
      {
         // Draw the H thumb
         POINT ptHThumb = { BTN_SIZE + m_ptThumb.x, szViewport.cy };
         RECT rcThumb = { ptHThumb.x, ptHThumb.y, ptHThumb.x + BTN_SIZE, ptHThumb.y + BTN_SIZE };
         pCanvas->Fill( &rcThumb, RGB( 0, 0, 0 ) );
         pIconCache->DrawIcon( &ptHThumb, ( m_nCurrentCommand == eScrollerHThumb ) ? 0x06001264 : 0x06001263, 0, pCanvas );
      }
   }

   if( m_bVScroll )
   {
      RECT rcVScroll = { szViewport.cx, BTN_SIZE, szViewport.cx + BTN_SIZE, szViewport.cy - BTN_SIZE };
      m_pVScrollBack->PatBlt( pCanvas, &rcVScroll, &ptOff );

      if( m_szArea.cy != szViewport.cy )
      {
         // Draw the V thumb
         POINT ptVThumb = { szViewport.cx, BTN_SIZE + m_ptThumb.y };
         RECT rcThumb = { ptVThumb.x, ptVThumb.y, ptVThumb.x + BTN_SIZE, ptVThumb.y + BTN_SIZE };
         pCanvas->Fill( &rcThumb, RGB( 0, 0, 0 ) );
         pIconCache->DrawIcon( &ptVThumb, ( m_nCurrentCommand == eScrollerVThumb ) ? 0x06001264 : 0x06001263, 0, pCanvas );
      }
   }

   if( m_bHScroll && m_bVScroll )
   {
      // Draw the little corner piece
      RECT rcCorner = { szViewport.cx, szViewport.cy, szViewport.cx + BTN_SIZE, szViewport.cy + BTN_SIZE };
      pCanvas->Fill( &rcCorner, RGB( 130, 100, 80 ) );
   }

   return S_OK;
}

STDMETHODIMP cScroller::Reformat()
{
   SIZE szViewport;
   get_Viewport( &szViewport );

   CComPtr< ILayerSite > pPagerSite;
   HRESULT hRes = m_pSite->get_Child( eScrollerPager, ePositionByID, &pPagerSite );

   RECT rcPager = { 0, 0, szViewport.cx, szViewport.cy };
   pPagerSite->put_Position( &rcPager );

   CComPtr< ILayerSite > pClientSite;
   pPagerSite->get_Child( eScrollerClient, ePositionByID, &pClientSite );

   RECT rcClient = { 0, 0, m_szArea.cx, m_szArea.cy };
   pClientSite->put_Position( &rcClient );

   CComPtr< ILayerSite > pUp, pDown, pLeft, pRight;

   m_pSite->get_Child( eScrollerUp, ePositionByID, &pUp );
   m_pSite->get_Child( eScrollerDown, ePositionByID, &pDown );
   m_pSite->get_Child( eScrollerLeft, ePositionByID, &pLeft );
   m_pSite->get_Child( eScrollerRight, ePositionByID, &pRight );

   static RECT rcHidden = { 0, 0, 0, 0 };

   if( m_bHScroll )
   {
      RECT rcLeft = { 0, szViewport.cy, BTN_SIZE, szViewport.cy + BTN_SIZE },
         rcRight = { szViewport.cx - BTN_SIZE, szViewport.cy, szViewport.cx, szViewport.cy + BTN_SIZE };

      pLeft->put_Position( &rcLeft );
      pRight->put_Position( &rcRight );
   }
   else
   {
      pLeft->put_Position( &rcHidden );
      pRight->put_Position( &rcHidden );
   }

   if( m_bVScroll )
   {
      RECT rcUp = { szViewport.cx, 0, szViewport.cx + BTN_SIZE, BTN_SIZE },
         rcDown = { szViewport.cx, szViewport.cy - BTN_SIZE, szViewport.cx + BTN_SIZE, szViewport.cy };

      pUp->put_Position( &rcUp );
      pDown->put_Position( &rcDown );
   }
   else
   {
      pUp->put_Position( &rcHidden );
      pDown->put_Position( &rcHidden );
   }

   // Calculate the size of the page increments
   m_szPage.cx = ( szViewport.cx / m_szIncrement.cx ) * m_szIncrement.cx;
   m_szPage.cy = ( szViewport.cy / m_szIncrement.cy ) * m_szIncrement.cy;

   return S_OK;
}

void cScroller::onButtonHit(long nID)
{
   m_pPager->put_Command( nID );
}

void cScroller::onButtonUnhit(long)
{
   m_pPager->FinishCommand();
}

VARIANT_BOOL cScroller::onClientGetNextPosition(long nID, long nCommand, long *pnX, long *pnY)
{
   POINT ptDest;
   m_pPager->get_Offset( &ptDest );

   POINT ptOriginal = ptDest;

   switch( nCommand )
   {
   case eScrollerUp:
      ptDest.y -= m_szIncrement.cy;
      break;

   case eScrollerDown:
      ptDest.y += m_szIncrement.cy;
      break;

   case eScrollerPageUp:
      ptDest.y -= m_szPage.cy;
      break;

   case eScrollerPageDown:
      ptDest.y += m_szPage.cy;
      break;

   case eScrollerLeft:
      ptDest.x -= m_szIncrement.cx;
      break;

   case eScrollerRight:
      ptDest.x += m_szIncrement.cx;
      break;

   case eScrollerPageLeft:
      ptDest.x -= m_szPage.cx;
      break;

   case eScrollerPageRight:
      ptDest.x += m_szPage.cx;
      break;

   default:
      // Unknown scrolling command
      _ASSERTE( FALSE );
   }

   constrainOffset( &ptDest );

   if( ptDest.x == ptOriginal.x && ptDest.y == ptOriginal.y )
      // We didn't move this cycle - do not continue
      return VARIANT_FALSE;

   *pnX = ptDest.x;
   *pnY = ptDest.y;

   return VARIANT_TRUE;
}

void cScroller::onClientChange( long nID, long, long nX, long nY )
{
   _ASSERTE( nID == eScrollerPager );

   // Convert these values to a thumb-like position
   SIZE szViewport;
   get_Viewport( &szViewport );

   SIZE szScroll = szViewport;
   szScroll.cx -= BTN_SIZE * 3;
   szScroll.cy -= BTN_SIZE * 3;

   // The viewport is now the size of the thumb ranges
   m_ptThumb.x = static_cast< long >( ( ( m_szArea.cx - szViewport.cx ) != 0 ) ? static_cast< double >( nX * szScroll.cx ) / static_cast< double >( m_szArea.cx - szViewport.cx ) : 0.0 );
   m_ptThumb.y = static_cast< long >( ( ( m_szArea.cy - szViewport.cy ) != 0 ) ? static_cast< double >( nY * szScroll.cy ) / static_cast< double >( m_szArea.cy - szViewport.cy ) : 0.0 );

   if( m_ptThumb.x > szScroll.cx || nX >= ( m_szArea.cx - szViewport.cx - m_szIncrement.cy  ) )
      m_ptThumb.x = szScroll.cx;
   if( m_ptThumb.y > szScroll.cy || nY >= ( m_szArea.cy - szViewport.cy - m_szIncrement.cy ) )
      m_ptThumb.y = szScroll.cy;

   if( m_ptThumb.x < 0 )
      m_ptThumb.x = 0;
   if( m_ptThumb.y < 0 )
      m_ptThumb.y = 0;

   // Check if the thumb moving has canceled a mouse action
   switch( m_nCurrentCommand )
   {
   case eScrollerPageUp:
   case eScrollerPageDown:
   case eScrollerPageLeft:
   case eScrollerPageRight:
      {
         MouseState ms = { this, { 0, 0 }, { m_ptMouseLast.x, m_ptMouseLast.y }, VARIANT_FALSE, VARIANT_FALSE };
         if( hitTest( &ms ) != m_nCurrentCommand )
            m_pPager->FinishCommand();
      }
      break;
   }

   // Draw next frame
   m_pSite->Invalidate();

   long nIDThis;
   m_pSite->get_ID( &nIDThis );
   Fire_Change( nIDThis, nX, nY );
}

STDMETHODIMP cScroller::get_Viewport(LPSIZE pVal)
{
   _ASSERTE( pVal != NULL );

   RECT rc;
   m_pSite->get_Position( &rc );

   pVal->cx = ( rc.right - rc.left ) - ( ( m_bVScroll ) ? BTN_SIZE : 0 );
   pVal->cy = ( rc.bottom - rc.top ) - ( ( m_bHScroll ) ? BTN_SIZE : 0 );

	return S_OK;
}

STDMETHODIMP cScroller::get_Area(LPSIZE pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_szArea;

	return S_OK;
}

STDMETHODIMP cScroller::put_Area(LPSIZE newVal)
{
   _ASSERTE( newVal != NULL );
   //_ASSERTE( newVal->cx >= 0 && newVal->cy >= 0 );

   // Now this area will have to be rounded up to an increment or
   // a page size
   SIZE szViewport;
   get_Viewport( &szViewport );

   if( newVal->cx < szViewport.cx )
      // This view is thinner than the viewport, set it to the minimum
      newVal->cx = szViewport.cx;
   else if( m_bHScroll )
	  if((szViewport.cx % m_szIncrement.cx)!=0) 
		newVal->cx += m_szIncrement.cx - ( newVal->cx % m_szIncrement.cx );   // Make sure there is an extra line so we don't scroll off the edge  

   // Repeat for y-scoord
   if( newVal->cy < szViewport.cy )
      newVal->cy = szViewport.cy;
   else if( m_bVScroll )
	   if((szViewport.cy % m_szIncrement.cy)!=0)
      newVal->cy += m_szIncrement.cy - ( newVal->cy % m_szIncrement.cy );

   m_szArea = *newVal;
   m_pSite->Reformat();

	return S_OK;
}

STDMETHODIMP cScroller::get_HorizontalEnabled(VARIANT_BOOL *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_bHScroll;

	return S_OK;
}

STDMETHODIMP cScroller::put_HorizontalEnabled(VARIANT_BOOL newVal)
{
   if( newVal != m_bHScroll )
   {
      m_bHScroll = newVal;
      m_pSite->Reformat();
   }

	return S_OK;
}

STDMETHODIMP cScroller::get_VerticalEnabled(VARIANT_BOOL *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_bVScroll;

	return S_OK;
}

STDMETHODIMP cScroller::put_VerticalEnabled(VARIANT_BOOL newVal)
{
   if( newVal != m_bVScroll )
   {
      m_bVScroll = newVal;
      m_pSite->Reformat();
   }

	return S_OK;
}

STDMETHODIMP cScroller::CreateClient(ILayer *pChild)
{
   CComPtr< ILayerSite > pPagerSite;
   HRESULT hRes = m_pSite->get_Child( eScrollerPager, ePositionByID, &pPagerSite );

   _ASSERTE( SUCCEEDED( hRes ) );

   LayerParams lp = { eScrollerClient, { 0, 0, 0, 0 }, eRenderClipped };

	return pPagerSite->CreateChild( &lp, pChild );
}

STDMETHODIMP cScroller::get_Offset(LPPOINT pVal)
{
   _ASSERTE( pVal != NULL );

   POINT ptTrueOff;
   m_pPager->get_Offset( &ptTrueOff );

   pVal->x = ptTrueOff.x / m_szIncrement.cx;
   pVal->y = ptTrueOff.y / m_szIncrement.cy;

	return S_OK;
}

STDMETHODIMP cScroller::put_Offset(LPPOINT newVal)
{
   _ASSERTE( newVal != NULL );
   _ASSERTE( newVal->x >= 0 && newVal->y >= 0 );

   POINT ptTrueOff = { newVal->x * m_szIncrement.cx, newVal->y * m_szIncrement.cy };

   constrainOffset( &ptTrueOff );
   m_pPager->put_Offset( &ptTrueOff );

	return S_OK;
}

STDMETHODIMP cScroller::ScrollTo(LPPOINT ptOffset)
{
   _ASSERTE( ptOffset != NULL );
   _ASSERTE( ptOffset->x >= 0 && ptOffset->y >= 0 );

   POINT ptTrueOff = { ptOffset->x * m_szIncrement.cx, ptOffset->y * m_szIncrement.cy };

   constrainOffset( &ptTrueOff );
   m_pPager->ScrollTo( &ptTrueOff );

	return S_OK;
}

STDMETHODIMP cScroller::get_Increments(LPSIZE pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_szIncrement;

	return S_OK;
}

STDMETHODIMP cScroller::put_Increments(LPSIZE newVal)
{
   _ASSERTE( newVal != NULL );
   _ASSERTE( newVal->cx > 0 && newVal->cy > 0 );

   // Assign the new increments
   m_szIncrement = *newVal;

   // Align the current position to the new position
   POINT ptCurrent;
   m_pPager->get_Offset( &ptCurrent );

   ptCurrent.x = ( ptCurrent.x / newVal->cx ) * newVal->cx;
   ptCurrent.y = ( ptCurrent.y / newVal->cy ) * newVal->cy;

   constrainOffset( &ptCurrent );
   m_pPager->put_Offset( &ptCurrent );
   m_pSite->Reformat();

	return S_OK;
}

STDMETHODIMP cScroller::MouseDown( MouseState *pMS )
{
   m_nCurrentCommand = hitTest( pMS );

   if( m_nCurrentCommand == -1 )
      return S_OK;

   switch( m_nCurrentCommand )
   {
   case eScrollerHThumb:
      m_nScreenStart = pMS->screen.x;
      m_nThumbStart = m_ptThumb.x;
      break;

   case eScrollerVThumb:
      m_nScreenStart = pMS->screen.y;
      m_nThumbStart = m_ptThumb.y;
      break;

   case eScrollerPageUp:
   case eScrollerPageDown:
   case eScrollerPageLeft:
   case eScrollerPageRight:
      m_ptMouseLast = pMS->client;
      m_pPager->put_Command( m_nCurrentCommand );
      break;
   }

   m_pSite->Invalidate();
   return S_OK;
}

STDMETHODIMP cScroller::MouseUp( MouseState *pMS )
{
   m_nCurrentCommand = -1;

   // Make sure there are no more commands in the pager
   m_pPager->FinishCommand();

   m_pSite->Invalidate();

   return S_OK;
}

STDMETHODIMP cScroller::MouseMove( MouseState *pMS )
{
   if( m_nCurrentCommand == -1 )
      // No command, so nothgin to do
      return S_OK;

   SIZE szViewport;
   get_Viewport( &szViewport );
   SIZE szScroller = { szViewport.cx - BTN_SIZE * 3, szViewport.cy - BTN_SIZE * 3 };

   POINT ptOffset;
   m_pPager->get_Offset( &ptOffset );

   switch( m_nCurrentCommand )
   {
   case eScrollerPageUp:
   case eScrollerPageDown:
   case eScrollerPageLeft:
   case eScrollerPageRight:
      {
         if( hitTest( pMS ) == m_nCurrentCommand )
            m_pPager->put_Command( m_nCurrentCommand );
         else
            m_pPager->FinishCommand();
         m_ptMouseLast = pMS->client;
      }
      break;

   case eScrollerHThumb:
      {
         long nPos = m_nThumbStart + ( pMS->screen.x - m_nScreenStart );

         // Make sure it's bigger than 0
         if( nPos < 0 )
            nPos = 0;

         // Convert this to an actual position
         ptOffset.x = nPos * ( m_szArea.cx - szViewport.cx ) / szScroller.cx;

         // Align to the nearest line
         ptOffset.x -= ptOffset.x % m_szIncrement.cx;

         // Set the position, pager callback will set the thumb position
         constrainOffset( &ptOffset );
         m_pPager->put_Offset( &ptOffset );
      }
      break;

   case eScrollerVThumb:
      {
         long nPos = m_nThumbStart + ( pMS->screen.y - m_nScreenStart );

         // Make sure it's bigger than 0
         if( nPos < 0 )
            nPos = 0;

         // Convert this to an actual position
         ptOffset.y = nPos * ( m_szArea.cy - szViewport.cy ) / szScroller.cy;

         // Align to the nearest line
         ptOffset.y -= ptOffset.y % m_szIncrement.cy;

         // Set the position, pager callback will set the thumb position
         constrainOffset( &ptOffset );
         m_pPager->put_Offset( &ptOffset );
      }
      break;
   }

   return S_OK;
}

HRESULT cScroller::SchemaLoad( IView *pView, IUnknown *pXMLElement )
{
   MSXML::IXMLDOMElementPtr pElement = pXMLElement;

   _variant_t vHScroll = pElement->getAttribute( _T( "hscroll" ) ),
      vVScroll = pElement->getAttribute( _T( "vscroll" ) ),
      vAreaWidth = pElement->getAttribute( _T( "areawidth" ) ),
      vAreaHeight = pElement->getAttribute( _T( "areaheight" ) ),
      vIncrementX = pElement->getAttribute( _T( "incrementx" ) ),
      vIncrementY = pElement->getAttribute( _T( "incrementy" ) );

   // Do the client
   MSXML::IXMLDOMElementPtr pClient = pElement->selectSingleNode( _T( "control" ) );
   if( pClient.GetInterfacePtr() != NULL )
   {
      // Recurse and load the client
      CComPtr< ILayerSite > pPager;
      m_pSite->get_Child( eScrollerPager, ePositionByID, &pPager );
      long nAssigned;
      pView->LoadControl( pPager, eScrollerClient, pClient, &nAssigned );
   }

   // First enable the scroll bars
   if( vHScroll.vt != VT_NULL )
   {
      try
      {
         m_bHScroll = static_cast< bool >( vHScroll ) ? VARIANT_TRUE : VARIANT_FALSE;
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vVScroll.vt != VT_NULL )
   {
      try
      {
         m_bVScroll = static_cast< bool >( vVScroll ) ? VARIANT_TRUE : VARIANT_FALSE;
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   // Next set the increment sizes
   if( vIncrementX.vt != VT_NULL )
   {
      try
      {
         m_szIncrement.cx = static_cast< long >( vIncrementX );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vIncrementY.vt != VT_NULL )
   {
      try
      {
         m_szIncrement.cy = static_cast< long >( vIncrementY );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   // Last, set the area
   SIZE szArea = m_szArea;
   if( vAreaWidth.vt != VT_NULL )
   {
      try
      {
         szArea.cx = static_cast< long >( vAreaWidth );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vAreaHeight.vt != VT_NULL )
   {
      try
      {
         szArea.cy = static_cast< long >( vAreaHeight );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   put_Area( &szArea );

   return S_OK;
}

STDMETHODIMP cScroller::ResetScroller()
{

	POINT ptCurrent;
	ptCurrent.x = 0;
	ptCurrent.y = 0;
	constrainOffset( &ptCurrent );
	m_pPager->put_Offset( &ptCurrent );  
	m_ptThumb.x =0;
	m_ptThumb.y =0;	

	return S_OK;
}
