// BarSite.cpp : Implementation of cBarSite
#include "stdafx.h"
#include "Inject.h"
#include "LayerSite.h"

#include "Manager.h"

#include "Button.h"
#include "Pager.h"

/////////////////////////////////////////////////////////////////////////////
// cBarSite

enum eSinkCaps
{
   eSinkCapRender = 0x01,
   eSinkCapMouse = 0x02,
   eSinkCapKeyboard = 0x04,
   eSinkCapPopup = 0x08,
   eSinkCapTimer = 0x10
};

cLayerSite::cLayerSite()
: m_pParent( NULL ),
m_dwSinkCaps( 0 ),
m_Alpha( 255 )
{
}

cLayerSite::~cLayerSite()
{
   // Make sure we're really destroyed
   _ASSERTE( m_pParent == NULL );
   _ASSERTE( m_children.size() == 0 );
}

void cLayerSite::removeChild( cLayerSite *pChild )
{
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
   {
      if( *i == pChild )
      {
         m_children.erase( i );
         break;
      }
   }
}

struct cSinkCapTest
{
   eSinkCaps m_mask;
   const IID *m_iid;
};

void cLayerSite::create( LayerParams *pParams, cLayerSite *pParent, ILayer *pSink )
{
   // First copy all the relevant data
   m_params = *pParams;
   m_pParent = pParent;

   static cSinkCapTest _capstest[] = {
      { eSinkCapRender, &IID_ILayerRender },
      { eSinkCapMouse, &IID_ILayerMouse },
      { eSinkCapKeyboard, &IID_ILayerKeyboard },
      { eSinkCapPopup, &IID_ILayerPopup },
      { eSinkCapTimer, &IID_ILayerTimer },
   };

   static cSinkCapTest *_end_caps = _capstest + ( sizeof( _capstest ) / sizeof( cSinkCapTest ) );

   m_pSink = pSink;

   // Probe for relevant interfaces
   for( cSinkCapTest *i = _capstest; i != _end_caps; ++ i )
   {
      CComPtr< IUnknown > pUnk;
      if( SUCCEEDED( pSink->QueryInterface( *( i->m_iid ), reinterpret_cast< void ** >( &pUnk ) ) ) )
         m_dwSinkCaps |= i->m_mask;
   }

   // Do initialization activities
   if( !( m_params.render & eRenderClipped ) )
   {
      // Create the back buffer
      SIZE sz = { m_params.pos.right - m_params.pos.left, m_params.pos.bottom - m_params.pos.top };
      if( sz.cx > 0 && sz.cy > 0 )
         cManager::_p->CreateCanvas( &sz, &m_pBuffer );
   }

   // Add in the default render flags
   // All layer begin their life transparent until set otherwise
   m_params.render |= eRenderTransparent;

   // Make this control format itself next round
   Reformat();
   Invalidate();

   m_pSink->LayerCreate( this );
}

void cLayerSite::destroy()
{
   // First destroy all of our children
   while( !m_children.empty() )
   {
#ifdef _DEBUG
      int nChildren = m_children.size();
      m_children.front()->destroy();
      _ASSERTE( m_children.size() == ( nChildren - 1 ) );
#else
      m_children.front()->destroy();
#endif
   }

   // Remove from parent
   if( m_pParent != NULL )
   {
      m_pParent->removeChild( this );
      m_pParent = NULL;
   }

   // The child now releases the reference - this should trick our reference count to 0
   m_pSink->LayerDestroy();

   // After LayerDestroy returns this object has already been deleted -
   // do not touch any data members and we're OK

   // Reset capture pointers
   if( cManager::_p->m_pMouseDown == this )
      cManager::_p->m_pMouseDown = NULL;

   if( cManager::_p->m_pMouseOver == this )
      cManager::_p->m_pMouseOver = NULL;

   if( cManager::_p->m_pKeyboard == this )
      cManager::_p->m_pKeyboard = NULL;

   for( cManager::cLayerList::iterator i = cManager::_p->m_popups.begin(); i != cManager::_p->m_popups.end(); ++ i )
   {
      if( *i == this )
      {
         cManager::_p->m_popups.erase( i );
         break;
      }
   }
}

