// BullsEye.cpp : Implementacja CBullsEye

#include "stdafx.h"
#include "BullsEyeCtl.h"
#include "BullsEye.h"
#include "ocidl.h"	// Dodane przez ClassView
#include "AboutDlg.h"

/////////////////////////////////////////////////////////////////////////////
// Zmienne statycznego elementu składowego klasy CBullsEyeLicense

// {956FC2C2-5AF8-11d2-BD05-00A0C9C8E50D}
const GUID CBullsEyeLicense::RunTimeLicenseKey = 
{ 0x956fc2c2, 0x5af8, 0x11d2, { 0xbd, 0x5, 0x0, 0xa0, 0xc9, 0xc8, 0xe5, 0xd } };

HRESULT SetStrings(/*[in*/ ULONG cElems, /*[in]*/ const LPCOLESTR* rgpsz, /*[out]*/ CALPOLESTR* pStrings);
HRESULT SetCookies(/*[in*/ ULONG cElems, /*[in*/ const DWORD* pdwCookies, /*[out]*/ CADWORD* pcaCookies);

/////////////////////////////////////////////////////////////////////////////
// Tworzenie, inicjalizacja i zniszczenie klasy CBullsEye

CLIPFORMAT CBullsEye::m_cfEmbeddedObject;
CLIPFORMAT CBullsEye::m_cfObjectDescriptor;

/***************/
/* Konstruktor */
/***************/

CBullsEye::CBullsEye()
{
    // Właściwości
    m_nBackStyle        = 0;        // Nieprzezroczyste = 0, Przezroczyste = 1

    m_nBorderStyle      = 0;        // Brak ramki = 0, Pojedyncza szerokosc = 1
    m_nBorderWidth      = 1;
    m_clrBackColor      = RGB (255,255,255);
    m_clrForeColor      = RGB (  0,  0,  0);
    m_bEnabled          = TRUE;

    // Własne właściwości
    m_clrAlternateColor = RGB (  0,  0,  0);
    m_clrCenterColor    = RGB (255,  0,  0);
    m_beep              = VARIANT_TRUE;

    // Stan elementu sterującego
    m_alternateBrush    = NULL;
    m_backBrush         = NULL;
    m_centerBrush       = NULL;
    m_borderPen         = NULL;
    m_nAppearance       = 0;

    m_bAmbientsFetched  = false;
}

/******************/
/* FinalConstruct */
/******************/

HRESULT CBullsEye::FinalConstruct ()
{
    FreezeEvents (TRUE);
    put_RingCount (5);
    FreezeEvents (FALSE);
    return S_OK;
}

/**************/
/* Destruktor */
/**************/

