openDecal/Native/DecalControls/Notebook.cpp
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
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>
2026-02-08 18:27:56 +01:00

281 lines
6.5 KiB
C++

// Notebook.cpp : Implementation of cNotebook
#include "stdafx.h"
#include "DecalControls.h"
#include "Notebook.h"
/////////////////////////////////////////////////////////////////////////////
// cNotebook
cNotebook::cNotebook():
m_nNextPageID( 1001 ),
m_nActiveTab( -1 ),
m_nCaptureTab( -1 ),
m_bHitCapture( false )
{
}
long cNotebook::hitTest( MouseState *pMS )
{
if( pMS->over != this )
// Not hitting us, therefore not hitting a tab
return -1;
// Yes for y boundaries
if( pMS->client.y < 0 || pMS->client.y >= 16 )
return -1;
// Walk through the list of tabs
for( cPageList::iterator i = m_pages.begin(); i != m_pages.end(); ++ i )
{
if( pMS->client.x >= i->m_nLeft && pMS->client.x < i->m_nRight )
// Hit a tab!
return ( i - m_pages.begin() );
}
// No tabs hit
return -1;
}
void cNotebook::positionActiveTab()
{
if( m_nActiveTab == -1 )
return;
RECT rcClient;
m_pSite->get_Position( &rcClient );
CComPtr< ILayerSite > pChild;
m_pSite->get_Child( m_pages[ m_nActiveTab ].m_nPageID, ePositionByID, &pChild );
RECT rcChild = { 0, 16, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top };
pChild->put_Position( &rcChild );
}
STDMETHODIMP cNotebook::AddPage(BSTR strText, IControl *pClient)
{
cPage p;
p.m_strName = strText;
p.m_nPageID = m_nNextPageID ++;
CComPtr< ILayer > pClientLayer;
pClient->QueryInterface( &pClientLayer );
LayerParams lp = { p.m_nPageID, { 0, 0, 0, 0 }, eRenderClipped };
m_pSite->CreateChild( &lp, pClientLayer );
m_pages.push_back( p );
if( m_nActiveTab == -1 )
// Activate the page if it's the first one
m_nActiveTab = 0;
m_pSite->Reformat();
return S_OK;
}
void cNotebook::onCreate()
{
// Load the images
CComPtr< IPluginSite > pPlugin;
m_pSite->get_PluginSite( &pPlugin );
pPlugin->LoadBitmapFile( _bstr_t( _T( "Tab-Active.bmp" ) ), &m_pActive );
pPlugin->LoadBitmapFile( _bstr_t( _T( "Tab-Inactive.bmp" ) ), &m_pInactive );
BSTR bstrFontName;
pPlugin->get_FontName(&bstrFontName);
pPlugin->CreateFont( bstrFontName /*_bstr_t( _T( "Times New Roman" ) )*/, 14, 0, &m_pFont );
}
void cNotebook::onDestroy()
{
m_pActive.Release();
m_pInactive.Release();
}
#define TEXT_LR_MARGIN 5
STDMETHODIMP cNotebook::Reformat()
{
// Walk through the tabs and position the graphics
long nTabPos = 0;
for( cPageList::iterator i = m_pages.begin(); i != m_pages.end(); ++ i )
{
// Calculate the text length
SIZE sz;
m_pFont->MeasureText( i->m_strName, &sz );
long nWidth = sz.cx + TEXT_LR_MARGIN * 2;
i->m_nLeft = nTabPos;
i->m_nRight = nTabPos + nWidth;
nTabPos += nWidth;
}
if( m_nActiveTab != -1 )
positionActiveTab();
_ASSERTMEM( _CrtCheckMemory( ) );
return S_OK;
}
STDMETHODIMP cNotebook::Render( ICanvas *pCanvas )
{
for( cPageList::iterator i = m_pages.begin(); i != m_pages.end(); ++ i )
{
POINT ptTab = { i->m_nLeft, 0 };
long nWidth = i->m_nRight - i->m_nLeft;
long nIndex = i - m_pages.begin();
bool bActive = ( ( nIndex == m_nActiveTab ) || ( m_nCaptureTab != -1 && m_bHitCapture && nIndex == m_nCaptureTab ) );
if( bActive )
m_pActive->StretchBlt( pCanvas, &ptTab, nWidth, 7, 90 );
else
m_pInactive->StretchBlt( pCanvas, &ptTab, nWidth, 7, 90 );
// Draw the text
POINT ptText = { i->m_nLeft + TEXT_LR_MARGIN, 2 };
m_pFont->DrawText( &ptText, i->m_strName, ( bActive ) ? RGB( 0, 0, 0 ) : RGB( 192, 192, 192 ), pCanvas );
}
_ASSERTMEM( _CrtCheckMemory( ) );
return S_OK;
}
STDMETHODIMP cNotebook::MouseDown( MouseState *pMS )
{
m_nCaptureTab = hitTest( pMS );
if( m_nCaptureTab != -1 )
{
m_bHitCapture = true;
m_pSite->Invalidate();
}
return S_OK;
}
STDMETHODIMP cNotebook::MouseUp( MouseState *pMS )
{
_ASSERTMEM( _CrtCheckMemory( ) );
if( m_nCaptureTab == -1 )
return S_OK;
if( hitTest( pMS ) == m_nCaptureTab )
put_ActiveTab( m_nCaptureTab );
_ASSERTMEM( _CrtCheckMemory( ) );
m_nCaptureTab = -1;
return S_OK;
}
STDMETHODIMP cNotebook::MouseMove( MouseState *pMS )
{
if( m_nCaptureTab == -1 )
// We are hitting in progress
return S_OK;
// Check for a hit
bool bHitCapture = ( hitTest( pMS ) == m_nCaptureTab );
if( bHitCapture != m_bHitCapture )
{
m_bHitCapture = bHitCapture;
m_pSite->Invalidate();
}
return S_OK;
}
STDMETHODIMP cNotebook::get_ActiveTab(long *pVal)
{
*pVal = m_nActiveTab;
return S_OK;
}
STDMETHODIMP cNotebook::put_ActiveTab(long newVal)
{
if( m_nActiveTab != -1 )
{
// Hide the current active view
static RECT rcHide = { 0, 0, 0, 0 };
CComPtr< ILayerSite > pChild;
m_pSite->get_Child( m_pages[ m_nActiveTab ].m_nPageID, ePositionByID, &pChild );
pChild->put_Position( &rcHide );
}
m_nActiveTab = newVal;
if( m_nActiveTab != -1 )
positionActiveTab();
long nID;
m_pSite->get_ID( &nID );
Fire_Change( nID, m_nActiveTab );
m_pSite->Invalidate();
return S_OK;
}
STDMETHODIMP cNotebook::SchemaLoad(IView *pView, IUnknown *pSchema)
{
MSXML::IXMLDOMElementPtr pElement = pSchema;
MSXML::IXMLDOMElementPtr pPage;
for( MSXML::IXMLDOMNodeListPtr pPages = pElement->selectNodes( _T( "page" ) ); ( pPage = pPages->nextNode() ).GetInterfacePtr() != NULL; )
{
// We have the page, get the text, then recurse and generate the control
_variant_t vText = pPage->getAttribute( _T( "label" ) );
_ASSERTE( vText.vt == VT_BSTR );
cPage p;
p.m_strName = vText.bstrVal;
p.m_nPageID = m_nNextPageID ++;
m_pages.push_back( p );
MSXML::IXMLDOMElementPtr pControl = pPage->selectSingleNode( _T( "control" ) );
_ASSERTE( pControl.GetInterfacePtr() != NULL );
long nAssigned;
pView->LoadControl( m_pSite, p.m_nPageID, pControl, &nAssigned );
}
if( m_nActiveTab == -1 && m_pages.size() > 0 )
// Activate the page if it's the first one
m_nActiveTab = 0;
return S_OK;
}
STDMETHODIMP cNotebook::get_PageText(long nIndex, BSTR *pVal)
{
_ASSERTE( pVal != NULL );
_ASSERTE( nIndex >= 0 );
_ASSERTE( nIndex < m_pages.size() );
*pVal = OLE2BSTR( m_pages[ nIndex ].m_strName );
return S_OK;
}
STDMETHODIMP cNotebook::put_PageText(long nIndex, BSTR newVal)
{
_ASSERTE( newVal != NULL );
_ASSERTE( nIndex >= 0 );
_ASSERTE( nIndex < m_pages.size() );
m_pages[ nIndex ].m_strName = newVal;
m_pSite->Reformat();
return S_OK;
}