void cLayerSite::format()
{
   // ::MessageBox( NULL, _T( "cLayerSite::format" ), _T( "Inject.dll" ), MB_OK );

   // This is no longer recursive - in all cases, parents position their children
   formatLayer();
}

void cLayerSite::formatLayer()
{
   // Check for timers
   if( m_timers.size() > 0 )
   {
      long nCurrentTime = ::timeGetTime();
      for( cTimerList::iterator i = m_timers.begin(); i != m_timers.end(); )
      {
         if( ( i->m_nLastTimeout + i->m_nInterval ) <= nCurrentTime )
         {
            // Default to false, prevent the assert from going off *lots*
            VARIANT_BOOL bContinue = VARIANT_FALSE;

            ++ i->m_nRepetitions;

            if( m_dwSinkCaps & eSinkCapTimer )
            {
               CComPtr< ILayerTimer > pTimer;
               m_pSink->QueryInterface( &pTimer );

               pTimer->TimerTimeout( i->m_nID, nCurrentTime - i->m_nLastTimeout, i->m_nRepetitions, &bContinue );
            }

            if( bContinue )
               i->m_nLastTimeout = nCurrentTime;
            else
            {
               // Do not continue timing
               i = m_timers.erase( i );
               continue;
            }
         }

         ++ i;
      }
   }

   // When reformatting - never reformat a surface that's 0,0 size
   if( ( m_params.render & eRenderReformatNext ) &&
      m_params.pos.right > m_params.pos.left &&
      m_params.pos.bottom > m_params.pos.top )
   {
      // Do a reformat - NOTE: it's done here becasue a reformat will almost
      // always destroy the surface

      if( m_dwSinkCaps & eSinkCapRender )
      {
         CComPtr< ILayerRender > pLayer;
         m_pSink->QueryInterface( &pLayer );

         pLayer->Reformat();
      }

      // Clear the reformat flag
      m_params.render &= ~eRenderReformatNext;
   }

   // Iterate over the children
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
      ( *i )->formatLayer();
}

void cLayerSite::prerender()
{
   if( m_dwSinkCaps & eSinkCapRender )
   {
      CComPtr< ILayerRender > pRender;
      m_pSink->QueryInterface( &pRender );

      pRender->PreRender();
   }

   // Iterate over the children
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
      ( *i )->prerender();
}

void cLayerSite::render(ICanvas *pPrimary)
{
   if( !( m_params.render & eRenderClipped ) )
   {
      // Check for 0,0 size
      if( m_params.pos.right > m_params.pos.left && m_params.pos.bottom > m_params.pos.top )
      {
         // ::MessageBox( NULL, _T( "cLayerSite::render" ), _T( "Inject.dll" ), MB_OK );
         _ASSERTE( m_pBuffer.p != NULL );

         VARIANT_BOOL bRedraw;
         m_pBuffer->get_WasLost( &bRedraw );

         // Here we push a scratch clip rect for the entire area
         // This is intended for unclipped layers that modfiy their
         // child clipping area.
         RECT rcAll = { 0, 0, m_params.pos.right - m_params.pos.left, m_params.pos.bottom - m_params.pos.top };
         VARIANT_BOOL bDrawChildren;
         m_pBuffer->PushClipRect( &rcAll, &bDrawChildren );

         // Assuming this area is non-0 size
         _ASSERTE( bDrawChildren );

         renderBuffer( m_pBuffer, !!bRedraw );

         m_pBuffer->PopClipRect();

         // Place it on the screen
         POINT pt = { m_params.pos.left, m_params.pos.top };
         RECT rc = { 0, 0, m_params.pos.right - m_params.pos.left, m_params.pos.bottom - m_params.pos.top };

		 pPrimary->put_Alpha(m_Alpha);
         pPrimary->Blt( &rc, m_pBuffer, &pt );
      }
      else
         // This object has no size, it should have no canvas
         _ASSERTE( m_pBuffer.p == NULL );
   }

   // Iterate through children - look for more non-clipped layers to render
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
      ( *i )->render( pPrimary );
}