CBullsEye::~CBullsEye()
{
    if (m_alternateBrush) ::DeleteObject (m_alternateBrush);
    if (m_backBrush)      ::DeleteObject (m_backBrush);
    if (m_centerBrush)    ::DeleteObject (m_centerBrush);
    if (m_borderPen)      ::DeleteObject (m_borderPen);
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody IPerPropertyBrowsing

/********************/
/* GetDisplayString */
/********************/

STDMETHODIMP CBullsEye::GetDisplayString(DISPID dispid,BSTR *pBstr)
{
	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::GetDisplayString\n"));
    switch (dispid) {
    case DISPID_BEEP:
        if (VARIANT_TRUE == m_beep)
            *pBstr = SysAllocString (OLESTR("Tak"));
        else
            *pBstr = SysAllocString (OLESTR("Nie"));

        return *pBstr ? S_OK : E_OUTOFMEMORY;

    case DISPID_BACKSTYLE:
        if (1 == m_nBackStyle)
            *pBstr = SysAllocString (OLESTR("Nieprzezroczyste"));
        else
            *pBstr = SysAllocString (OLESTR("Przezroczyste"));

        return *pBstr ? S_OK : E_OUTOFMEMORY;

    case DISPID_ALTERNATECOLOR: // Visual Basic stosuje domyślne formatowanie
    case DISPID_BACKCOLOR:      //   dla tych właściwości kolory
    case DISPID_CENTERCOLOR:    // W przeciwnym wypadku wartości kolorów byłyby wyświetlane dziesiętnie
    case DISPID_FORECOLOR:      //   co powodowałoby problemy z próbą kolorów.
    case DISPID_RINGCOUNT:
        return S_FALSE;         // Nieudokumentowana wartość zwrotna, która jednak działa poprawnie...
    }

    return IPerPropertyBrowsingImpl<CBullsEye>::GetDisplayString(dispid, pBstr);
}

/*********************/
/* MapPropertyToPage */
/*********************/

STDMETHODIMP CBullsEye::MapPropertyToPage(DISPID dispid, CLSID *pClsid)
{
  ATLTRACE2(atlTraceControls,2,_T("CBullsEye::MapPropertyToPage\n"));
  // Wyszukanie stronicy właściwości CLSID w PROP MAP
  HRESULT hr = IPerPropertyBrowsingImpl<CBullsEye>::MapPropertyToPage(dispid, pClsid);
  // Jeśli nie ma stronicy właściwości dla podanej właściwości, należy zgłosić niepowodzenie wywołania
  if (SUCCEEDED(hr) && CLSID_NULL == *pClsid) hr = E_INVALIDARG;
  return hr;
}

/************************/
/* GetPredefinedStrings */
/************************/

#define DIM(a) (sizeof(a)/sizeof(a[0]))

static const LPCOLESTR    rszBeepStrings [] = { OLESTR("Tak, niech gra"), OLESTR("Nie, siedź cicho") };
static const DWORD        rdwBeepCookies [] = { 0, 1 };
static const VARIANT_BOOL rvbBeepValues  [] = { VARIANT_TRUE, VARIANT_FALSE };

static const UINT cBeepStrings = DIM(rszBeepStrings);
static const UINT cBeepCookies = DIM(rdwBeepCookies);
static const UINT cBeepValues  = DIM(rvbBeepValues);

static const LPCOLESTR    rszBackStyleStrings [] = { OLESTR("Nieprzezroczyste"), OLESTR("Przezroczyste") };
static const DWORD        rdwBackStyleCookies [] = { 0, 1 };
static const long         rvbBackStyleValues  [] = { 1, 0 };

static const UINT cBackStyleStrings = DIM(rszBackStyleStrings);
static const UINT cBackStyleCookies = DIM(rdwBackStyleCookies);
static const UINT cBackStyleValues  = DIM(rvbBackStyleValues);

STDMETHODIMP CBullsEye::GetPredefinedStrings(/*[in]*/ DISPID dispid, /*[out]*/ CALPOLESTR *pcaStringsOut, /*[out]*/ CADWORD *pcaCookiesOut)
{
	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::GetPredefinedStrings\n"));
	if (NULL == pcaStringsOut || NULL == pcaCookiesOut) return E_POINTER;


    ATLASSERT (cBeepStrings == cBeepCookies);
    ATLASSERT (cBeepStrings == cBeepValues);

    ATLASSERT (cBackStyleStrings == cBackStyleCookies);
    ATLASSERT (cBackStyleStrings == cBackStyleValues);

	pcaStringsOut->cElems = 0;
	pcaStringsOut->pElems = NULL;
	pcaCookiesOut->cElems = 0;
	pcaCookiesOut->pElems = NULL;

    HRESULT hr = S_OK;
    switch (dispid) {
    case DISPID_BEEP:
        hr = SetStrings (cBeepValues, rszBeepStrings, pcaStringsOut);
        if (FAILED (hr)) return hr;
        return SetCookies (cBeepValues, rdwBeepCookies, pcaCookiesOut);

    case DISPID_BACKSTYLE:
        hr = SetStrings (cBackStyleValues, rszBackStyleStrings, pcaStringsOut);
        if (FAILED (hr)) return hr;
        return SetCookies (cBackStyleValues, rdwBackStyleCookies, pcaCookiesOut);
    }
    return IPerPropertyBrowsingImpl<CBullsEye>::GetPredefinedStrings(dispid, pcaStringsOut, pcaCookiesOut);
}

/**********************/
/* GetPredefinedValue */
/**********************/

STDMETHODIMP CBullsEye::GetPredefinedValue(DISPID dispid, DWORD dwCookie, VARIANT* pVarOut)
{
    if (NULL == pVarOut) return E_POINTER;

    ULONG i;
    switch (dispid) {
    case DISPID_BEEP:
        // Wyszukanie odpowiedniej wartości w tablicy Cookie
        for (i = 0; i < cBeepCookies; i++) {
            if (rdwBeepCookies[i] == dwCookie) {
                pVarOut->vt = VT_BOOL;
                pVarOut->boolVal = rvbBeepValues [i];
                return S_OK;
            }
        }
        return E_INVALIDARG;

    case DISPID_BACKSTYLE:
        // Wyszukanie odpowiedniej wartości w tablicy Cookie
        for (i = 0; i < cBackStyleCookies; i++) {
            if (rdwBackStyleCookies[i] == dwCookie) {
                pVarOut->vt = VT_I4;
                pVarOut->lVal = rvbBackStyleValues [i];
                return S_OK;
            }
        }
        return E_INVALIDARG;
    }

    return IPerPropertyBrowsingImpl<CBullsEye>::GetPredefinedValue(dispid, dwCookie, pVarOut);
}


/////////////////////////////////////////////////////////////////////////////
// Implementacja interfejsu ICategorizeProperties

/*************************/
/* MapPropertyToCategory */
/*************************/

STDMETHODIMP CBullsEye::MapPropertyToCategory(/*[in]*/ DISPID dispid, /*[out]*/ PROPCAT* ppropcat)
{
    if (NULL == ppropcat) return E_POINTER;

    switch (dispid) {
    case DISPID_FORECOLOR:
    case DISPID_BACKCOLOR:
    case DISPID_CENTERCOLOR:
    case DISPID_ALTERNATECOLOR:
    case DISPID_RINGCOUNT:
    case DISPID_BACKSTYLE:
        *ppropcat = PROPCAT_Appearance;
        return S_OK;

    case DISPID_BEEP:
    case DISPID_ENABLED:
        *ppropcat = PROPCAT_Behavior;
        return S_OK;

    case DISPID_RINGVALUE:
        *ppropcat = PROPCAT_Scoring;
        return S_OK;

    default:
        return E_FAIL;
    }
}

/*******************/
/* GetCategoryName */
/*******************/

STDMETHODIMP CBullsEye::GetCategoryName(/*[in]*/ PROPCAT propcat, /*[in]*/ LCID lcid, /*[out]*/ BSTR* pbstrName)
{
    if(PROPCAT_Scoring == propcat) {
        *pbstrName = ::SysAllocString(L"Wyniki"); 
        return S_OK;
    }
    return E_FAIL;
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody IViewObjectExImpl

/*****************/
/* GetViewStatus */
/*****************/

STDMETHODIMP CBullsEye::GetViewStatus(DWORD* pdwStatus)
{
	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::GetViewStatus\n"));
    *pdwStatus = m_nBackStyle == 1 ?  // Nieprzezroczyste?
        VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE :
        VIEWSTATUS_DVASPECTTRANSPARENT ;
	return S_OK;
}

/*****************/
/* QueryHitPoint */
/*****************/

STDMETHODIMP CBullsEye::QueryHitPoint(DWORD dwAspect, LPCRECT pRectBounds, POINT ptlLoc, LONG /* lCloseHint */, DWORD *pHitResult)
{
    ATLTRACE2(atlTraceControls,2,_T("CBullsEye::QueryHitPoint\n"));
    switch (dwAspect) {
    case DVASPECT_CONTENT:
    case DVASPECT_OPAQUE:
    case DVASPECT_TRANSPARENT:
        // Obliczenie punktu relatywnego do początkowego elementu sterującego
        CPoint   pt (ptlLoc.x - pRectBounds->left, ptlLoc.y - pRectBounds->top);
        if (-1 != HitTest (pt))
		    *pHitResult = HITRESULT_HIT;
        else
		    *pHitResult = HITRESULT_OUTSIDE;

		return S_OK;
    }
    ATLTRACE2(atlTraceControls,2,_T("Błędny DVASPECT\n"));
    return E_FAIL;
}

/****************/
/* QueryHitRect */
/****************/

STDMETHODIMP CBullsEye::QueryHitRect(DWORD dwAspect, LPCRECT pRectBounds, LPCRECT prcLoc, LONG /* lCloseHint */, DWORD* pHitResult)
{
	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::QueryHitRect\n"));
    switch (dwAspect) {
    case DVASPECT_CONTENT:
    case DVASPECT_OPAQUE:
    case DVASPECT_TRANSPARENT:
        RECT rc;
		*pHitResult = UnionRect(&rc, pRectBounds, prcLoc) ? HITRESULT_HIT : HITRESULT_OUTSIDE;
		return S_OK;
	}
	ATLTRACE2(atlTraceControls,2,_T("Błędny DVASPECT\n"));
	return E_FAIL;
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody IDataObject

/****************/
/* QueryGetData */
/****************/

STDMETHODIMP CBullsEye::QueryGetData(FORMATETC* pformatetc)
{
	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::QueryGetData\n"));

    if (NULL == pformatetc) return E_POINTER;

    if (pformatetc->lindex != -1) return DV_E_LINDEX;

    if ((pformatetc->tymed & TYMED_MFPICT) == 0) return DV_E_TYMED ;

    if (pformatetc->dwAspect != DVASPECT_CONTENT) return DV_E_DVASPECT;

    return S_OK;
}

/*****************/
/* EnumFormatEtc */
/*****************/

STDMETHODIMP CBullsEye::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
{
    static FORMATETC array[] = { 
        { CF_METAFILEPICT, NULL, DVASPECT_CONTENT, -1, TYMED_MFPICT }, };

	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::EnumFormatEtc\n"));

    if (DATADIR_SET == dwDirection) return E_NOTIMPL;

    typedef CComObject <CComEnum <IEnumFORMATETC, &IID_IEnumFORMATETC, FORMATETC, _Copy<FORMATETC> > > EnumFORMATETC;

    EnumFORMATETC* penum = new EnumFORMATETC ();

    HRESULT hr = penum->Init (&array [0], &array [1], NULL, AtlFlagNoCopy);
    if (FAILED (hr)) return hr;

    penum->AddRef ();
    hr = penum->QueryInterface (ppenumFormatEtc);
    penum->Release ();
    return hr;
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody IOleControl

/******************/
/* GetControlInfo */
/******************/

STDMETHODIMP CBullsEye::GetControlInfo(CONTROLINFO *pci)
{
    if(!pci) return E_POINTER;
    pci->hAccel   = NULL;
    pci->cAccel   = 0;
    pci->dwFlags  = 0;     
    return S_OK;
}

/***************************/
/* OnAmbientPropertyChange */
/***************************/

STDMETHODIMP CBullsEye::OnAmbientPropertyChange (DISPID dispid)
{
    ATLTRACE2(atlTraceControls,2,_T("CBullsEye::OnAmbientPropertyChange\n"));
    ATLTRACE2(atlTraceControls,2,_T(" -- DISPID = %d (%d)\n"), dispid);

    bool bViewChange = false;
    if (DISPID_AMBIENT_APPEARANCE == dispid || DISPID_UNKNOWN == dispid) {
        HRESULT hr = GetAmbientAppearance (m_nAppearance);
        bViewChange = true;
    }

    if (DISPID_AMBIENT_BACKCOLOR == dispid || DISPID_UNKNOWN == dispid) {
        HRESULT hr = GetAmbientBackColor (m_clrBackColor);
        if (SUCCEEDED (hr)) {
            ::DeleteObject (m_backBrush);
            m_backBrush = NULL;
            bViewChange = true;
        }
    }

    if (DISPID_AMBIENT_FORECOLOR == dispid || DISPID_UNKNOWN == dispid) {
        HRESULT hr = GetAmbientForeColor (m_clrForeColor);
        if (SUCCEEDED (hr)) {
            bViewChange = true;
        }
    }

    if (bViewChange) FireViewChange(); // Żądanie odświeżenia
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody IOleInPlaceActiveObject

/************************/
/* TranslateAccelerator */
/************************/

typedef enum tagKEYMODIFIERS {
    KEYMOD_SHIFT      = 0x00000001, 
    KEYMOD_CONTROL    = 0x00000002,
    KEYMOD_ALT        = 0x00000004 
} KEYMODIFIERS; 

STDMETHODIMP CBullsEye::TranslateAccelerator(MSG *pMsg)
{
    if (((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) &&
        ((VK_TAB == pMsg->wParam) || (VK_RETURN == pMsg->wParam))) {
        
        CComQIPtr<IOleControlSite, &IID_IOleControlSite> spCtrlSite(m_spClientSite);
        if (spCtrlSite) {
            DWORD km = 0;
            km |= GetKeyState (VK_SHIFT)   < 0 ? KEYMOD_SHIFT   : 0;
            km |= GetKeyState (VK_CONTROL) < 0 ? KEYMOD_CONTROL : 0;
            km |= GetKeyState (VK_MENU)    < 0 ? KEYMOD_ALT     : 0;

            return spCtrlSite->TranslateAccelerator (pMsg, km);
        }
    }
    return S_FALSE;  
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody IOleControl

STDMETHODIMP CBullsEye::GetMiscStatus (DWORD dwAspect, DWORD *pdwStatus)
{
    if (NULL == pdwStatus) return E_POINTER;

    *pdwStatus = 
           OLEMISC_SETCLIENTSITEFIRST |
           OLEMISC_ACTIVATEWHENVISIBLE |
           OLEMISC_INSIDEOUT |
           OLEMISC_CANTLINKINSIDE |
           OLEMISC_RECOMPOSEONRESIZE |
           OLEMISC_NOUIACTIVATE ;

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Zastąpienie metody CComControlBase

/********************************/
/* IQuickActivate_QuickActivate */
/********************************/

HRESULT CBullsEye::IQuickActivate_QuickActivate(QACONTAINER *pQACont, QACONTROL *pQACtrl)
{
    m_clrForeColor = pQACont->colorFore;
    m_clrBackColor = pQACont->colorBack;
    m_nAppearance  = (short) pQACont->dwAppearance;
    m_bAmbientsFetched = true;

    HRESULT hr = CComControlBase::IQuickActivate_QuickActivate(pQACont, pQACtrl);
    return hr;
}

/****************************/
/* IOleObject_SetClientSite */
/****************************/

HRESULT CBullsEye::IOleObject_SetClientSite(IOleClientSite *pClientSite)
{
    HRESULT hr = CComControlBase::IOleObject_SetClientSite(pClientSite);
    
    if (!m_bAmbientsFetched) {
        HRESULT hr = GetAmbientBackColor (m_clrBackColor);
                hr = GetAmbientForeColor (m_clrForeColor);
                hr = GetAmbientAppearance (m_nAppearance);
    }
    return hr;
}

/***************************/
/* IPersistStreamInit_Save */
/***************************/

HRESULT CBullsEye::IPersistStreamInit_Save(LPSTREAM pStm, BOOL fClearDirty, ATL_PROPMAP_ENTRY* pMap)
{
	if (NULL == pStm) return E_POINTER;

    // Zapisanie właściwości opisanych w PROP_MAP
	HRESULT hr = IPersistStreamInitImpl<CBullsEye>::IPersistStreamInit_Save(pStm,  fClearDirty, pMap);
    if (FAILED (hr)) return hr;

    // Zapisanie zindeksowanej właściwości - RingValues

    // Uzyskanie liczby pierścieni
    short sRingCount;
    get_RingCount (&sRingCount);

    // Zapisanie wartości dla każdego pierścienia
    for (short nIndex = 1; nIndex <= sRingCount; nIndex++) {

        // Uzyskanie wartości pierścienia
        long lRingValue ;
        get_RingValue (nIndex, &lRingValue);

        // Zapisanie jej do strumienia
        ULONG cbWritten;
        hr = pStm->Write(&lRingValue, sizeof(lRingValue), &cbWritten);
        ATLASSERT (SUCCEEDED (hr));
        ATLASSERT (sizeof(lRingValue) == cbWritten);
        if (FAILED (hr) || cbWritten != sizeof(lRingValue)) {
            hr = E_UNEXPECTED;
            break;
        }
	}
    return hr;
}

/***************************/
/* IPersistStreamInit_Load */
/***************************/

HRESULT CBullsEye::IPersistStreamInit_Load(LPSTREAM pStm, ATL_PROPMAP_ENTRY* pMap)
{
    if (NULL == pStm) return E_POINTER;

    // Załadowanie właściwości opisanych w PROP_MAP
	HRESULT hr = IPersistStreamInitImpl<CBullsEye>::IPersistStreamInit_Load(pStm, pMap);
    if (FAILED (hr)) return hr;

    // Załadowanie zindeksowanej wartości - RingValues

    // Uzyskanie liczby pierścieni
    short sRingCount;
    get_RingCount (&sRingCount);

    // Odczytanie wartości każdego pierścienia
    for (short nIndex = 1; nIndex <= sRingCount; nIndex++) {

        // Odczytanie wartości pierścienia ze strumienia
        long lRingValue ;
        ULONG cbRead;
        hr = pStm->Read(&lRingValue, sizeof(lRingValue), &cbRead);
        ATLASSERT (SUCCEEDED (hr));
        ATLASSERT (sizeof(lRingValue) == cbRead);
        if (FAILED (hr) || cbRead != sizeof(lRingValue)) {
            hr = E_UNEXPECTED;
            break;
        }

        // Ustawienie wartości pierścienia
        put_RingValue (nIndex, lRingValue);

	}

    if (SUCCEEDED(hr)) m_bRequiresSave = TRUE;
    return hr;
}

/****************************/
/* IPersistPropertyBag_Save */
/****************************/

HRESULT CBullsEye::IPersistPropertyBag_Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties, ATL_PROPMAP_ENTRY* pMap)
{
	if (NULL == pPropBag) return E_POINTER;

    // Zapisanie właściwości opisanych w PROP_MAP
	HRESULT hr = IPersistPropertyBagImpl<CBullsEye>::IPersistPropertyBag_Save(pPropBag, fClearDirty, fSaveAllProperties, pMap);
    if (FAILED (hr)) return hr;

    // Zapisanie zindeksowanej wartości - RingValues

    // Uzyskanie liczby pierścieni
    short sRingCount;
    get_RingCount (&sRingCount);

    // Zapisanie wartości każdego pierścienia
    for (short nIndex = 1; nIndex <= sRingCount; nIndex++) {

        // Utworzenie nazwy właściwości
        CComBSTR bstrName = OLESTR("RingValue");
        CComVariant vRingNumber = nIndex;
        hr = vRingNumber.ChangeType (VT_BSTR);
        ATLASSERT (SUCCEEDED (hr));
        bstrName += vRingNumber.bstrVal;

        // Uzyskanie wartości pierścienia
        long lRingValue ;
        get_RingValue (nIndex, &lRingValue);

        // Zapisanie jej do strumienia
        CComVariant vValue = lRingValue;
        hr = pPropBag->Write(bstrName, &vValue);
        ATLASSERT (SUCCEEDED (hr));
        if (FAILED (hr)) {
            hr = E_UNEXPECTED;
            break;
        }
	}
    return hr;
}

/****************************/
/* IPersistPropertyBag_Load */
/****************************/

HRESULT CBullsEye::IPersistPropertyBag_Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, ATL_PROPMAP_ENTRY* pMap)
{
    if (NULL == pPropBag) return E_POINTER;

    // Załadowanie właściwości opisanych w PROP_MAP
	HRESULT hr = IPersistPropertyBagImpl<CBullsEye>::IPersistPropertyBag_Load(pPropBag, pErrorLog, pMap);

    // Obejście błędu ATL 3.0 w AtlIPersistPropertyBag_Load
    // HRESULT hr = FixedAtlIPersistPropertyBag_Load(pPropBag, pErrorLog, pMap, this, GetUnknown());
	if (SUCCEEDED(hr))
		m_bRequiresSave = FALSE;

    if (FAILED (hr)) return hr;

    // Załadowanie zindeksowanej wartości - RingValues

    // Uzyskanie liczby pierścieni
    short sRingCount;
    get_RingCount (&sRingCount);

    // Odczytanie wartości każdego pierścienia
    for (short nIndex = 1; nIndex <= sRingCount; nIndex++) {

        // Ustawienie nazwy właściwości bazowej
        CComBSTR bstrName = OLESTR("RingValue");

        // Utworzenie numeru pierścienia jako ciągu
        CComVariant vRingNumber = nIndex;
        hr = vRingNumber.ChangeType (VT_BSTR);
        ATLASSERT (SUCCEEDED (hr));

        // Połączenie dwóch ciągów w celu utworzenia nazwy właściwości
        bstrName += vRingNumber.bstrVal;

        // Odczytanie wartości pierścienia z właściwości
        CComVariant vValue = 0L;
        hr = pPropBag->Read(bstrName, &vValue, pErrorLog);
        ATLASSERT (SUCCEEDED (hr));
        ATLASSERT (VT_I4 == vValue.vt);

        if (FAILED (hr)) {
            hr = E_UNEXPECTED;
            break;
        }

        // Ustawienie wartości pierścienia
        put_RingValue (nIndex, vValue.lVal);
	}

    if (SUCCEEDED(hr)) m_bRequiresSave = TRUE;
    return hr;
}

/***************************************************/
/* IPersistPropertyBag/IPersistStreamInit::InitNew */
/***************************************************/


HRESULT CBullsEye::InitNew()
{
    m_bRequiresSave = TRUE;
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Implementacja interfejsu IBullsEye - właściwości elementów sterujących i metody

/////////////////////////////////////////////////////////////////////////////
// Właściwości elementów sterujących

/**********************/
/* get_AlternateColor */
/**********************/

STDMETHODIMP CBullsEye::get_AlternateColor(OLE_COLOR *pVal)
{
    if (NULL == pVal) return E_POINTER;

	*pVal = m_clrAlternateColor;

	return S_OK;
}

/**********************/
/* put_AlternateColor */
/**********************/

STDMETHODIMP CBullsEye::put_AlternateColor(OLE_COLOR newVal)
{
    if (m_clrAlternateColor == newVal) return S_OK;

    if (!m_nFreezeEvents)
        if (FireOnRequestEdit(DISPID_ALTERNATECOLOR) == S_FALSE)
            return S_FALSE;

	m_clrAlternateColor = newVal;               // Zapisanie nowego koloru
    ::DeleteObject (m_alternateBrush);          // Usunięcie starego koloru pędzla
    m_alternateBrush = NULL;

    m_bRequiresSave = TRUE;                     // Ustawienie znacznika Dirty
    if (!m_nFreezeEvents)
        FireOnChanged(DISPID_ALTERNATECOLOR);   // Powiadomienie kontenera o zmianie
    FireViewChange();                           // Żądanie odświeżenia
    SendOnDataChange(NULL);                     // Powiadomienie ujścia o zmianie
	return S_OK;
}

/*******************/
/* get_Application */
/*******************/

STDMETHODIMP CBullsEye::get_Application(LPDISPATCH *ppDisp)
{
    if (!ppDisp) return E_POINTER;

    // Najpierw próba bezpośredniego uzyskania obiektu Application
    HRESULT hr = GetApplicationFromServiceProvider (ppDisp);
    if (SUCCEEDED (hr)) return hr;

    static OLECHAR szApplication [] = OLESTR("Aplikacja");

    // Poproszenie obiektu Document (nasz kontener) o jego właściwość Application
    return GetPropertyFromDocument (szApplication, ppDisp);
}

/*****************/
/* put_BackColor */
/*****************/

STDMETHODIMP CBullsEye::put_BackColor(OLE_COLOR newVal)
{
    if (m_clrBackColor == newVal)
        return S_OK;

    if (!m_nFreezeEvents)
        if (FireOnRequestEdit(DISPID_BACKCOLOR) == S_FALSE)
            return S_FALSE;

	m_clrBackColor = newVal;                // Zapisanie nowego koloru
    ::DeleteObject (m_backBrush);           // Usunięcie starego koloru pędzla
    m_backBrush = NULL;

    m_bRequiresSave = TRUE;                 // Ustawienie znacznika Dirty
    if (!m_nFreezeEvents)
        FireOnChanged(DISPID_BACKCOLOR);    // Powiadomienie kontenera o zmianie
    FireViewChange();                       // Żądanie odświeżenia
    SendOnDataChange(NULL);                 // Powiadomienie ujścia o zmianie

	return S_OK;
}

/*****************/
/* put_BackStyle */
/*****************/

STDMETHODIMP CBullsEye::put_BackStyle(LONG nBackStyle)
{
	ATLTRACE2(atlTraceControls,2,_T("CBullsEye::put_BackStyle\n"));

    if (m_nBackStyle == nBackStyle) return S_OK;

    if (FireOnRequestEdit(DISPID_BACKSTYLE) == S_FALSE)
		return S_FALSE;

	m_nBackStyle = nBackStyle;
	m_bRequiresSave = TRUE;

    // Powiadomienie kontenera o zmianie właściwości
	FireOnChanged(DISPID_BACKSTYLE);

    // Właściwość BackStyle zmienia wartość VIEWSTATUS
    // Powiadomienie kontenera o zmianie viewstatus
    DWORD dwViewStatus;
    GetViewStatus (&dwViewStatus);
    SendOnViewExChange (dwViewStatus);

    // Powiadomienie kontenera o zmianie widoku
	FireViewChange();

    // Powiadomienie kontenera o zmianie stanu
	SendOnDataChange(NULL);
	return S_OK;
}

/************/
/* get_Beep */
/************/

STDMETHODIMP CBullsEye::get_Beep(VARIANT_BOOL *pVal)
{
    if (NULL == pVal) return E_POINTER;

	*pVal = m_beep;

	return S_OK;
}

/************/
/* put_Beep */
/************/

STDMETHODIMP CBullsEye::put_Beep(VARIANT_BOOL newVal)
{
    if (m_beep == newVal) return S_OK;

    if (!m_nFreezeEvents)
        if (FireOnRequestEdit(DISPID_BEEP) == S_FALSE)
            return S_FALSE;

    m_beep = newVal;

    m_bRequiresSave = TRUE;         // Ustawienie znacznika Dirty
    if (!m_nFreezeEvents)
        FireOnChanged(DISPID_BEEP); // Powiadomienie kontenera o zmianie
    SendOnDataChange(NULL);         // Powiadomienie ujścia o zmianie

	return S_OK;
}

/*******************/
/* get_CenterColor */
/*******************/

STDMETHODIMP CBullsEye::get_CenterColor(OLE_COLOR *pVal)
{
    if (NULL == pVal) return E_POINTER;

    *pVal = m_clrCenterColor;

	return S_OK;
}

/*******************/
/* put_CenterColor */
/*******************/

STDMETHODIMP CBullsEye::put_CenterColor(OLE_COLOR newVal)
{
    if (m_clrCenterColor == newVal)
        return S_OK;

    if (!m_nFreezeEvents)
        if (FireOnRequestEdit(DISPID_CENTERCOLOR) == S_FALSE)
            return S_FALSE;

	m_clrCenterColor = newVal;              // Zapisanie nowego koloru
    ::DeleteObject (m_centerBrush);         // Usunięcie starego koloru pędzla
    m_centerBrush = NULL;

    m_bRequiresSave = TRUE;                 // Ustawienie znacznika Dirty
    if (!m_nFreezeEvents)
        FireOnChanged(DISPID_CENTERCOLOR);  // Powiadomienie kontenera o zmianie
    FireViewChange();                       // Żądanie odświeżenia
    SendOnDataChange(NULL);                 // Powiadomienie ujścia o zmianie

	return S_OK;
}

/**************/
/* get_Parent */
/**************/

STDMETHODIMP CBullsEye::get_Parent(LPDISPATCH *ppDisp)
{
    if (NULL == ppDisp) return E_POINTER;

    *ppDisp = NULL;

	CComPtr<IOleClientSite> pClientSite;

    HRESULT hr = GetClientSite (&pClientSite);
    ATLASSERT (SUCCEEDED (hr));
    if (FAILED (hr)) return hr;    // Naprawdę potrzebny jest klient!!!

    // Uzyskanie obiektu dokumentu
    CComPtr<IOleContainer> pOleContainer;
    hr = pClientSite->GetContainer (&pOleContainer);
    if (FAILED (hr)) return hr;     // GetContainer to metoda opcjonalna...

    // Uzyskanie interfejsu automatyzacji dla obiektu dokumentu
    return pOleContainer->QueryInterface (ppDisp);
}

/*****************/
/* get_RingCount */
/*****************/

STDMETHODIMP CBullsEye::get_RingCount(short *pVal)
{
    if (NULL == pVal) return E_POINTER;

	*pVal = m_ringValues.GetSize ();
	return S_OK;
}

/*****************/
/* put_RingCount */
/*****************/

STDMETHODIMP CBullsEye::put_RingCount(short newVal)
{
    if (newVal <= 0 || newVal > 9) {
		return Error (IDS_INVALIDRINGCOUNT, CLSID_BullsEye, CTL_E_INVALIDPROPERTYARRAYINDEX);
    }

    if (m_ringValues.GetSize () == newVal)  // Tutaj nie można wywołać get_RingCount
        return S_OK;

    if (!m_nFreezeEvents)
        if (FireOnRequestEdit(DISPID_RINGCOUNT) == S_FALSE)
            return S_FALSE;

    m_ringValues.RemoveAll();

    long defValue = 512;
    for (short i = 0; i < newVal; i++) {
        m_ringValues.Add (defValue);
        defValue /= 2;
        if (defValue == 0)              // Minimalna wartość to zawsze 1
            defValue = 1;
    }

    m_bRequiresSave = TRUE;             // Ustawienie znacznika Dirty
    if (!m_nFreezeEvents)
        FireOnChanged(DISPID_RINGCOUNT);// Powiadomienie kontenera o zmianie
    FireViewChange();                   // Żądanie odświeżenia
    SendOnDataChange(NULL);             // Powiadomienie ujścia o zmianie

	return S_OK;
}

/*****************/
/* get_RingValue */
/*****************/

STDMETHODIMP CBullsEye::get_RingValue(/*[in]*/ short sRingNumber, /*[out]*/ long *pVal)
{
    if (NULL == pVal) return E_POINTER;

    if (sRingNumber < 1) {
		return Error (IDS_INVALIDRINGLESSTHANONE, CLSID_BullsEye, CTL_E_INVALIDPROPERTYARRAYINDEX);
    }

    short sRingCount;
    HRESULT hr = get_RingCount (&sRingCount);
    ATLASSERT (SUCCEEDED (hr));

    if (sRingNumber > sRingCount) {
		return Error (IDS_INVALIDRINGNUMBER, CLSID_BullsEye, CTL_E_INVALIDPROPERTYARRAYINDEX);
    }

	*pVal = m_ringValues [sRingNumber - 1];

	return S_OK;
}

/*****************/
/* put_RingValue */
/*****************/

STDMETHODIMP CBullsEye::put_RingValue(/*[in*/ short sRingNumber, /*[in]*/ long newVal)
{
    if (sRingNumber < 1) {
		return Error (IDS_INVALIDRINGLESSTHANONE, CLSID_BullsEye, CTL_E_INVALIDPROPERTYARRAYINDEX);
    }

    short sRingCount;
    HRESULT hr = get_RingCount (&sRingCount);
    ATLASSERT (SUCCEEDED (hr));

    if (sRingNumber > sRingCount) {
		return Error (IDS_INVALIDRINGNUMBER, CLSID_BullsEye, CTL_E_INVALIDPROPERTYARRAYINDEX);
    }

    if (m_ringValues [sRingNumber - 1] == newVal)
        return S_OK;

    if (!m_nFreezeEvents)
        if (FireOnRequestEdit(DISPID_RINGVALUE) == S_FALSE)
            return S_FALSE;

	m_ringValues [sRingNumber - 1] = newVal;

    m_bRequiresSave = TRUE;                 // Ustawienie znacznika Dirty
    if (!m_nFreezeEvents)
        FireOnChanged(DISPID_RINGVALUE);    // Powiadomienie kontenera o zmianie
    SendOnDataChange(NULL);                 // Powiadomienie ujścia o zmianie

	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Metody elementów sterujących

STDMETHODIMP CBullsEye::Refresh()
{
    FireViewChange();
    return S_OK;
}

// DISPID_DOCLICK w celu symulacji kliknięcia myszą
STDMETHODIMP CBullsEye::DoClick()
{
    HitRing (0);
    return S_OK;
}

// DISPID_ABOUTBOX do wyświetlenia informacji
STDMETHODIMP CBullsEye::AboutBox()
{
    CAboutDlg dlg;
    dlg.DoModal();
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Procedury obsługi wiadomości CBullsEye

/* Zastąpienie w celu naprawy błędu w ATL */
/* Bez tej zmiany Print Preview w Accessie nie wyświetli prawidłowo elementu sterującego. */

HRESULT CBullsEye::OnDrawAdvanced(ATL_DRAWINFO& di)
{
	BOOL bDeleteDC = FALSE;
	if (di.hicTargetDev == NULL)
	{
		di.hicTargetDev = AtlCreateTargetDC(di.hdcDraw, di.ptd);
		bDeleteDC = (di.hicTargetDev != di.hdcDraw);
	}
	RECTL rectBoundsDP = *di.prcBounds;
	BOOL bMetafile = GetDeviceCaps(di.hdcDraw, TECHNOLOGY) == DT_METAFILE;
	if (!bMetafile)
	{
		// ::LPtoDP(di.hicTargetDev, (LPPOINT)&rectBoundsDP, 2); wersja ATL
		::LPtoDP(di.hdcDraw, (LPPOINT)&rectBoundsDP, 2);  // wersja Brenta Rectora
		SaveDC(di.hdcDraw);
		SetMapMode(di.hdcDraw, MM_TEXT);
		SetWindowOrgEx(di.hdcDraw, 0, 0, NULL);
		SetViewportOrgEx(di.hdcDraw, 0, 0, NULL);
		di.bOptimize = TRUE; //możemy tak postąpić, ponieważ mamy DC
	}
	di.prcBounds = &rectBoundsDP;
	GetZoomInfo(di);

	HRESULT hRes = OnDraw(di);
	if (bDeleteDC)
		::DeleteDC(di.hicTargetDev);
	if (!bMetafile)
		RestoreDC(di.hdcDraw, -1);
	return hRes;
}


static const int LOGWIDTH = 1000;

/**********/
/* OnDraw */
/**********/

// Uwaga na temat optymalizacji rysowania:
// Wartość TRUE oznacza, że obiekt obsługuje zoptymalizowane renderowanie. Ponieważ
// większość obiektów w tym formularzu korzysta z tej samej czcionki, koloru tła
// i typu ramki, to pozostawienie tych wartości w kontekście urządzenia pozwoli
// kolejnemu obiektowi na ich użycie bez konieczności dokonania ponownego wyboru.
// Obiekt powracający z metody IViewObject::Draw może pozostawić wybraną czcionkę,
// pędzel i pióro. Po zakończeniu procesu rysowania kontener musi usunąć zaznaczenie
// tych wartości. Obiekt może pozostawić w kontekście urządzenia także inne informacje,
// takie jak kolor tła, kolor tekstu, tryb operacji rastrowej, aktualny punkt i linię
// oraz tryb wypełniania wieloboków. Obiekt nie może zmienić wartości stanu, chyba że
// inne obiekty mogą je przywrócić.


HRESULT CBullsEye::OnDraw(ATL_DRAWINFO& di)
{
    CRect rc = *(RECT*)di.prcBounds;
    HDC hdc  = di.hdcDraw;

    // Utworzenie pędzla koloru tła tylko w razie potrzeby
    if (NULL == m_backBrush) {
        OLE_COLOR ocBack;
        HRESULT hr = get_BackColor (&ocBack);      // Uzyskanie koloru tła
        ATLASSERT (SUCCEEDED (hr));

        COLORREF  crBack;                          // Translacja koloru do COLORREF
        hr = ::OleTranslateColor (ocBack, NULL, &crBack);
        ATLASSERT (SUCCEEDED (hr));

        m_backBrush = ::CreateSolidBrush (crBack);  // Utworzenie pędzla tła
        ATLASSERT (NULL != m_backBrush);
    }

    // Wypełnienie kolorem tła tylko w niewłaściwym obszarze, jeśli BackStyle ma wartość Opaque
    if (1 /* Opaque*/ == m_nBackStyle) {
        int s = ::FillRect (hdc, &rc, m_backBrush);
        ATLASSERT (0 != s);
    }

    int nPrevMapMode;
    POINT   ptWOOrig, ptVOOrig;
    SIZE szWEOrig, szVEOrig;

    BOOL bMetafile = GetDeviceCaps(di.hdcDraw, TECHNOLOGY) == DT_METAFILE;
    if (!bMetafile) {
        // OnDrawAdvanced normalizuje kontekst urządzenia
        // Teraz można użyć MM_TEXT, a współrzędne są współrzędnymi urządzenia

        // Utworzenie wygodnego systemu współrzędnych
        nPrevMapMode = ::SetMapMode (hdc, MM_ISOTROPIC);
        ATLASSERT (0 != nPrevMapMode);

        // Mapowanie logicznego 0,0 do fizycznego środka prostokąta
        BOOL bSuccess = ::SetWindowOrgEx (hdc, 0, 0, &ptWOOrig);
        ATLASSERT (0 != bSuccess);
        bSuccess = ::SetViewportOrgEx (hdc, rc.left + rc.Width () / 2,
                                            rc.top  + rc.Height () / 2, &ptVOOrig);
        ATLASSERT (0 != bSuccess);

        // Mapowanie logicznego zakresu (LOGWIDTH, LOGWIDTH) do fizycznego obszaru prostokąta
        bSuccess = ::SetWindowExtEx (hdc, LOGWIDTH, LOGWIDTH, &szWEOrig);
        ATLASSERT (0 != bSuccess);
        bSuccess = ::SetViewportExtEx (hdc, rc.Width (), -rc.Height (), &szVEOrig);
        ATLASSERT (0 != bSuccess);
    }
    else {
        // Odtwarzanie w trybie mapowania ANISOTROPIC

        CRect rcBoundsDP (rc) ;

        // Nie jest możliwe użycie SetViewportOrg i SetViewportExt w metapliku,
        // ponieważ kontener będzie próbował umieścić metaplik.
        // 
        // Odnalezienie współrzędnej środka i krótszego boku.
        CSize size = rcBoundsDP.Size () ;
        int iShortSide = min (size.cx, size.cy) ;
        CPoint ptCenter (rcBoundsDP.left + size.cx / 2,
                         rcBoundsDP.top  + size.cy / 2) ;	

       // Obliczenie stosunku LOGWIDTH do krótszego boku
       double dRatio = (double) LOGWIDTH / (double) iShortSide ;

       // Ustawienie logicznego źródła okna i zamiana osi współrzędnych
       BOOL bSuccess = SetWindowOrgEx (hdc, -int (ptCenter.x * dRatio),  int (ptCenter.y * dRatio), &ptWOOrig) ;
       ATLASSERT (0 != bSuccess);

       // Ustawienie logicznego obszaru okna
       // Kompensacja dla kodu rysującego, który rysuje w zakresie od -LOGWIDTH do +LOGWIDTH
       bSuccess = SetWindowExtEx (hdc,  int (size.cx * dRatio), -int (size.cy * dRatio), &szWEOrig) ;
       ATLASSERT (0 != bSuccess);
    }

    // Rysowanie BullsEye
    DrawBullsEye (di);

    // Uwaga na temat optymalizacji rysowania:
    // Nawet w przypadku użycia zoptymalizowanej funkcji rysowania element sterujący
    // nie może pozostawić zmienionego trybu mapowania, wartości transformacji współrzędnych,
    // wybranej bitmapy lub metapliku w kontekście urządzenia.

    if (!bMetafile) {
        ::SetMapMode (hdc, nPrevMapMode);

        ::SetViewportOrgEx (hdc, ptVOOrig.x,  ptVOOrig.y,  NULL);
        ::SetViewportExtEx (hdc, szVEOrig.cx, szVEOrig.cy, NULL);
    }

    ::SetWindowOrgEx (hdc, ptWOOrig.x,  ptWOOrig.y,  NULL);
    ::SetWindowExtEx (hdc, szWEOrig.cx, szWEOrig.cy, NULL);

    return S_OK;
}

/*****************/
/* OnLButtonDown */
/*****************/

LRESULT CBullsEye::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//    if (!m_bEnabled) return 0;

    CPoint pt (LOWORD (lParam), HIWORD (lParam));

     // Punkt jest relatywny do kontenera w trybie bez okna
    if (m_bWndLess)     // Zapewnienie relatywności do elementu sterującego
        pt.Offset (-m_rcPos.left, -m_rcPos.top);

    BOOL bUserMode = TRUE;
    HRESULT hRet = GetAmbientUserMode(bUserMode);
    // Wykonanie testu trafienia oraz testu pierścienia w trybie użytkownika
    // lub jeśli nie może ustalić trybu.
 
    if (FAILED(hRet) || bUserMode) {
        short ring = HitTest (pt);
        HitRing (ring);
        return 0;
    }

    // Rozpoczęcie przenoszenia w trybie projektowania

    DoDragDrop(DROPEFFECT_COPY, NULL);
    return 0;
}

/////////////////////////////////////////////////////////////////////////////
// CBullsEye Helper Methods

void CBullsEye::DrawBullsEye (ATL_DRAWINFO& di)
{
    HDC hdc  = di.hdcDraw;

    // Utworzenia pióra ramki tylko w razie potrzeby
    if (NULL == m_borderPen) {
        OLE_COLOR ocFore;
        HRESULT hr = get_ForeColor (&ocFore);
        ATLASSERT (SUCCEEDED (hr));

        COLORREF crFore;
        hr = ::OleTranslateColor (ocFore, NULL, &crFore);
        ATLASSERT (SUCCEEDED (hr));

        m_borderPen = ::CreatePen (PS_SOLID, 0, crFore);
        ATLASSERT (NULL != m_borderPen);
    }

    // Utworzenia pędzla koloru środka tylko w razie potrzeby
    if (NULL == m_centerBrush) {
        COLORREF  crCenter;
        HRESULT hr = ::OleTranslateColor (m_clrCenterColor, NULL, &crCenter);
        ATLASSERT (SUCCEEDED (hr));

        m_centerBrush = ::CreateSolidBrush (crCenter);
        ATLASSERT (NULL != m_centerBrush);
    }

    // Utworzenia alternatywnego pędzla koloru tylko w razie potrzeby
    if (NULL == m_alternateBrush) {
        COLORREF  crAlternate;
        HRESULT hr = ::OleTranslateColor (m_clrAlternateColor, NULL, &crAlternate);
        ATLASSERT (SUCCEEDED (hr));

        m_alternateBrush = ::CreateSolidBrush (crAlternate);
        ATLASSERT (NULL != m_alternateBrush);
    }

    // Obliczenie szerokości pierścienia
    short sRingCount;
    HRESULT hr = get_RingCount (&sRingCount);
    ATLASSERT (SUCCEEDED (hr));

    int ringWidth = LOGWIDTH / (sRingCount * 2 - 1);

    // Narysowanie granicy między pierścienami przy użyciu pióra ramki
    HPEN hOldPen = (HPEN) SelectObject (hdc, m_borderPen);

    HBRUSH hOldBrush = NULL;

    // Rysowanie kolejnych pierścieni - od zewnętrznego do wewnętrznego
    // Ta procedura nie jest zbyt wydajna
    for (short i = sRingCount - 1; i >= 0; i--) {

        // Obliczenie prostokąta ograniczającego pierścień
        int ringDiameter = i * 2 * ringWidth + ringWidth;
        int ringRadius   = ringDiameter / 2;
        CRect rcRing (-ringRadius, ringRadius, ringRadius, -ringRadius);

        // Pierścienie są ponumerowane od 1 do N w kolejności od środka od zewnątrz.
        // Pętla wykonuje jednak iterację od N-1 do 0 w odwrotnej kolejności.
        // Z tego powodu parzyste pierścienie powinny mieć kolor środka,
        // a pierścienie nieparzyste kolor alternatywny.
        HBRUSH& ringBrush = i & 1 ? m_alternateBrush : m_centerBrush;

        // Set the correct ring color
        HBRUSH hBrush = (HBRUSH) ::SelectObject (hdc, ringBrush);
        if (NULL == hOldBrush)      // Pierwsze wykonanie, zapisanie oryginalnego pędzla
            hOldBrush = hBrush;

        BOOL bStatus = ::Ellipse (hdc, rcRing.left, rcRing.right, rcRing.top, rcRing.bottom);    // Rysowanie pierścienia
        ATLASSERT (NULL != bStatus);
    }

    // Jeśli nie jest stosowane zoptymalizowane rysowanie, należy przywrócić oryginalne pióro i pędzel.
    if (!di.bOptimize) {
        ::SelectObject (hdc, hOldPen);
        ::SelectObject (hdc, hOldBrush);
    }
}

/***********/
/* HitTest */
/***********/

int CBullsEye::HitTest (const CPoint& pt)
{
    // Obliczenie wielkości elementu sterującego w jednostkach urządzenia
    CSize size;
    AtlHiMetricToPixel(&m_sizeExtent, &size);

    // Obliczenie średnicy tarczy w w jednostkach urządzenia
    int diameter = min (size.cx, size.cy);

    // Obliczenie szerokości pierścienia
    short sRingCount;
    HRESULT hr = get_RingCount (&sRingCount);
    ATLASSERT (SUCCEEDED (hr));

    int ringWidth = diameter / (sRingCount * 2 - 1);

    // Ustalenia delta x, delta y od środka okna
    int x = pt.x - size.cx / 2;
    int y = pt.y - size.cy / 2;

    // Obliczenie kwadratu odległości od środka
    int rSquared = x * x + y * y;

    // Test trafienia każdego pierścienia począwszy od środka
    for (short i = 0; i < sRingCount; i++) {

        // Obliczenie promienia danego pierścienia
        int radiusSquared = i * ringWidth + ringWidth / 2;
        radiusSquared *= radiusSquared;

        // Czy trafiono w ten pierścien?
        if (rSquared < radiusSquared)
            return i;
    }
    return -1;
}

/*************************************/
/* GetApplicationFromServiceProvider */
/*************************************/

HRESULT CBullsEye::GetApplicationFromServiceProvider (/* [out] */ IDispatch** ppDisp)
{
    if (NULL == ppDisp) return E_POINTER;

    *ppDisp = NULL;

	CComPtr<IOleClientSite> pClientSite;

    HRESULT hr = GetClientSite (&pClientSite);
    ATLASSERT (SUCCEEDED (hr));
    if (FAILED (hr)) return hr;    // Naprawdę potrzebny jest klient!!!

    // Czy kontener implementuje IServiceProvider?
    CComQIPtr<IServiceProvider> pServiceProvider = pClientSite;
	if (!pServiceProvider) return E_NOINTERFACE;

    // Tak! Żądanie obiektu Application...
	return pServiceProvider->QueryService (SID_SApplicationObject, ppDisp);
}

/***********************************/
/* GetDispatchPropertyFromDocument */
/***********************************/

HRESULT CBullsEye::GetPropertyFromDocument (/* [in] */ LPOLESTR pszProperty,
                                            /* [out] */ IDispatch** ppDisp)
{
    if (NULL == pszProperty) return E_INVALIDARG;
    if (OLESTR('\0') == *pszProperty) return E_INVALIDARG;

    if (NULL == ppDisp) return E_POINTER;

    *ppDisp = NULL;

    // Uzyskanie interfejsu Automation dla obiektu Document.
    CComDispatchDriver  pDispatch;  // Destruktor zwolni pDispatch!

    HRESULT hr = get_Parent (&pDispatch);
    if (SUCCEEDED (hr)) {

        CComVariant v;

        hr = pDispatch.GetPropertyByName (pszProperty, &v);
        if (SUCCEEDED (hr)) {
            if (v.vt != VT_DISPATCH) {
                hr = v.ChangeType (VT_DISPATCH);
                if (SUCCEEDED (hr)) {
                    *ppDisp = v.pdispVal;
                    v.vt = VT_EMPTY;
                    return S_OK;
                }
            }
        }
    }
    return hr;
}

/**************/
/* SetStrings */
/**************/

// Skopiowanie tablicy ciągów do policzonej tablicy ciągów
HRESULT SetStrings(/*[in*/ ULONG cElems, /*[in]*/ const LPCOLESTR* rgpsz, /*[out]*/ CALPOLESTR* pStrings)
{
    ATLASSERT (0 != cElems);
    ATLASSERT (NULL != rgpsz);
    ATLASSERT (NULL != pStrings);

    pStrings->cElems = 0;

    // Alokacja tablicy wskaźników ciągów
    ULONG len = cElems * sizeof(LPOLESTR);
    pStrings->pElems = (LPOLESTR*) CoTaskMemAlloc (len);
    if (NULL == pStrings->pElems) {
        return E_OUTOFMEMORY;
    }

#ifdef _DEBUG
    // Inicjalizacja tablicy na zero
    ::ZeroMemory (pStrings->pElems, len);
#endif

    // Alokacja poszczególnych ciągów
    for (ULONG i = 0; i < cElems; i++) {
        // Alokacja pamięci dla ciągu wraz z końcowym pustym znakiem
        ULONG len = (ocslen(rgpsz[i]) + 1) * sizeof(OLECHAR);
        pStrings->pElems[i] = (LPOLESTR) CoTaskMemAlloc (len);

        if (NULL == pStrings->pElems[i]) return E_OUTOFMEMORY;
        ocscpy(pStrings->pElems[i], rgpsz[i]);
        pStrings->cElems++;
    }
    return S_OK;
}

/**************/
/* SetCookies */
/**************/

// Ustawienie policzonej tablicy DWORD dna dostarczoną listę DWORD cookies
HRESULT SetCookies(/*[in*/ ULONG cElems, /*[in*/ const DWORD* pdwCookies, /*[out]*/ CADWORD* pcaCookies)
{
    ATLASSERT (0 != cElems);
    ATLASSERT (NULL != pcaCookies);

    pcaCookies->cElems = 0;

    // Alokacja tablicy Cookies
    ULONG len = cElems * sizeof(DWORD);
    pcaCookies->pElems = (LPDWORD) CoTaskMemAlloc (len);
    if (NULL == pcaCookies->pElems) {
        return E_OUTOFMEMORY;
    }

    pcaCookies->cElems = cElems;
    ::CopyMemory (pcaCookies->pElems, pdwCookies, cElems * sizeof (DWORD));

    return S_OK;
}

/**********************/
/* SendOnViewExChange */
/**********************/

// Metoda pomocnicza służąca do wysyłania powiadomienia o zmianie viewstatus elementu sterującego
HRESULT CBullsEye::SendOnViewExChange(DWORD dwViewStatus)
{
    if (m_spClientSite) {
        CComQIPtr<IAdviseSinkEx> spAdviseSinkEx (m_spClientSite);
        if (spAdviseSinkEx)
			spAdviseSinkEx->OnViewStatusChange(dwViewStatus);
    }
	return S_OK;
}

/***********/
/* HitRing */
/***********/

void CBullsEye::HitRing(short sRingNumber)
{
    if (-1 != sRingNumber) {
        if (m_beep)
            ::PlaySound((LPCTSTR)IDR_ARROW, _Module.GetResourceInstance(), SND_RESOURCE | SND_ASYNC);
        if (!m_nFreezeEvents) {
            Fire_OnRingHit (sRingNumber + 1);
            Fire_OnScoreChanged (m_ringValues [sRingNumber]);
        }
    }
}
