All 5 phases of the open-source Decal rebuild: Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil) Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters - Decal.Core, DecalControls, DecalRender, D3DService Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL Phase 4: DenAgent WinForms tray application Phase 5: WiX installer and build script 25 C# projects building with 0 errors. Native C++ projects require VS 2022 + Windows SDK (x86). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
863 lines
22 KiB
C++
863 lines
22 KiB
C++
// 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;
|
|
}
|