void cLayerSite::renderBuffer( ICanvas *pBuffer, bool bForce )
{
   // Remove the next flag
   VARIANT_BOOL bRenderChildren = VARIANT_TRUE;
   bool bRendered = bForce;

   if( m_dwSinkCaps & eSinkCapRender )
   {
      CComPtr< ILayerRender > pLayer;
      m_pSink->QueryInterface( &pLayer );

      if( bForce || ( m_params.render & eRenderNext ) )
      {
         if( !( m_params.render & eRenderClipped ) )
         {
            RECT rcClient = { 0, 0, m_params.pos.right - m_params.pos.left, m_params.pos.bottom - m_params.pos.top };
            // Unclipped layers all get an inital transparent fill
            m_pBuffer->Fill( &rcClient, RGB( 0, 255, 255 ) );
         }

         bRendered = true;
         pLayer->Render( pBuffer );
      }

      pLayer->AdjustRenderArea( pBuffer, &bRenderChildren );
   }

   m_params.render &= ~eRenderNext;

   if( !bRenderChildren )
      // While adjusting the render parameters, the rectangle got clipped out of existance
      return;

   // Walk through the children and transform the surface params
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
   {
      if( !( ( *i )->m_params.render & eRenderClipped ) )
         // Only clipped children
         continue;

      // Clip the child rectangle
      VARIANT_BOOL bRenderChild;
      pBuffer->PushClipRect( &( *i )->m_params.pos, &bRenderChild );

      if( bRenderChild )
         // Iterate
         ( *i )->renderBuffer( pBuffer, bRendered );

      pBuffer->PopClipRect();
   }
}

void cLayerSite::reformat()
{
   if( m_dwSinkCaps & eSinkCapRender )
      // Schedule a reformat for next render cycle
      m_params.render |= eRenderReformatNext;

   // Propagate reformat to children
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
      ( *i )->reformat();
}

cLayerSite *cLayerSite::hitTestUnclipped( POINTS pt, ICanvas *pCanvas )
{
   // First test if any children have been hit and delegate to them
   // as children have a higher z-order value
   cLayerSite *pSite;
   for( cChildList::reverse_iterator i = m_children.rbegin(); i != m_children.rend(); ++ i )
   {
      pSite = ( *i )->hitTestUnclipped( pt, pCanvas );
      if( pSite != NULL )
         // Found a child hit - overlapping children are not tested
         // as there is no sibling clipping
         return pSite;
   }

   // Children are not hit, check if we hit ourselves
   if( !( m_params.render & eRenderClipped ) )
   {
      POINT ptl = { pt.x, pt.y };
      VARIANT_BOOL bVisible;
      pCanvas->PushClipRect( &m_params.pos, &bVisible );

      cLayerSite *pHit = NULL;

      if( bVisible )
      {
         VARIANT_BOOL bHit;
         pCanvas->HitTest( &ptl, &bHit );
         if( bHit )
         {
            // Check for transparent area hitting
            if( ( m_dwSinkCaps & eSinkCapRender ) && ( m_params.render & eRenderTransparent ) )
            {
               // This may contain transparent areas
               // TODO: Check for transparent areas
               pHit = this;
            }
            else
               pHit = this;
         }
      }

      pCanvas->PopClipRect();

      return pHit;
   }

   // Nothing hit in this branch
   return NULL;
}

