CARDCTL.CPP

//=--------------------------------------------------------------------------= 
// CardCtl.Cpp
//=--------------------------------------------------------------------------=
// Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//=--------------------------------------------------------------------------=
//
//
//
#include "IPServer.H"

#include "Guids.H"
#include "LocalObj.H"
#include "Util.H"
#include "Globals.H"
#include "Resource.H"
#include "cardctl.H"

#include "carddraw.h"

extern HBITMAP cardcacheMem[];
extern CRITICAL_SECTION drawCritSec;

// for ASSERT and FAIL
//
SZTHISFILE

WCHAR wszImage [] = L"Card";

//=--------------------------------------------------------------------------=
// all the events in this control
//
// TODO: add events here ...
//

static VARTYPE rgMouse[] = { VT_I2, VT_I2, VT_I4, VT_I4 };
enum { MouseMove=0, MouseDown, MouseUp, Click, DblClick };

static EVENTINFO rgEvents [] = {
{ DISPID_MOUSEMOVE, 4, rgMouse },
{ DISPID_MOUSEDOWN, 4, rgMouse },
{ DISPID_MOUSEUP, 4, rgMouse },
{ DISPID_CLICK, 0, NULL },
{ DISPID_DBLCLICK, 0, NULL },
};

//=--------------------------------------------------------------------------=
// array describing all of our property pages. these clsids are typically
// in guids.h
//
// TODO: add any additional property page guids here ...
//
const GUID *rgCardPropPages [] = {
&CLSID_CardGeneralPage
};

//=--------------------------------------------------------------------------=
// Custum Verb information
//
// TODO: add any custom verbs here in an array, using the VERBINFO structure.
// then mark the controld def'n in CardCtl.H with
// this verb array
//


//=--------------------------------------------------------------------------=
// CCardControl::CCardControl
//=--------------------------------------------------------------------------=
// "Being born is like being kidnapped. And then sold into slavery."
// - andy warhol (1928 - 87)
//
// Parameters:
// IUnknown * - [in]
//
// Notes:
//
#pragma warning(disable:4355) // using 'this' in constructor
CCardControl::CCardControl
(
IUnknown *pUnkOuter
)
: COleControl(pUnkOuter, OBJECT_TYPE_CTLCARD, (IDispatch *)this)
{
static SIZEL size = { CCardDraw::CardSizeX, CCardDraw::CardSizeY };

m_number = DefaultNumber;
m_suite = DefaultSuite;
m_invert = DefaultInvert;
m_cardalignment = DefaultCardAlignment;
BackBrush = NULL;

SetControlSize( &size );

#if 0
// for better performance when the cards are first displayed (such as
// when a full deck is displayed to the user), un-ifdef this code.
// for better initial control creation time, keep this code ifdef'd.

// initialize the standard 52 cards (no jokers, no backs)
if( !cardcacheMem[0] )
{
EnterCriticalSection( &drawCritSec );
if( !cardcacheMem[0] )
{
CCardDraw cd( CCardDraw::DrawAll );
HDC drawDc;
HBITMAP drawDcMem;
int t;

drawDc = CreateCompatibleDC( NULL );

for( t = 0; t < 52; t++ )
{
cardcacheMem[t] = cd.CreateDIBCard( drawDc );
drawDcMem = SelectObject( drawDc, cardcacheMem[t] );
cd.DrawCard( drawDc, (t/14) + 1, (t%14) + 1 );
SelectObject( drawDc, drawDcMem );
}
DeleteDC( drawDc );
}
LeaveCriticalSection( &drawCritSec );
}
#endif
}
#pragma warning(default:4355) // using 'this' in constructor

//=--------------------------------------------------------------------------=
// CCardControl::~CCardControl
//=--------------------------------------------------------------------------=
// "We all labour against our own cure, for death is the cure of all diseases"
// - Sir Thomas Browne (1605 - 82)
//
// Notes:
//
CCardControl::~CCardControl ()
{
if( BackBrush )
DeleteObject( BackBrush );
}

//=--------------------------------------------------------------------------=
// CCardControl:RegisterClassData
//=--------------------------------------------------------------------------=
// register the window class information for your control here.
// this information will automatically get cleaned up for you on DLL shutdown.
//
// Output:
// BOOL - FALSE means fatal error.
//
// Notes:
//
BOOL CCardControl::RegisterClassData()
{
WNDCLASS wndclass;

// TODO: register any additional information you find interesting here.
// this method is only called once for each type of control
//
memset(&wndclass, 0, sizeof(WNDCLASS));
wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC;
wndclass.lpfnWndProc = COleControl::ControlWindowProc;
wndclass.hInstance = g_hInstance;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszClassName = WNDCLASSNAMEOFCONTROL(OBJECT_TYPE_CTLCARD);

return RegisterClass(&wndclass);
}

