open_toontown_panda3d/direct/src/plugin_activex/P3DActiveXCtrl.cpp

618 lines
16 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file P3DActiveXCtrl.cpp
* @author atrestman
* @date 2009-09-14
*/
// P3DActiveXCtrl.cpp : Implementation of the CP3DActiveXCtrl ActiveX Control
// class.
#include "stdafx.h"
#include "P3DActiveX.h"
#include "P3DActiveXCtrl.h"
#include "P3DActiveXPropPage.h"
#include "PPBrowserObject.h"
#include <Mshtml.h>
#include <atlconv.h>
#include <comutil.h>
#include <strstream>
class CPropbagPropExchange : public CPropExchange
{
public:
CPropbagPropExchange(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog,
BOOL bLoading, BOOL bSaveAllProperties=FALSE);
~CPropbagPropExchange();
// Operations
virtual BOOL ExchangeProp(LPCTSTR pszPropName, VARTYPE vtProp,
void* pvProp, const void* pvDefault = NULL);
virtual BOOL ExchangeBlobProp(LPCTSTR pszPropName, HGLOBAL* phBlob,
HGLOBAL hBlobDefault = NULL);
virtual BOOL ExchangeFontProp(LPCTSTR pszPropName, CFontHolder& font,
const FONTDESC* pFontDesc, LPFONTDISP pFontDispAmbient);
virtual BOOL ExchangePersistentProp(LPCTSTR pszPropName,
LPUNKNOWN* ppUnk, REFIID iid, LPUNKNOWN pUnkDefault);
// Implementation
LPPROPERTYBAG m_pPropBag;
LPERRORLOG m_pErrorLog;
BOOL m_bSaveAllProperties;
};
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
IMPLEMENT_DYNCREATE(CP3DActiveXCtrl, COleControl)
// Message map
BEGIN_MESSAGE_MAP(CP3DActiveXCtrl, COleControl)
ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
ON_WM_CREATE()
ON_MESSAGE(WM_PANDA_NOTIFICATION, OnPandaNotification)
END_MESSAGE_MAP()
// Dispatch map
BEGIN_DISPATCH_MAP(CP3DActiveXCtrl, COleControl)
DISP_PROPERTY_NOTIFY_ID(CP3DActiveXCtrl, "main", dispidmain, m_pPandaObject, OnmainChanged, VT_DISPATCH)
END_DISPATCH_MAP()
// Event map
BEGIN_EVENT_MAP(CP3DActiveXCtrl, COleControl)
END_EVENT_MAP()
// Property pages
// TODO: Add more property pages as needed. Remember to increase the count!
BEGIN_PROPPAGEIDS(CP3DActiveXCtrl, 1)
PROPPAGEID(CP3DActiveXPropPage::guid)
END_PROPPAGEIDS(CP3DActiveXCtrl)
// Initialize class factory and guid
IMPLEMENT_OLECREATE_EX(CP3DActiveXCtrl, "P3DACTIVEX.P3DActiveXCtrl.1",
0x924b4927, 0xd3ba, 0x41ea, 0x9f, 0x7e, 0x8a, 0x89, 0x19, 0x4a, 0xb3, 0xac)
// Type library ID and version
IMPLEMENT_OLETYPELIB(CP3DActiveXCtrl, _tlid, _wVerMajor, _wVerMinor)
// Interface IDs
const IID BASED_CODE IID_DP3DActiveX =
{ 0x76904D54, 0xCC5, 0x4DBB, { 0xB0, 0x22, 0xF4, 0x8B, 0x1E, 0x95, 0x18, 0x3B } };
const IID BASED_CODE IID_DP3DActiveXEvents =
{ 0x1B2413ED, 0x51C8, 0x495E, { 0xB9, 0x17, 0x98, 0x3C, 0x45, 0x9B, 0x8F, 0xC7 } };
// Control type information
static const DWORD BASED_CODE _dwP3DActiveXOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CP3DActiveXCtrl, IDS_P3DACTIVEX, _dwP3DActiveXOleMisc)
// CP3DActiveXCtrl::CP3DActiveXCtrlFactory::UpdateRegistry - Adds or removes
// system registry entries for CP3DActiveXCtrl
BOOL CP3DActiveXCtrl::CP3DActiveXCtrlFactory::UpdateRegistry(BOOL bRegister)
{
// TODO: Verify that your control follows apartment-model threading rules.
// Refer to MFC TechNote 64 for more information. If your control does
// not conform to the apartment-model rules, then you must modify the code
// below, changing the 6th parameter from afxRegApartmentThreading to 0.
if (bRegister)
return AfxOleRegisterControlClass(
AfxGetInstanceHandle(),
m_clsid,
m_lpszProgID,
IDS_P3DACTIVEX,
IDB_P3DACTIVEX,
afxRegApartmentThreading,
_dwP3DActiveXOleMisc,
_tlid,
_wVerMajor,
_wVerMinor);
else
return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}
// CP3DActiveXCtrl::CP3DActiveXCtrl - Constructor
CP3DActiveXCtrl::CP3DActiveXCtrl() : m_instance( *this ), m_pPandaObject( NULL )
{
InitializeIIDs(&IID_DP3DActiveX, &IID_DP3DActiveXEvents);
// TODO: Initialize your control's instance data here.
_state = S_init;
// The init thread is initially not running.
_init_not_running.SetEvent();
}
// CP3DActiveXCtrl::~CP3DActiveXCtrl - Destructor
CP3DActiveXCtrl::~CP3DActiveXCtrl()
{
// TODO: Cleanup your control's instance data here.
if ( m_pPandaObject )
{
m_pPandaObject->Release();
}
}
// CP3DActiveXCtrl::OnDraw - Drawing function
void CP3DActiveXCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
if (!pdc)
return;
switch (_state) {
case S_init:
{
_state = S_loading;
// The first time we get the Draw message, we know we're sufficiently
// set up to start downloading the instance.
m_instance.read_tokens();
get_twirl_bitmaps();
// Start the twirl timer going.
SetTimer(1, 100, timer_callback);
_init_time = GetTickCount();
// But do most of the setup in a child thread, so we don't lock up the
// browser GUI while the instance gets itself downloaded and such.
_init_not_running.ResetEvent(); // Now the init thread is running.
if (_beginthread(st_init, 0, this) == -1L) {
nout << "Couldn't start thread.\n";
_init_not_running.SetEvent();
_state = S_failed;
}
}
break;
case S_loading:
// Waiting for the init thread to finish.
break;
case S_ready:
// Now the instance has downloaded, so set it going.
{
_state = S_started;
KillTimer(1);
m_instance.Start( m_instance.GetP3DFilename( ) );
}
break;
case S_started:
// The instance is running, no need to draw anything.
KillTimer(1);
DoSuperclassPaint(pdc, rcBounds);
return;
case S_failed:
// Something went wrong. Go ahead and draw the failed icon.
KillTimer(1);
break;
}
// The instance is setting itself up. In the meantime, draw the
// background and the twirling icon.
// Paint the background.
CBrush brBackGnd(RGB(m_instance._bgcolor_r, m_instance._bgcolor_g, m_instance._bgcolor_b));
pdc->FillRect(rcBounds, &brBackGnd);
DWORD now = GetTickCount();
// Don't draw the twirling icon until at least half a second has passed,
// so we don't distract people by drawing it unnecessarily.
if (_state == S_failed || (now - _init_time) >= 500) {
int step = (now / 100) % twirl_num_steps;
if (_state == S_failed) {
step = twirl_num_steps;
}
// Create an in-memory DC compatible with the display DC we're using to
// paint
CDC dcMemory;
dcMemory.CreateCompatibleDC(pdc);
// Select the bitmap into the in-memory DC
dcMemory.SelectObject(&_twirl_bitmaps[step]);
// Find a centerpoint for the bitmap in the client area
CRect rect;
GetClientRect(&rect);
int nX = rect.left + (rect.Width() - twirl_width) / 2;
int nY = rect.top + (rect.Height() - twirl_height) / 2;
// Copy the bits from the in-memory DC into the on-screen DC to actually
// do the painting. Use the centerpoint we computed for the target
// offset.
pdc->BitBlt(nX, nY, twirl_width, twirl_height, &dcMemory,
0, 0, SRCCOPY);
}
}
void CP3DActiveXCtrl::OnClose( DWORD dwSaveOption )
{
m_instance.Stop();
// Make sure the init thread has finished.
if (_state == S_loading) {
nout << "Waiting for thread stop\n" << flush;
::WaitForSingleObject( _init_not_running.m_hObject, INFINITE );
nout << "Done waiting for thread stop\n" << flush;
}
COleControl::OnClose( dwSaveOption );
}
// CP3DActiveXCtrl::DoPropExchange - Persistence support
void CP3DActiveXCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: Call PX_ functions for each persistent custom property.
ExchangeProperties( pPX );
}
// CP3DActiveXCtrl::GetControlFlags - Flags to customize MFC's implementation
// of ActiveX controls.
DWORD CP3DActiveXCtrl::GetControlFlags()
{
DWORD dwFlags = COleControl::GetControlFlags();
// The control will not be redrawn when making the transition between the
// active and inactivate state.
dwFlags |= noFlickerActivate;
return dwFlags;
}
// CP3DActiveXCtrl::OnResetState - Reset control to default state
void CP3DActiveXCtrl::OnResetState()
{
COleControl::OnResetState(); // Resets defaults found in DoPropExchange
// TODO: Reset any other control state here.
}
// CP3DActiveXCtrl::PreCreateWindow - Modify parameters for CreateWindowEx
BOOL CP3DActiveXCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = _T("STATIC");
return COleControl::PreCreateWindow(cs);
}
// CP3DActiveXCtrl::IsSubclassedControl - This is a subclassed control
BOOL CP3DActiveXCtrl::IsSubclassedControl()
{
return TRUE;
}
// CP3DActiveXCtrl::OnOcmCommand - Handle command messages
LRESULT CP3DActiveXCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{
#ifdef _WIN32
WORD wNotifyCode = HIWORD(wParam);
#else
WORD wNotifyCode = HIWORD(lParam);
#endif
// TODO: Switch on wNotifyCode here.
return 0;
}
// CP3DActiveXCtrl message handlers
int CP3DActiveXCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
int error(0);
m_spClientSite = GetClientSite();
if ( !m_spClientSite )
{
return ( error = -1 );
}
CComPtr<IOleContainer> pOleContainer;
HRESULT hr = m_spClientSite->GetContainer( &pOleContainer );
if ( FAILED( hr ) || !pOleContainer )
{
return ( error = -1 );
}
CComPtr<IHTMLDocument2> pHtml2Doc;
hr = pOleContainer->QueryInterface( IID_IHTMLDocument, ( void** )&pHtml2Doc );
if ( FAILED( hr ) || !pHtml2Doc )
{
return ( error = -1 );
}
BSTR url;
hr = pHtml2Doc->get_URL( &url );
if ( FAILED( hr ) || !url )
{
return ( error = -1 );
}
m_hostingPageUrl = url;
m_hostingPageUrl.Replace( '\\', '/' );
::SysFreeString( url );
CComPtr<IHTMLElementCollection> pHtmlElementCollect;
hr = pHtml2Doc->get_scripts( &pHtmlElementCollect );
if ( FAILED( hr ) || !pHtmlElementCollect )
{
return ( error = -1 );
}
long collectionLength = 0;
hr = pHtmlElementCollect->get_length( &collectionLength );
if ( FAILED( hr ) || !pHtmlElementCollect )
{
return ( error = -1 );
}
if ( collectionLength < 1 )
{
// javascript engine was not specified on the page. hence we need to
// initialize it by infusing javascript element tags
CComPtr<IHTMLElement> spHtmlElement;
hr = pHtml2Doc->createElement( CComBSTR( "script" ), &spHtmlElement );
if ( SUCCEEDED( hr ) && spHtmlElement )
{
hr = spHtmlElement->put_language( CComBSTR( "javascript" ) );
if ( SUCCEEDED( hr ) )
{
CComPtr<IHTMLDOMNode> spElementDomNode;
hr = spHtmlElement->QueryInterface( IID_IHTMLDOMNode, ( void** )&spElementDomNode );
if ( SUCCEEDED( hr ) && spElementDomNode )
{
CComPtr<IHTMLDOMNode> spHtmlDomNode;
hr = pHtml2Doc->QueryInterface( IID_IHTMLDOMNode, ( void** )&spHtmlDomNode );
if ( SUCCEEDED( hr ) && spHtmlDomNode )
{
CComPtr<IHTMLDOMNode> newNode;
hr = spHtmlDomNode->appendChild( spElementDomNode, &newNode );
}
}
}
}
}
return 0;
}
LRESULT CP3DActiveXCtrl::OnPandaNotification(WPARAM wParam, LPARAM lParam)
{
PPInstance::HandleRequestLoop();
return 0;
}
// The static init method.
void CP3DActiveXCtrl::
st_init(void *data) {
CP3DActiveXCtrl *self = (CP3DActiveXCtrl *)data;
self->init();
self->_init_not_running.SetEvent();
}
// The init method. This is called once at startup, in a child thread.
int CP3DActiveXCtrl::init( )
{
int error( 0 );
std::string p3dDllFilename;
error = m_instance.DownloadP3DComponents( p3dDllFilename );
if ( !error && !( p3dDllFilename.empty() ) )
{
error = m_instance.LoadPlugin( p3dDllFilename );
if ( !error )
{
m_pPandaObject = new PPandaObject( this, NULL );
if (_state == S_loading) {
// Ready to start.
_state = S_ready;
return error;
}
}
}
if (_state == S_loading) {
// Something went wrong.
_state = S_failed;
}
return error;
}
HRESULT CP3DActiveXCtrl::ExchangeProperties( CPropExchange* pPX )
{
USES_CONVERSION;
HRESULT hr = E_FAIL;
if ( !pPX )
{
return hr;
}
CPropbagPropExchange* pPropExchange = dynamic_cast<CPropbagPropExchange*>( pPX );
if ( !pPropExchange || !pPropExchange->m_pPropBag )
{
return hr;
}
// get the property bag version 2
IPropertyBag2 * pPropBag2;
hr = pPropExchange->m_pPropBag->QueryInterface( IID_IPropertyBag2, (void**)&pPropBag2);
if (! SUCCEEDED (hr)) {
return hr;
}
// get the number of parameters
unsigned long aNum;
hr = pPropBag2->CountProperties (& aNum);
ATLASSERT (hr >= 0);
if (! SUCCEEDED (hr)) {
return hr;
}
if (aNum == 0) return S_OK;
// allocate the parameters names
PROPBAG2 * aPropNames = new PROPBAG2[aNum];
unsigned long aReaded;
// get the param names
hr = pPropBag2->GetPropertyInfo (0, aNum, aPropNames, & aReaded);
ATLASSERT (hr >= 0);
if (! SUCCEEDED (hr)) {
delete[] aPropNames;
return hr;
}
// allocate the variants and result array
CComVariant * aVal = new CComVariant[aNum];
HRESULT * hvs = new HRESULT[aNum];
hr = pPropBag2->Read (aNum, aPropNames, NULL, aVal, hvs);
ATLASSERT (hr >= 0);
if (! SUCCEEDED (hr)) {
delete[] hvs;
delete[] aVal;
delete[] aPropNames;
return hr;
}
// get the params ; put them in the ParamList
for (unsigned long ind = 0; ind < aNum; ind++) {
std::pair< CString, CString> p( CString( aPropNames[ind].pstrName ), CString( aVal[ind] ) );
m_parameters.push_back( p );
// do_what_you_want_with (OLE2T (aPropNames[ind].pstrName), aVal[ind]);
}
// delete the unused arrays
delete[] hvs;
delete[] aVal;
delete[] aPropNames;
return hr;
}
P3D_object* CP3DActiveXCtrl::GetP3DObject( )
{
if (_state == S_started) {
return m_instance.m_p3dObject;
}
return NULL;
}
IOleClientSite* CP3DActiveXCtrl::GetClientSte( )
{
return GetClientSite();
}
void CP3DActiveXCtrl::OnmainChanged(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: Add your property handler code here
SetModifiedFlag();
}
void CP3DActiveXCtrl::
get_twirl_bitmaps() {
static const size_t twirl_size = twirl_width * twirl_height;
unsigned char twirl_data[twirl_size * 3];
unsigned char new_data[twirl_size * 4];
for (int step = 0; step < twirl_num_steps + 1; ++step) {
get_twirl_data(twirl_data, twirl_size, step,
m_instance._fgcolor_r, m_instance._fgcolor_g, m_instance._fgcolor_b,
m_instance._bgcolor_r, m_instance._bgcolor_g, m_instance._bgcolor_b);
// Expand out the RGB channels into RGBA.
for (int yi = 0; yi < twirl_height; ++yi) {
const unsigned char *sp = twirl_data + yi * twirl_width * 3;
unsigned char *dp = new_data + yi * twirl_width * 4;
for (int xi = 0; xi < twirl_width; ++xi) {
// RGB <= BGR.
dp[0] = sp[2];
dp[1] = sp[1];
dp[2] = sp[0];
dp[3] = (unsigned char)0xff;
sp += 3;
dp += 4;
}
}
// Now load the image.
_twirl_bitmaps[step].CreateBitmap(twirl_width, twirl_height, 1, 32, new_data);
}
}
void CP3DActiveXCtrl::
timer_callback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) {
// Just invalidate the region and make it draw again.
::InvalidateRect(hwnd, NULL, FALSE);
}