cLayerSite *cLayerSite::hitTestClipped( POINTS pt, LPPOINT pptHit, ICanvas *pCanvas )
{
   // ::MessageBox( NULL, _T( "cLayerSite::hitTestClipped" ), _T( "Inject.dll" ), MB_OK );

   // Convert the render params
   VARIANT_BOOL bRenderChildren = VARIANT_TRUE;
   if( m_dwSinkCaps & eSinkCapRender )
   {
      CComPtr< ILayerRender > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->AdjustRenderArea( pCanvas, &bRenderChildren );
   }

   POINT ptm = { pt.x, pt.y };

   if( bRenderChildren )
   {
      for( cChildList::reverse_iterator i = m_children.rbegin(); i != m_children.rend(); ++ i )
      {
         if( !( ( *i )->m_params.render & eRenderClipped ) )
            // Only clipped children
            continue;

         // Clip the child rectangle
         VARIANT_BOOL bVisible;
         pCanvas->PushClipRect( &( *i )->m_params.pos, &bVisible );

         if( bVisible )
         {
            VARIANT_BOOL bHit;
            pCanvas->HitTest( &ptm, &bHit );

            if( bHit )
            {
               cLayerSite *pRet = ( *i )->hitTestClipped( pt, pptHit, pCanvas );
               pCanvas->PopClipRect();

               return pRet;
            }
         }

         pCanvas->PopClipRect();
      }
   }

   *pptHit = ptm;
   pCanvas->ToClient( pptHit );

   return this;
}

cLayerSite *cLayerSite::hitTest( POINTS pt, LPPOINT pptHit, ICanvas *pCanvas )
{
   // First, find the unclipped parent
   cLayerSite *pHitUnclipped = hitTestUnclipped( pt, pCanvas );

   if( pHitUnclipped == NULL )
      // Absolutely nothing hit
      return NULL;

   VARIANT_BOOL bVisible;
   pCanvas->PushClipRect( &pHitUnclipped->m_params.pos, &bVisible );

   cLayerSite *pHitClipped = pHitUnclipped->hitTestClipped( pt, pptHit, pCanvas );
   pCanvas->PopClipRect();

   return pHitClipped;
}

void cLayerSite::clipCanvas(ICanvas *pCanvas)
{
   if( m_params.render & eRenderClipped )
      m_pParent->clipCanvas( pCanvas );

   VARIANT_BOOL bVisible;
   pCanvas->PushClipRect( &m_params.pos, &bVisible );

   if( m_dwSinkCaps & eSinkCapRender )
   {
      CComPtr< ILayerRender > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->AdjustRenderArea( pCanvas, &bVisible );
   }
}

bool cLayerSite::isClippedChild( cLayerSite *pChild )
{
   for( cLayerSite *pChildLayer = pChild; pChild != NULL; pChild = pChild->m_pParent )
   {
      if( pChildLayer == this )
         return true;

      if( !( pChildLayer->m_params.render & eRenderClipped ) )
         break;
   }

   return false;
}

bool cLayerSite::isChild( cLayerSite *pChild )
{
   for( cLayerSite *pChildLayer = pChild; pChildLayer != NULL; pChildLayer = pChildLayer->m_pParent )
   {
      if( pChildLayer == this )
         return true;
   }

   return false;
}

cLayerSite *cLayerSite::getCommonClippedParent( cLayerSite *pHit )
{
   for( cLayerSite *pCurrentParent = this; pCurrentParent != NULL; pCurrentParent = pCurrentParent->m_pParent )
   {
      for( cLayerSite *pHitParent = pHit; pHitParent != NULL; pHitParent = pHitParent->m_pParent )
      {
         if( pHitParent == pCurrentParent )
            return pHitParent;

         if( !( pCurrentParent->m_params.render & eRenderClipped ) )
            break;
      }

      if( !( pCurrentParent->m_params.render & eRenderClipped ) )
         break;
   }

   return NULL;
}

void cLayerSite::sendMouseEnter( cLayerSite *pCommonParent, MouseState *pMS )
{
   if( pCommonParent != this && ( m_params.render & eRenderClipped ) )
      // Enter our parent first - note that we stop at the unclipped parent
      m_pParent->sendMouseEnter( pCommonParent, pMS );

   if( m_dwSinkCaps & eSinkCapMouse )
   {
      CComPtr< ILayerMouse > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->MouseEnter( pMS );
   }
}

void cLayerSite::sendMouseExit( cLayerSite *pCommonParent, MouseState *pMS )
{
   for( cLayerSite *pExit = this; pExit != pCommonParent; pExit = pExit->m_pParent )
   {
      if( !( pExit->m_params.render & eRenderClipped ) )
         // Common parent may be set to NULL if we're switching
         // to another unclipped parent
         break;

      if( pExit->m_dwSinkCaps & eSinkCapMouse )
      {
         CComPtr< ILayerMouse > pLayer;
         pExit->m_pSink->QueryInterface( &pLayer );

         pLayer->MouseExit( pMS );
      }
   }
}