//=--------------------------------------------------------------------------=
// CCardControl::InternalQueryInterface
//=--------------------------------------------------------------------------=
// qi for things only we support.
//
// Parameters:
// Parameters:
// REFIID - [in] interface they want
// void ** - [out] where they want to put the resulting object ptr.
//
// Output:
// HRESULT - S_OK, E_NOINTERFACE
//
// Notes:
//
HRESULT CCardControl::InternalQueryInterface
(
REFIID riid,
void **ppvObjOut
)
{
IUnknown *pUnk;

*ppvObjOut = NULL;

// TODO: if you want to support any additional interrfaces, then you should
// indicate that here. never forget to call COleControl's version in the
// case where you don't support the given interface.
//
if (DO_GUIDS_MATCH(riid, IID_ICard)) {
pUnk = (IUnknown *)(ICard *)this;
} else{
return COleControl::InternalQueryInterface(riid, ppvObjOut);
}

pUnk->AddRef();
*ppvObjOut = (void *)pUnk;
return S_OK;
}

//=--------------------------------------------------------------------------=
// CCardControl::LoadTextState
//=--------------------------------------------------------------------------=
// load in our text state for this control.
//
// Parameters:
// IPropertyBag * - [in] property bag to read from
// IErrorLog * - [in] errorlog object to use with proeprty bag
//
// Output:
// HRESULT
//
// Notes:
// - NOTE: if you have a binary object, then you should pass an unknown
// pointer to the property bag, and it will QI it for IPersistStream, and
// get said object to do a Load()
//
STDMETHODIMP CCardControl::LoadTextState
(
IPropertyBag *pPropertyBag,
IErrorLog *pErrorLog
)
{
VARIANT v;
HRESULT hr;

VariantInit(&v);

v.vt = VT_I2;
hr = pPropertyBag->Read(L"Number",&v,pErrorLog);
if (SUCCEEDED(hr))
m_number = (enumCardNumber) v.iVal;
ValidateNumber();
VariantClear(&v);

v.vt = VT_I2;
hr = pPropertyBag->Read(L"Suite",&v,pErrorLog);
if (SUCCEEDED(hr))
m_suite = (enumCardSuite) v.iVal;
ValidateSuite();
VariantClear(&v);

v.vt = VT_BOOL;
hr = pPropertyBag->Read(L"Invert",&v,pErrorLog);
if (SUCCEEDED(hr))
m_invert = v.boolVal;
VariantClear(&v);

v.vt = VT_I2;
hr = pPropertyBag->Read(L"CardAlignment",&v,pErrorLog);
if (SUCCEEDED(hr))
m_cardalignment = (enumCardAlignment) v.iVal;
ValidateCardAlignment();
VariantClear(&v);

return S_OK;
}

#defineSTREAMHDR_MAGIC1296924267

//=--------------------------------------------------------------------------=
// CCardControl::LoadBinaryState
//=--------------------------------------------------------------------------=
// loads in our binary state using streams.
//
// Parameters:
// IStream * - [in] stream to write to.
//
// Output:
// HRESULT
//
// Notes:
//
STDMETHODIMP CCardControl::LoadBinaryState
(
IStream *pStream
)
{
HRESULT hr;
DWORD sh;

hr = pStream->Read(&sh, sizeof(sh), NULL);
RETURN_ON_FAILURE(hr);

if (sh != STREAMHDR_MAGIC )
return E_UNEXPECTED;

hr = pStream->Read(&(m_number),sizeof(m_number),NULL);
RETURN_ON_FAILURE(hr);
ValidateNumber();

hr = pStream->Read(&(m_suite),sizeof(m_suite),NULL);
RETURN_ON_FAILURE(hr);
ValidateSuite();

hr = pStream->Read(&(m_invert),sizeof(m_invert),NULL);
RETURN_ON_FAILURE(hr);

hr = pStream->Read(&(m_cardalignment),sizeof(m_cardalignment),NULL);
RETURN_ON_FAILURE(hr);
ValidateCardAlignment();

return S_OK;
}

