openDecal/Native/Inject/Panel.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

647 lines
15 KiB
C++

// Panel.cpp : Implementation of cPanel
#include "stdafx.h"
#include "Inject.h"
#include "Panel.h"
#include "Manager.h"
/////////////////////////////////////////////////////////////////////////////
// cPanel
enum ePanelChildren
{
eViewFirst = 1000
};
cPanel::cPanel()
: m_nActiveView( -1 ),
m_bDragging( false ),
m_bSizingXL( false ),
m_bSizingXR( false ),
m_bSizingYT( false ),
m_bSizingYB( false ),
m_bTransparent( false ),
m_pcView( NULL ),
m_Alpha( 255 )
{
}
void cPanel::hideView()
{
m_pSite->Invalidate();
if( m_nActiveView == -1 )
// No active view currently
return;
CComPtr< ILayerSite > pActive;
m_pSite->get_Child( m_nActiveView, ePositionByID, &pActive );
static RECT rcHide = { 0, 0, 0, 0 };
pActive->put_Position( &rcHide );
if( m_pSink.p != NULL )
m_pSink->PanelDeactivate( m_nActiveView );
m_nActiveView = -1;
}
void cPanel::onCreate()
{
CComPtr< IPluginSite > pPlugin;
m_pSite->get_PluginSite( &pPlugin );
pPlugin->LoadBitmapPortal( 0x0600126F, &m_pBackground );
pPlugin->LoadBitmapPortal( 0x06001277, &m_pBorder );
BSTR bstrFontName;
pPlugin->get_FontName(&bstrFontName);
pPlugin->CreateFont( bstrFontName /*_bstr_t( _T( "Times New Roman" ) )*/, 15, eFontBold, &m_pTitle );
// Create the Button
CComPtr< IButton > pRollup;
HRESULT hRes = ::CoCreateInstance( CLSID_Button, NULL, CLSCTX_INPROC_SERVER, IID_IButton,
reinterpret_cast< void ** >( &pRollup ) );
_ASSERTE( SUCCEEDED( hRes ) );
CComPtr< ILayer > pBtnLayer;
pRollup->QueryInterface( &pBtnLayer );
LayerParams lp = { 1, { 180 - 16, 0, 180, 16 }, eRenderClipped };
m_pSite->CreateChild( &lp, pBtnLayer );
pRollup->put_Matte( RGB( 0, 0, 0 ) );
pRollup->SetImages( 0, 0x0600113C, 0x0600113B );
ICommandEventsImpl< BUTTON_CLOSE, cPanel >::advise( pRollup );
// Create the Increment Button
hRes = ::CoCreateInstance( CLSID_Button, NULL, CLSCTX_INPROC_SERVER, IID_IButton,
reinterpret_cast< void ** >( &m_pButtonInc ) );
_ASSERTE( SUCCEEDED( hRes ) );
CComPtr< ILayer > pBtnIncLayer;
m_pButtonInc->QueryInterface( &pBtnIncLayer );
LayerParams lpInc = { 2, { 180 - 32, 0, 180 - 32 + 16, 16 }, eRenderClipped };
m_pSite->CreateChild( &lpInc, pBtnIncLayer );
m_pButtonInc->put_Matte( RGB( 0, 0, 0 ) );
m_pButtonInc->SetImages( 0, 0x06001298, 0x06001299 );
ICommandEventsImpl< BUTTON_INC, cPanel >::advise( m_pButtonInc );
// Create the Decrement Button
hRes = ::CoCreateInstance( CLSID_Button, NULL, CLSCTX_INPROC_SERVER, IID_IButton,
reinterpret_cast< void ** >( &m_pButtonDec ) );
_ASSERTE( SUCCEEDED( hRes ) );
CComPtr< ILayer > pBtnDecLayer;
m_pButtonDec->QueryInterface( &pBtnDecLayer );
LayerParams lpDec = { 3, { 180 - 48, 0, 180 - 48 + 16, 16 }, eRenderClipped };
m_pSite->CreateChild( &lpDec, pBtnDecLayer );
m_pButtonDec->put_Matte( RGB( 0, 0, 0 ) );
m_pButtonDec->SetImages( 0, 0x06001295, 0x06001296 );
ICommandEventsImpl< BUTTON_DEC, cPanel >::advise( m_pButtonDec );
// Set this layer not transparent to be transparent
m_pSite->put_Transparent( VARIANT_FALSE );
RegKey key;
key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal" ) );
if(key.QueryDWORDValue("ViewAlpha", m_Alpha) != ERROR_SUCCESS)
m_Alpha = 255;
}
void cPanel::onDestroy()
{
m_pBorder.Release();
m_pBackground.Release();
m_pTitle.Release();
m_pButtonInc.Release();
m_pButtonDec.Release();
}
STDMETHODIMP cPanel::Render( ICanvas *pCanvas )
{
RECT rc;
m_pSite->get_Position( &rc );
SIZE szBorder;
m_pBorder->get_Size( &szBorder );
// Draw the background
RECT rc_pat = { 0, 0, rc.right - rc.left, rc.bottom - rc.top };
static POINT pt_pat = { 0, 0 };
if( ! m_bTransparent )
m_pBackground->PatBlt( pCanvas, &rc_pat, &pt_pat );
// Draw the borders
RECT rc_border_top = { 0, 0, rc.right - rc.left, szBorder.cy },
rc_border_bottom = { 0, rc.bottom - rc.top - szBorder.cy,
rc.right - rc.left, rc.bottom - rc.top };
pCanvas->Fill( &rc_border_top, RGB( 0, 0, 0 ) );
m_pBorder->PatBlt( pCanvas, &rc_border_top, &pt_pat );
pCanvas->Fill( &rc_border_bottom, RGB( 0, 0, 0 ) );
m_pBorder->PatBlt( pCanvas, &rc_border_bottom, &pt_pat );
// Draw the title text
if( _bstr_t(m_pVP->label).length() > 0 )
{
POINT ptText = { 24, szBorder.cy + 5 };
m_pTitle->DrawText( &ptText, m_pVP->label, 0, pCanvas );
}
if( m_pVP->icon != 0 )
{
CComPtr< IPluginSite > pPlugin;
m_pSite->get_PluginSite( &pPlugin );
CComPtr< IIconCache > pIconCache;
static SIZE szIcon = { 16, 16 };
pPlugin->GetIconCache( &szIcon, &pIconCache );
static POINT pt = { 4, szBorder.cy + 4 };
pIconCache->DrawIcon( &pt, m_pVP->icon, m_pVP->iconLibrary, pCanvas );
}
_ASSERTMEM( _CrtCheckMemory( ) );
return S_OK;
}
STDMETHODIMP cPanel::Reformat()
{
// Reset the size
CComPtr< IPluginSite > pPlugin;
m_pSite->get_PluginSite( &pPlugin );
SIZE szScreen;
pPlugin->GetScreenSize( &szScreen );
RECT rc = { m_pVP->left, m_pVP->top, m_pVP->left + m_pVP->width, m_pVP->top + m_pVP->height };
m_pSite->put_Position( &rc );
RECT rcCloseBtn = { m_pVP->width - 16, 0, m_pVP->width, 16 };
CComPtr< ILayerSite > pCloseBtnLS;
m_pSite->get_Child(1, ePositionByID, &pCloseBtnLS);
pCloseBtnLS->put_Position(&rcCloseBtn);
RECT rcIncBtn = { m_pVP->width - 32, 0, m_pVP->width - 32 + 16, 16 };
CComPtr< ILayerSite > pButtonInc;
m_pSite->get_Child(2, ePositionByID, &pButtonInc);
pButtonInc->put_Position( &rcIncBtn );
RECT rcDecBtn = { m_pVP->width - 48, 0, m_pVP->width - 48 + 16, 16 };
CComPtr< ILayerSite > pButtonDec;
m_pSite->get_Child(3, ePositionByID, &pButtonDec);
pButtonDec->put_Position( &rcDecBtn );
if( m_nActiveView != -1 )
{
// Position the active view
SIZE szBorder;
m_pBorder->get_Size( &szBorder );
CComPtr< ILayerSite > pActive;
m_pSite->get_Child( m_nActiveView, ePositionByID, &pActive );
RECT rcChild = { 0, 24 + szBorder.cy, m_pVP->width, m_pVP->height - szBorder.cy };
pActive->put_Position( &rcChild );
}
return S_OK;
}
STDMETHODIMP cPanel::AddView(long nViewID, ILayer *pLayer)
{
_ASSERTE( pLayer != NULL );
LayerParams p = { nViewID, { 0, 0, 0, 0 }, eRenderClipped };
// Set up the layer - note that it is not visible
return m_pSite->CreateChild( &p, pLayer );
}
STDMETHODIMP cPanel::ActivateView(long nViewID, ViewParams *pParams, long *pView)
{
// Hide the current view by resizing to 0,0
hideView();
m_pVP = pParams;
m_pcView = (cView*)pView;
VARIANT_BOOL isTransparent=VARIANT_FALSE ;
if (m_pcView) m_pcView->get_Transparent(&isTransparent) ;
put_Transparent(isTransparent) ;
if( m_pVP->alpha == -1 )
m_pSite->put_Alpha( m_Alpha );
else
m_pSite->put_Alpha( m_pVP->alpha );
#ifdef _DEBUG
// Make sure the child exists
CComPtr< ILayerSite > pChildSite;
_ASSERTE( SUCCEEDED( m_pSite->get_Child( nViewID, ePositionByID, &pChildSite ) ) );
#endif
// Locate the child
m_nActiveView = nViewID;
// Trick it into reformatting next frame
static RECT rcBig = { 0, 0, 1, 1 };
m_pSite->put_Position( &rcBig );
// Pop us to the front
m_pSite->moveToFront();
return S_OK;
}
STDMETHODIMP cPanel::RemoveView( long nViewID )
{
if( nViewID == m_nActiveView )
// If this is the current view, run and hide
Deactivate();
CComPtr< ILayerSite > pChildSite;
HRESULT hRes = m_pSite->get_Child( nViewID, ePositionByID, &pChildSite );
_ASSERTE( SUCCEEDED( hRes ) );
if( SUCCEEDED( hRes ) )
pChildSite->Destroy();
else
return E_FAIL;
return S_OK;
}
void cPanel::onCloseAccepted(long nID)
{
// We should only be getting commands from the button
_ASSERTE( nID == 1 );
Deactivate();
}
void cPanel::onAlphaInc(long nID)
{
// We should only be getting commands from the button
_ASSERTE( nID == 2 );
if(m_pVP->alpha == -1 )
{
m_pVP->alpha = m_Alpha + 5;
m_pSite->put_Alpha( m_pVP->alpha );
}
else
{
m_pVP->alpha += 5;
if( m_pVP->alpha > 255 )
m_pVP->alpha = 255;
m_pSite->put_Alpha( m_pVP->alpha );
}
}
void cPanel::onAlphaDec(long nID)
{
// We should only be getting commands from the button
_ASSERTE( nID == 3 );
if(m_pVP->alpha == -1 )
{
m_pVP->alpha = m_Alpha - 5;
m_pSite->put_Alpha( m_pVP->alpha );
}
else
{
m_pVP->alpha -= 5;
if( m_pVP->alpha < 0 )
m_pVP->alpha = 0;
m_pSite->put_Alpha( m_pVP->alpha );
}
}
STDMETHODIMP cPanel::get_ActiveView(long *pVal)
{
_ASSERTE( pVal != NULL );
*pVal = m_nActiveView;
return S_OK;
}
STDMETHODIMP cPanel::LoadView(long nViewID, IView *pView, IUnknown *pSchema)
{
_ASSERTE( pView != NULL );
_ASSERTE( pSchema != NULL );
long nAssigned;
// Set up the layer - note that it is not visible
return pView->LoadControl( m_pSite, nViewID, pSchema, &nAssigned );
}
STDMETHODIMP cPanel::LoadViewEx(long nViewID, IView *pView, IUnknown *pSchema, long lViewFlags)
{
_ASSERTE( pView != NULL );
_ASSERTE( pSchema != NULL );
long nAssigned;
if( lViewFlags & eTransparent )
m_bTransparent = true;
// Set up the layer - note that it is not visible
return pView->LoadControl( m_pSite, nViewID, pSchema, &nAssigned );
}
STDMETHODIMP cPanel::Deactivate()
{
if( m_nActiveView == -1 )
// No active view currently
return S_OK;
// Hide the current view by resizing to 0,0
hideView();
// Hide the entire panel
static RECT rcHide = { 0, 0, 0, 0 };
m_pSite->put_Position( &rcHide );
m_nActiveView = -1;
return S_OK;
}
STDMETHODIMP cPanel::putref_Sink(IPanelSink *newVal)
{
m_pSink = newVal;
return S_OK;
}
STDMETHODIMP cPanel::MouseEvent(long nMsg, long wParam, long lParam)
{
static RECT rcTemp;
static SIZE size;
switch(nMsg)
{
case WM_LBUTTONDOWN:
{
// Pop us to the front
m_pSite->moveToFront();
SIZE szBorder;
m_pBorder->get_Size( &szBorder );
POINTS ptm;
ptm = MAKEPOINTS(lParam);
if( !cManager::_p->m_bContainer )
ptm.y-=28;
if((ptm.x>=m_pVP->left+szBorder.cy) && (ptm.x<=m_pVP->left+m_pVP->width-szBorder.cy) && (ptm.y>=m_pVP->top+szBorder.cy) && (ptm.y<=m_pVP->top+28))
{
m_DeltaX = ptm.x-m_pVP->left;
m_DeltaY = ptm.y-m_pVP->top;
m_bDragging = true;
}
else
{
if(m_pcView->Fire_Size()==VARIANT_TRUE)
{
if((ptm.x>=m_pVP->left) && (ptm.x<=m_pVP->left+m_pVP->width))
{
if((ptm.y>=m_pVP->top) && (ptm.y<=m_pVP->top+szBorder.cy))
{
m_bSizingYT = true;
m_DeltaY = ptm.y-m_pVP->top;
} else if((ptm.y>=m_pVP->top+m_pVP->height-szBorder.cy) && (ptm.y<=m_pVP->top+m_pVP->height))
{
m_bSizingYB = true;
m_DeltaY = m_pVP->top+m_pVP->height-ptm.y;
}
}
if((ptm.y>=m_pVP->top) && (ptm.y<=m_pVP->top+m_pVP->height))
{
if((ptm.x>=m_pVP->left) && (ptm.x<=m_pVP->left+szBorder.cy))
{
m_bSizingXL = true;
m_DeltaX = ptm.x-m_pVP->left;
} else if((ptm.x>=m_pVP->left+m_pVP->width-szBorder.cy) && (ptm.x<=m_pVP->left+m_pVP->width))
{
m_bSizingXR = true;
m_DeltaX = m_pVP->left+m_pVP->width-ptm.x;
}
}
}
}
break;
}
case WM_LBUTTONUP:
{
m_bDragging = m_bSizingXL = m_bSizingXR = m_bSizingYT = m_bSizingYB = false;
break;
}
case WM_MOUSEMOVE:
{
if(wParam==MK_LBUTTON)
{
bool bUpdate = false;
POINTS ptm;
ptm = MAKEPOINTS(lParam);
if( !cManager::_p->m_bContainer )
ptm.y-=28;
if(m_bDragging)
{
m_pVP->left = ptm.x-m_DeltaX;
m_pVP->top = ptm.y-m_DeltaY;
/* Drakier: make sure they aren't being bad little kittens and
moving the view offscreen! */
// Drakier: Get the pSite and Screen Size
CComPtr< IPluginSite > pPlugin;
CComPtr< IACHooks > pHooks;
m_pSite->get_PluginSite( &pPlugin );
SIZE szScreen, sz;
pPlugin->GetScreenSize( &sz );
szScreen.cx = sz.cx;
szScreen.cy = sz.cy;
long lX = sz.cx, lY = sz.cy;
// Drakier: if we are not in the container, get the 3D Area
if ( !cManager::_p->m_bContainer )
{
pPlugin->get_Hooks( &pHooks );
pHooks->get_Area3DHeight( &lY );
pHooks->get_Area3DWidth( &lX );
pHooks.Release();
}
pPlugin.Release();
// Drakier: if we are in the container, or the 3D area's are NULL
if ( (cManager::_p->m_bContainer) || (&lX == NULL) || (&lY == NULL) )
{
szScreen.cx = sz.cx - 308; // 308 = PANEL_SIZE
szScreen.cy = sz.cy;
}
else // Drakier: If everything comes out right and we are not in container
{
if( (&lX != NULL) && (lX > 0) && (lX < 5000) )
szScreen.cx = lX;
if( (&lY != NULL) && (lY > 0) && (lY < 5000) )
szScreen.cy = lY;
}
if ( m_pVP->left < 0 )
m_pVP->left = 0;
if ( m_pVP->top < 0 )
m_pVP->top = 0;
if ( (m_pVP->left + m_pVP->width) > szScreen.cx )
m_pVP->left = szScreen.cx - m_pVP->width;
if ( (m_pVP->top + m_pVP->height) > szScreen.cy )
m_pVP->top = szScreen.cy - m_pVP->height;
bUpdate = true;
}
else
{
if(m_bSizingYT)
{
rcTemp.bottom = m_pVP->height + m_pVP->top - (ptm.y-m_DeltaY);
rcTemp.top = ptm.y-m_DeltaY;
if(rcTemp.bottom > 34)
{
m_pVP->height = rcTemp.bottom;
m_pVP->top = rcTemp.top;
}
else
{
m_pVP->top = (m_pVP->top + m_pVP->height) - 34;
m_pVP->height = 34;
}
bUpdate = true;
}
else if(m_bSizingYB)
{
rcTemp.bottom = ptm.y-m_pVP->top+m_DeltaY;
if(rcTemp.bottom > 34)
m_pVP->height = rcTemp.bottom;
else
m_pVP->height = 34;
bUpdate = true;
}
if(m_bSizingXL)
{
rcTemp.right = m_pVP->width + m_pVP->left - (ptm.x-m_DeltaX);
rcTemp.left = ptm.x-m_DeltaX;
m_pTitle->MeasureText(m_pVP->label, &size);
if(rcTemp.right > size.cx + 48)
{
m_pVP->width = rcTemp.right;
m_pVP->left = rcTemp.left;
}
else
{
m_pVP->left = (m_pVP->left + m_pVP->width) - (size.cx + 48);
m_pVP->width = size.cx+48;
}
bUpdate = true;
}
else if(m_bSizingXR)
{
rcTemp.right = ptm.x-m_pVP->left+m_DeltaX;
m_pTitle->MeasureText(m_pVP->label, &size);
if(rcTemp.right > size.cx + 48)
m_pVP->width = rcTemp.right;
else
m_pVP->width = size.cx + 48;
bUpdate = true;
}
}
if(bUpdate)
{
if(!m_bDragging)
{
//RECT rc = { m_pcView->m_VP.left, m_pcView->m_VP.top, m_pcView->m_VP.width, m_pcView->m_VP.height };
m_pcView->Fire_Sizing(/*m_pcView*//*&m_pcView->m_VP*//*m_pcView->m_VP.left, m_pcView->m_VP.top,*/ 0, 0, m_pcView->m_VP.width, m_pcView->m_VP.height-34);
}
static RECT rcBig = { 0, 0, 1, 1 };
m_pSite->put_Position( &rcBig );
}
}
break;
}
}
return S_OK;
}
STDMETHODIMP cPanel::get_Transparent(VARIANT_BOOL *pVal)
{
*pVal = ( m_bTransparent ? VARIANT_TRUE : VARIANT_FALSE );
return S_OK;
}
STDMETHODIMP cPanel::put_Transparent(VARIANT_BOOL newVal)
{
if( newVal == VARIANT_FALSE )
m_bTransparent = false;
else
m_bTransparent = true;
if( m_pVP->alpha == -1 )
m_pSite->put_Alpha( m_Alpha );
else
m_pSite->put_Alpha( m_pVP->alpha );
m_pSite->Invalidate();
m_pSite->Reformat();
return S_OK;
}
STDMETHODIMP cPanel::put_Params(ViewParams *newVal)
{
if( newVal == NULL )
return E_POINTER;
m_pVP = newVal;
return S_OK;
}