void cLayerSite::sendMouseDown( MouseState *pMS )
{
   if( m_dwSinkCaps & eSinkCapMouse )
   {
      CComPtr< ILayerMouse > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->MouseDown( pMS );
   }
}

void cLayerSite::sendMouseUp( MouseState *pMS )
{
   if( m_dwSinkCaps & eSinkCapMouse )
   {
      CComPtr< ILayerMouse > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->MouseUp( pMS );
   }
}

void cLayerSite::sendMouseMove( MouseState *pMS )
{
   if( m_dwSinkCaps & eSinkCapMouse )
   {
      CComPtr< ILayerMouse > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->MouseMove( pMS );
   }
}

void cLayerSite::sendMouseDblClk( MouseState *pMS )
{
   if( m_dwSinkCaps & eSinkCapMouse )
   {
      CComPtr< ILayerMouse > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->MouseDblClk( pMS );
   }
}

void cLayerSite::sendMouseEvent( long nMsg, long wParam, long lParam )
{
   if( m_dwSinkCaps & eSinkCapMouse )
   {
      CComPtr< ILayerMouse > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->MouseEvent( nMsg, wParam, lParam );
   }
}

void cLayerSite::sendKeyboardEndCapture( VARIANT_BOOL bCancel )
{
   if( m_dwSinkCaps & eSinkCapKeyboard )
   {
      CComPtr< ILayerKeyboard > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->KeyboardEndCapture( bCancel );
   }
}

void cLayerSite::sendKeyboardChar( KeyState *pKS )
{
   if( m_dwSinkCaps & eSinkCapKeyboard )
   {
      CComPtr< ILayerKeyboard > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->KeyboardChar( pKS );
   }
}

void cLayerSite::sendKeyboardEvent( long nMsg, long wParam, long lParam )
{
   if( m_dwSinkCaps & eSinkCapKeyboard )
   {
      CComPtr< ILayerKeyboard > pLayer;
      m_pSink->QueryInterface( &pLayer );

      pLayer->KeyboardEvent( nMsg, wParam, lParam );
   }
}

bool cLayerSite::sendPopupCancel( MouseState *pMS )
{
   if( m_dwSinkCaps & eSinkCapPopup )
   {
      CComPtr< ILayerPopup > pLayer;
      m_pSink->QueryInterface( &pLayer );

      VARIANT_BOOL bContinue;
      pLayer->PopupCancel( pMS, &bContinue );

      return !!bContinue;
   }

   // Default action is to cancel the popup
   return false;
}

STDMETHODIMP cLayerSite::Destroy()
{
   cManager::_p->m_destroy.push_back( this );
   AddRef();
   return S_OK;
}

STDMETHODIMP cLayerSite::CreateChild(LayerParams *params, ILayer *pSink)
{
   CComObject< cLayerSite > *pChild;
   CComObject< cLayerSite >::CreateInstance( &pChild );

   pChild->create( params, this, pSink );
   m_children.push_back( pChild );

   return S_OK;
}

STDMETHODIMP cLayerSite::GetSink(REFIID iid, void **ppvItf)
{
	return m_pSink->QueryInterface( iid, ppvItf );
}

STDMETHODIMP cLayerSite::Invalidate()
{
   for( cLayerSite *pSite = this; pSite != NULL; pSite = pSite->m_pParent )
   {
      if( !( pSite->m_params.render & eRenderTransparent ) || !( pSite->m_params.render & eRenderClipped ) || pSite->m_pParent == NULL )
         break;
   }

   pSite->m_params.render |= eRenderNext;

	return S_OK;
}

STDMETHODIMP cLayerSite::get_PluginSite(IPluginSite **ppSite)
{
   *ppSite = cManager::_p;
   ( *ppSite )->AddRef();

	return S_OK;
}