//=--------------------------------------------------------------------------=
// CCardControl::SaveTextState
//=--------------------------------------------------------------------------=
// saves out the text state for this control using a property bag.
//
// Parameters:
// IPropertyBag * - [in] the property bag with which to work.
// BOOL - [in] if TRUE, then write out ALL properties, even
// if they're their the default value ...
//
// Output:
// HRESULT
//
// Notes:
//
STDMETHODIMP CCardControl::SaveTextState
(
IPropertyBag *pPropertyBag,
BOOL fWriteDefaults
)
{
VARIANT v;
HRESULT hr;

if( m_number != DefaultNumber )
{
VariantInit(&v);
v.vt = VT_I2;
v.iVal = m_number;
hr = pPropertyBag->Write( L"Number", &v);
}

if( m_suite != DefaultSuite )
{
VariantInit(&v);
v.vt = VT_I2;
v.iVal = m_suite;
hr = pPropertyBag->Write( L"Suite", &v);
}

if( m_invert != DefaultInvert )
{
VariantInit(&v);
v.vt = VT_BOOL;
v.boolVal = m_invert;
hr = pPropertyBag->Write( L"Invert", &v);
}

if( m_cardalignment != DefaultCardAlignment )
{
VariantInit(&v);
v.vt = VT_I2;
v.iVal = m_cardalignment;
hr = pPropertyBag->Write( L"CardAlignment", &v);
}

return S_OK;
}

//=--------------------------------------------------------------------------=
// CCardControl::SaveBinaryState
//=--------------------------------------------------------------------------=
// save out the binary state for this control, using the given IStream object.
//
// Parameters:
// IStream * - [in] save to which you should save.
//
// Output:
// HRESULT
//
// Notes:
// - it is important that you seek to the end of where you saved your
// properties when you're done with the IStream.
//
STDMETHODIMP CCardControl::SaveBinaryState
(
IStream *pStream
)
{
HRESULT hr;
DWORD sh = STREAMHDR_MAGIC;

// write out the stream hdr.
//
hr = pStream->Write(&sh, sizeof(sh), NULL);
RETURN_ON_FAILURE(hr);

hr = pStream->Write(&(m_number),sizeof(m_number),NULL);
RETURN_ON_FAILURE(hr);

hr = pStream->Write(&(m_suite),sizeof(m_suite),NULL);
RETURN_ON_FAILURE(hr);

hr = pStream->Write(&(m_invert),sizeof(m_invert),NULL);
RETURN_ON_FAILURE(hr);

hr = pStream->Write(&(m_cardalignment),sizeof(m_cardalignment),NULL);
RETURN_ON_FAILURE(hr);

return S_OK;
}


//=--------------------------------------------------------------------------=
// CCardControl::OnDraw
//=--------------------------------------------------------------------------=
// "I don't very much enjoy looking at paintings in general. i know too
// much about them. i take them apart."
// - georgia o'keeffe (1887-1986)
//
// Parameters:
// HDC - [in] HDC to draw to
// LPCRECTL - [in] rect we're drawing to
// LPCRECTL - [in] window extent and origin for meta-files
// HDC - [in] HIC for target device
//
// Output:
// HRESULT
//
// Notes:
//
HRESULT CCardControl::OnDraw
(
DWORD dvaspect,
HDC hdcDraw,
LPCRECTL prcBounds,
LPCRECTL prcWBounds,
HDC hicTargetDevice,
BOOL fOptimize
)
{
int OffsetX, OffsetY;
RECTL rcBoundsDP;
int width, height;
int savedDC;
HDC drawDc;
HBITMAP drawDcMem;
int c;

if( !Windowless() && !BackBrush )
{
OLE_COLOR BackColor;
COLORREF BackColorCR;
if( !GetAmbientProperty( DISPID_AMBIENT_BACKCOLOR, VT_I4, &BackColor ) )
BackColor = 0;
OleTranslateColor( BackColor, NULL, &BackColorCR );
BackBrush = CreateSolidBrush( BackColorCR );
}

rcBoundsDP = *prcBounds;
LPtoDP( hdcDraw, (POINT *) &rcBoundsDP, 2 );

width = rcBoundsDP.right - rcBoundsDP.left;
height = rcBoundsDP.bottom - rcBoundsDP.top;

switch( m_cardalignment )
{
case CardTopCenter:
case CardCenter:
case CardBottomCenter:
OffsetX = (width - CCardDraw::CardSizeX) / 2;
break;
case CardTopRight:
case CardCenterRight:
case CardBottomRight:
OffsetX = (width - CCardDraw::CardSizeX);
break;
default:
OffsetX = 0;
break;
}
switch( m_cardalignment )
{
case CardCenterRight:
case CardCenter:
case CardCenterLeft:
OffsetY = (height - CCardDraw::CardSizeY) / 2;
break;
case CardBottomRight:
case CardBottomCenter:
case CardBottomLeft:
OffsetY = (height - CCardDraw::CardSizeY);
break;
default:
OffsetY = 0;
break;
}

if( BackBrush )
FillRect( hdcDraw, (RECT *) prcBounds, BackBrush );

if( m_suite == CardBlank )
return S_OK;

savedDC = SaveDC( hdcDraw );

SetMapMode( hdcDraw, MM_TEXT );
SetWindowOrgEx( hdcDraw, -(OffsetX + rcBoundsDP.left),
-(OffsetY + rcBoundsDP.top), NULL );
SetViewportOrgEx( hdcDraw, 0, 0, NULL );

if( width < CCardDraw::CardSizeX || height < CCardDraw::CardSizeY )
{
HRGN clipRgn;
clipRgn = CreateRectRgn( rcBoundsDP.left,
rcBoundsDP.top,
rcBoundsDP.left + width,
rcBoundsDP.top + height );
SelectClipRgn( hdcDraw, clipRgn );
DeleteObject( clipRgn );
}

SelectObject( hdcDraw, GetStockObject( m_invert ? WHITE_PEN : BLACK_PEN ) );

drawDc = CreateCompatibleDC( NULL );

if( m_suite > 0 )
c = (m_suite-1)*14 + (m_number-1);
else
c = (m_suite < -12) ? 56 : -m_suite + 56;

if( !cardcacheMem[c] )
{
CCardDraw cd( (c > 55 ? CCardDraw::DrawBack : (int) m_number) );

EnterCriticalSection( &drawCritSec );
if( !cardcacheMem[c] )
{
cardcacheMem[c] = cd.CreateDIBCard( hdcDraw );
drawDcMem = (HBITMAP) SelectObject( drawDc, cardcacheMem[c] );
cd.DrawCard( drawDc, m_suite, m_number );
}
else
drawDcMem = (HBITMAP) SelectObject( drawDc, cardcacheMem[c] );
LeaveCriticalSection( &drawCritSec );
}
else
drawDcMem = (HBITMAP) SelectObject( drawDc, cardcacheMem[c] );

if( m_cardalignment != CardStretch )
{
BitBlt( hdcDraw, 1, 1, CCardDraw::CardSizeX-2, CCardDraw::CardSizeY-2,
drawDc, 1, 1, m_invert ? NOTSRCCOPY : SRCCOPY );
Polyline( hdcDraw, CCardDraw::CardBorder,
sizeof(CCardDraw::CardBorder)/sizeof(POINT) );
}
else
{
POINT border[9];
memcpy( border, CCardDraw::CardBorder, sizeof(border) );
border[2].x = border[5].x = width-3;
border[3].x = border[4].x = width-1;
border[4].y = border[7].y = height-3;
border[5].y = border[6].y = height-1;
StretchBlt( hdcDraw, 1, 1, width-2, height-2,
drawDc, 1, 1,
CCardDraw::CardSizeX-2,
CCardDraw::CardSizeY-2,
m_invert ? NOTSRCCOPY : SRCCOPY );
Polyline( hdcDraw, border, sizeof(border)/sizeof(POINT) );
}

RestoreDC( hdcDraw, savedDC );

SelectObject( drawDc, drawDcMem );
DeleteDC( drawDc );

return S_OK;
}

//=--------------------------------------------------------------------------=
// CCardControl::WindowProc
//=--------------------------------------------------------------------------=
// window procedure for this control. nothing terribly exciting.
//
// Parameters:
// see win32sdk on window procs.
//
// Notes:
//
LRESULT CCardControl::WindowProc
(
UINT msg,
WPARAM wParam,
LPARAM lParam
)
{
POINTS pt;
RECT rect;
int b, s;
int ev = -1;

switch( msg )
{
case WM_MOUSEMOVE:
ev = MouseMove;
// FALL THROUGH!
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
if( ev == -1 ) ev = MouseDown;
// FALL THROUGH!
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
if( ev == -1 ) ev = MouseUp;
pt = MAKEPOINTS( lParam );
OcxGetWindowRect( &rect );
b = (wParam & 3) == 3 ? 4 : wParam & 3;
s = (wParam >> 2) & 7;
FireEvent( &rgEvents[ev], b, s, pt.x-rect.left, pt.y-rect.top );
if( msg == WM_LBUTTONUP )
FireEvent( &rgEvents[Click] );
break;
case WM_LBUTTONDBLCLK:
FireEvent( &rgEvents[DblClick] );
break;
}

return OcxDefWindowProc(msg, wParam, lParam);
}