STDMETHODIMP cLayerSite::Reformat()
{
   if( m_params.pos.right != m_params.pos.left && m_params.pos.top != m_params.pos.bottom &&
         m_dwSinkCaps & eSinkCapRender )
      m_params.render |= eRenderReformatNext;

	return S_OK;
}

STDMETHODIMP cLayerSite::GetParentSink(REFIID iid, void **ppvItf)
{
   for( cLayerSite *pSite = m_pParent; pSite != NULL; pSite = pSite->m_pParent )
   {
      HRESULT hRes = pSite->m_pSink->QueryInterface( iid, ppvItf );
      if( SUCCEEDED( hRes ) )
         // Found our requested interface, return happy
         return hRes;
   }

	return E_NOINTERFACE;
}

STDMETHODIMP cLayerSite::get_Position(LPRECT pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_params.pos;

	return S_OK;
}

STDMETHODIMP cLayerSite::put_Position(LPRECT newVal)
{
   _ASSERTE( newVal != NULL );
   _ASSERTE( newVal->right >= newVal->left && newVal->bottom >= newVal->top );

   m_params.pos = *newVal;

   if( !( m_params.render & eRenderClipped ) )
   {
      // This is unclipped, check the buffer size
      bool bHasBuffer = ( m_params.pos.right > m_params.pos.left && m_params.pos.bottom > m_params.pos.top );
      if( bHasBuffer )
      {
         SIZE sz = { m_params.pos.right - m_params.pos.left, m_params.pos.bottom - m_params.pos.top };
         if( m_pBuffer.p == NULL )
            cManager::_p->CreateCanvas( &sz, &m_pBuffer );
         else
            m_pBuffer->put_Size( &sz );
      }
      else
      {
         if( m_pBuffer.p != NULL )
            m_pBuffer.Release();
      }
   }

   // Mark for format change only if area > 0
   Reformat();

	return S_OK;
}

STDMETHODIMP cLayerSite::get_ID(long *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_params.ID;

	return S_OK;
}

STDMETHODIMP cLayerSite::get_ChildCount(long *pVal)
{
   _ASSERTE( pVal != NULL );
   *pVal = m_children.size();

	return S_OK;
}

STDMETHODIMP cLayerSite::get_Child(long nIndex, ePositionType posType, ILayerSite **pVal)
{
   _ASSERTE( pVal != NULL );

   if( posType == ePositionByIndex )
   {
      if( nIndex < 0 || nIndex >= m_children.size() )
         // Out of range
         return E_INVALIDARG;

      *pVal = m_children[ nIndex ];
      ( *pVal )->AddRef();

      return S_OK;
   }

   if( posType != ePositionByID )
      // Invalid position type
      return E_INVALIDARG;

   // Scan for child by ID
   for( cChildList::iterator i = m_children.begin(); i != m_children.end(); ++ i )
   {
      if( ( *i )->m_params.ID == nIndex )
      {
         *pVal = *i;
         ( *pVal )->AddRef();

         return S_OK;
      }
   }

   // Invalid Index
	return E_INVALIDARG;
}

void cLayerSite::moveChildToFront(long nIndex) {
	// TODO : Fix this, it doesn't properly move the Z order of the children if they are at the front

	if( m_children.back()->m_params.ID == nIndex )
		return;

	for (cChildList::iterator i = m_children.begin(); i != m_children.end(); ++i) {
		if((*i)->m_params.ID == nIndex) {
			cLayerSite *layerSite = *i;
			m_children.erase(i);
			m_children.push_back(layerSite);

			break;
		}
	}
}

STDMETHODIMP cLayerSite::moveToFront() {
	_ASSERTE(m_pParent);

	m_pParent->moveChildToFront(m_params.ID);

	return S_OK;
}

STDMETHODIMP cLayerSite::CaptureKeyboard()
{
   // Cancel capture in the existing keyboard capture- if any
   // Don't fire EndCapture if we already have the capture. cyn -- 15/10/2002
  if( ( cManager::_p->m_pKeyboard != NULL ) && ( cManager::_p->m_pKeyboard != this ) )
      cManager::_p->m_pKeyboard->sendKeyboardEndCapture( VARIANT_TRUE );

   // Start capturing
   cManager::_p->m_pKeyboard = this;

	return S_OK;
}