//=--------------------------------------------------------------------------=
// CCardControl::AboutBox
//=--------------------------------------------------------------------------=
// prints up an about box. fweeeee.
//
// Notes:
//
void CCardControl::AboutBox
(
void
)
{
// TODO: Ideally, one would use DialogBox, and some sort of Dialog Box here if
// they wanted a slightly more interesting About Box ... you should
// still call ModalDialog first, however.
//
ModalDialog(TRUE);
MessageBox(NULL, "Microsoft ActiveX Card Control\n\n"
"Copyright 1996 - 1998 Microsoft Corp.",
"About Card",
MB_OK | MB_TASKMODAL);
ModalDialog(FALSE);
}

STDMETHODIMP CCardControl::get_Number(enumCardNumber * number)
{
CHECK_POINTER(number);

*number = m_number;

return( S_OK );
}

STDMETHODIMP CCardControl::put_Number(enumCardNumber number)
{
if( m_number == number )
return S_OK;

m_number = number;
ValidateNumber();
if( m_suite >= 1 )
{
if( Windowless() )
{
RECT rect;
OcxGetWindowRect( &rect );
OcxInvalidateRect( &rect, TRUE );
}
else
InvalidateControl( NULL );
}

// update anybody who cares about property changes and mark ourselves
// as dirty
//
PropertyChanged(DISPID_NUMBER);
m_fDirty = TRUE;

return S_OK;
}

STDMETHODIMP CCardControl::get_Invert( VARIANT_BOOL *invert )
{
CHECK_POINTER(invert);

*invert = m_invert;

return( S_OK );
}

STDMETHODIMP CCardControl::put_Invert( VARIANT_BOOL invert )
{
if( m_invert == invert )
return S_OK;

m_invert = invert;

{
if( Windowless() )
{
RECT rect;
OcxGetWindowRect( &rect );
OcxInvalidateRect( &rect, TRUE );
}
else
InvalidateControl( NULL );
}

// update anybody who cares about property changes and mark ourselves
// as dirty
//
PropertyChanged(DISPID_INVERT);
m_fDirty = TRUE;

return S_OK;
}

STDMETHODIMP CCardControl::get_CardAlignment( enumCardAlignment *cardalignment )
{
CHECK_POINTER(cardalignment);

*cardalignment = m_cardalignment;

return( S_OK );
}

STDMETHODIMP CCardControl::put_CardAlignment( enumCardAlignment cardalignment )
{
if( m_cardalignment == cardalignment )
return S_OK;

m_cardalignment = cardalignment;
ValidateCardAlignment();

{
if( Windowless() )
{
RECT rect;
OcxGetWindowRect( &rect );
OcxInvalidateRect( &rect, TRUE );
}
else
InvalidateControl( NULL );
}

// update anybody who cares about property changes and mark ourselves
// as dirty
//
PropertyChanged(DISPID_CARDALIGNMENT);
m_fDirty = TRUE;

return S_OK;
}

STDMETHODIMP CCardControl::get_Suite(enumCardSuite * suite)
{
CHECK_POINTER(suite);

*suite = m_suite;

return( S_OK );
}

STDMETHODIMP CCardControl::put_Suite(enumCardSuite suite)
{
if( m_suite == suite )
return S_OK;

m_suite = suite;
ValidateSuite();

{
if( Windowless() )
{
RECT rect;
OcxGetWindowRect( &rect );
OcxInvalidateRect( &rect, TRUE );
}
else
InvalidateControl( NULL );
}

// update anybody who cares about property changes and mark ourselves
// as dirty
//
PropertyChanged(DISPID_SUITE);
m_fDirty = TRUE;

return S_OK;
}

//=--------------------------------------------------------------------------=
// CCardControl::Create
//=--------------------------------------------------------------------------=
// global static function that creates an instance of the control an returns
// an IUnknown pointer for it.
//
// Parameters:
// IUnknown * - [in] controlling unknown for aggregation
//
// Output:
// IUnknown * - new object.
//
// Notes:
//
IUnknown *CCardControl::Create
(
IUnknown *pUnkOuter
)
{
// make sure we return the private unknown so that we support aggegation
// correctly!
//
CCardControl *pNew = new CCardControl(pUnkOuter);
return pNew->PrivateUnknown();
}