STDMETHODIMP cLayerSite::IsChild(ILayerSite *pSite, VARIANT_BOOL bTestUnclipped, VARIANT_BOOL *pbIsChild)
{
   cLayerSite *pInternalSite = static_cast< cLayerSite * >( pSite );

   *pbIsChild = ( ( bTestUnclipped ) ? isChild( pInternalSite ) : isClippedChild( pInternalSite ) ) ? VARIANT_TRUE : VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP cLayerSite::put_Popup(VARIANT_BOOL newVal)
{
   if( newVal )
   {
#ifdef _DEBUG
      // Check for an existing popup record
      for( cManager::cLayerList::iterator i = cManager::_p->m_popups.begin(); i != cManager::_p->m_popups.end(); ++ i )
         _ASSERTE( *i != this );
#endif

      // Ok, add it into the list
      cManager::_p->m_popups.push_back( this );
   }
   else
   {
      for( cManager::cLayerList::iterator i = cManager::_p->m_popups.begin(); i != cManager::_p->m_popups.end(); ++ i )
      {
         if( *i == this )
         {
            cManager::_p->m_popups.erase( i );
            return S_OK;
         }
      }

      // This popup is not in the list
      _ASSERTE( FALSE );
   }

	return S_OK;
}

STDMETHODIMP cLayerSite::get_ScreenPosition(LPRECT pVal)
{
   CComPtr< ICanvas > pPrimary;

   cManager::_p->GetPrimarySurface( &pPrimary );

   {
      ClipParams cp;
      pPrimary->GetClipParams( &cp );

      // First push the identity RECT
      RECT rcIdentity = { 0, 0, cp.window.right - cp.window.left, cp.window.bottom - cp.window.top };
      VARIANT_BOOL bVisible;
      pPrimary->PushClipRect( &rcIdentity, &bVisible );

      // Now set the origin
      POINT pt = { cp.window.left, cp.window.top };
      pPrimary->OffsetOrg( &pt, &bVisible );
   }

   // ::MessageBox( NULL, _T( "cLayerSite::get_ScreenPosition" ), _T( "Inject.dll" ), MB_OK );

   clipCanvas( pPrimary );

   // Extract the bounds
   ClipParams cp;
   pPrimary->GetClipParams( &cp );

   *pVal = cp.window;

	return S_OK;
}

STDMETHODIMP cLayerSite::StartTimer(long nID, long nInterval)
{
#ifdef _DEBUG
   // Walk through the list of timers to make sure the ID isn't in use
   for( cTimerList::iterator i = m_timers.begin(); i != m_timers.end(); ++ i )
      _ASSERTE( i->m_nID != nID );
#endif

   // NOTE: An interval of 0 is valid in that it'll fire every frame, this would be an
   // alternate method to creating an animated layer
   _ASSERTE( nInterval >= 0 );

   // The layer should implement ILayerTimer
   _ASSERTE( m_dwSinkCaps & eSinkCapTimer );

   // Add in the timer
   cTimer t = { ::timeGetTime(), nID, nInterval, 0 };
   m_timers.push_back( t );

	return S_OK;
}

STDMETHODIMP cLayerSite::EndTimer(long nID)
{
   // Find the timer by ID
   for( cTimerList::iterator i = m_timers.begin(); i != m_timers.end(); ++ i )
   {
      if( i->m_nID == nID )
      {
         m_timers.erase( i );
         return S_OK;
      }
   }

	return E_INVALIDARG;
}

STDMETHODIMP cLayerSite::get_Transparent(VARIANT_BOOL *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = ( m_params.render & eRenderTransparent ) ? VARIANT_TRUE : VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP cLayerSite::put_Transparent(VARIANT_BOOL newVal)
{
   if( newVal )
      m_params.render |= eRenderTransparent;
   else
      m_params.render &= ~eRenderTransparent;

	return S_OK;
}

STDMETHODIMP cLayerSite::put_Alpha(long Alpha)
{
	m_Alpha = Alpha;
	return S_OK;
}