IPOLYLIN.CPP

/* 
* IPOLYLIN.CPP
* Polyline Component Object Chapter 5
*
* Implementation of the IPolyline interface that we expose on the
* CPolyline object.
*
* Copyright (c)1993-1997 Microsoft Corporation, All Rights Reserved
*
* Kraig Brockschmidt, Software Design Engineer
* Microsoft Systems Developer Relations
*
* Internet : kraigb@microsoft.com
* Compuserve: >INTERNET:kraigb@microsoft.com
*/


#include "polyline.h"


/*
* CImpIPolyline:CImpIPolyline
* CImpIPolyline::~CImpIPolyline
*
* Constructor Parameters:
* pObj PCPolyline pointing to the object we live in.
* pUnkOuter LPUNKNOWN of the controlling unknown.
*/

CImpIPolyline::CImpIPolyline(PCPolyline pObj, LPUNKNOWN pUnkOuter)
{
m_cRef=0;
m_pObj=pObj;
m_pUnkOuter=pUnkOuter;
return;
}


CImpIPolyline::~CImpIPolyline(void)
{
return;
}




/*
* CImpIPolyline::QueryInterface
* CImpIPolyline::AddRef
* CImpIPolyline::Release
*
* Purpose:
* Standard set of IUnknown members for this interface
*/

STDMETHODIMP CImpIPolyline::QueryInterface(REFIID riid, PPVOID ppv)
{
return m_pUnkOuter->QueryInterface(riid, ppv);
}

STDMETHODIMP_(ULONG) CImpIPolyline::AddRef(void)
{
++m_cRef;
return m_pUnkOuter->AddRef();
}

STDMETHODIMP_(ULONG) CImpIPolyline::Release(void)
{
--m_cRef;
return m_pUnkOuter->Release();
}





/*
* CImpIPolyline::Init
*
* Purpose:
* Instantiates a polyline window within a given parent. The
* parent may be a main application window, could be an MDI child
* window. We really do not care.
*
* Parameters:
* hWndParent HWND of the parent of this window
* pRect LPRECT that this window should occupy
* dwStyle DWORD containing the window's style flags
* uID UINT ID to associate with this window
*
* Return Value:
* HRESULT NOERROR if successful, otherwise E_OUTOFMEMORY
*/

STDMETHODIMP CImpIPolyline::Init(HWND hWndParent, LPRECT pRect
, DWORD dwStyle, UINT uID)
{
SCODE sc;

m_pObj->m_hWnd=CreateWindowEx(WS_EX_NOPARENTNOTIFY
, SZCLASSPOLYLINE, SZCLASSPOLYLINE, dwStyle, pRect->left
, pRect->top, pRect->right-pRect->left
, pRect->bottom-pRect->top, hWndParent, (HMENU)uID
, m_pObj->m_hInst, m_pObj);

sc=(NULL!=m_pObj->m_hWnd) ? S_OK : E_OUTOFMEMORY;
return sc;
}



//CHAPTER5MOD
/*
* ReadFromFile and WriteToFile are replaced by the IPersistStorage
* implementation. We no longer read from files, instead we use
* storages.
*/
//End CHAPTER5MOD




/*
* CImpIPolyline::DataSet
*
* Purpose:
* Sets the current data in this Polyline to a given structure.
*
* Parameters:
* pplIn PPOLYLINEDATA to initialize to.
* fSizeToData BOOL indicating if we're to size to the data
* or scale it.
* fNotify BOOL indicating if we're to send an advise
* on this change.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::DataSet(PPOLYLINEDATA pplIn
, BOOL fSizeToData, BOOL fNotify)
{
PPOLYLINEDATA ppl=&m_pObj->m_pl;
RECT rc;

/*
* Copy the structure in pplIn and repaint to reflect the
* new point set. Note that unlike the RectSet message, we
* do no scaling, assuming that the rect in the structure
* is appropriate for the data.
*/

if (NULL==pplIn)
return POLYLINE_E_INVALIDPOINTER;

*ppl=*pplIn;

//Inform our parent of the data change
if (NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnDataChange();
}

/*
* If we're scaling the window to fit the data, then use
* RectSet passing our current rectangle as the new one.
* That makes sure that the data won't change but that the
* window is resized.
*/

if (fSizeToData)
{
POINT pt;

/*
* Get our offset in the parent window so we can RectSet
* to the right place since RectSet expects rectangle in
* parent coordinates and we get it in client coordinates.
*/
GetWindowRect(m_pObj->m_hWnd, &rc);
pt.x=rc.left;
pt.y=rc.top;
ScreenToClient(GetParent(m_pObj->m_hWnd), &pt);
RECTSTORECT(ppl->rc, rc);
OffsetRect(&rc, pt.x, pt.y);

//This will also cause a repaint.
RectSet(&rc, fNotify);
}
else
{
//Make sure we're updated.
InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);
UpdateWindow(m_pObj->m_hWnd);
}

return NOERROR;
}







/*
* CImpIPolyline::DataGet
*
* Purpose:
* Retrieves the Polyline's current data.
*
* Parameters:
* pplIn PPOLYLINEDATA into which we copy the data.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::DataGet(PPOLYLINEDATA pplIn)
{
if (NULL==pplIn)
return POLYLINE_E_INVALIDPOINTER;

*pplIn=m_pObj->m_pl;
return NOERROR;
}







/*
* CImpIPolyline::DataSetMem
*
* Purpose:
* Sets the Polyline's data using a global memory handle
* instead of a pointer.
*
* Parameters:
* hMem HGLOBAL containing the data.
* fFree BOOL indicating if we're to free the data.
* The memory will be freed regardless of any
* error returned from here.
* fSizeToData BOOL indicating if we're to size to the data
* or scale it.
* fNotify BOOL indicating if we're to send an advise
* on this change.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::DataSetMem(HGLOBAL hMem, BOOL fFree
, BOOL fSizeToData, BOOL fNotify)
{
PPOLYLINEDATA ppl;
HRESULT hr=POLYLINE_E_INVALIDPOINTER;

if (NULL!=hMem)
{
ppl=(PPOLYLINEDATA)GlobalLock(hMem);

hr=DataSet(ppl, fSizeToData, fNotify);

GlobalUnlock(hMem);

if (fFree)
GlobalFree(hMem);
}

return hr;
}







/*
* CImpIPolyline::DataGetMem
*
* Purpose:
* Retrieves the Polyline's data in a global memory handle.
*
* Parameters:
* phMem HGLOBAL * in which to store the handle.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::DataGetMem(HGLOBAL *phMem)
{
HGLOBAL hMem;
PPOLYLINEDATA ppl;
HRESULT hr=POLYLINE_E_INVALIDPOINTER;

if (NULL==phMem)
return POLYLINE_E_INVALIDPOINTER;

hMem=GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, CBPOLYLINEDATA);

if (NULL!=hMem)
{
ppl=(PPOLYLINEDATA)GlobalLock(hMem);
hr=DataGet(ppl);

GlobalUnlock(hMem);

if (FAILED(hr))
{
GlobalFree(hMem);
hMem=NULL;
}
}

*phMem=hMem;
return hr;
}




/*
* CImpIPolyline::RenderBitmap
*
* Purpose:
* Creates a bitmap image of the current Polyline.
*
* Parameters:
* phBmp HBITMAP * in which to return the bitmap.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::RenderBitmap(HBITMAP *phBmp)
{
HDC hDC;
HDC hMemDC;
HBITMAP hBmp;
RECT rc;
HGDIOBJ hObj;

if (NULL==phBmp)
return POLYLINE_E_INVALIDPOINTER;

//Render a bitmap the size of the current rectangle.
hDC=GetDC(m_pObj->m_hWnd);
hMemDC=CreateCompatibleDC(hDC);

GetClientRect(m_pObj->m_hWnd, &rc);
hBmp=CreateCompatibleBitmap(hDC, rc.right, rc.bottom);

if (NULL!=hBmp)
{
//Draw the POLYLINEDATA into the bitmap.
hObj=SelectObject(hMemDC, hBmp);
m_pObj->Draw(hMemDC, FALSE, TRUE);
SelectObject(hMemDC, hObj);
}

DeleteDC(hMemDC);
ReleaseDC(m_pObj->m_hWnd, hDC);

*phBmp=hBmp;
return NOERROR;
}







/*
* CImpIPolyline::RenderMetafile
*
* Purpose:
* Renders the current image of the Polyline into a metafile.
*
* Parameters:
* phMF HMETAFILE * in which to return the
* new metafile.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::RenderMetafile(HMETAFILE *phMF)
{
HDC hDC;
HMETAFILE hMF;
RECT rc;

if (NULL==phMF)
return POLYLINE_E_INVALIDPOINTER;

//Create a memory metafile and return its handle.
hDC=(HDC)CreateMetaFile(NULL);
hMF=NULL;

if (NULL!=hDC)
{
/*
* This is absolutely essential to the metafile so it
* can be scaled in the clipboard and any destination
* application.
*/
SetMapMode(hDC, MM_ANISOTROPIC);
GetClientRect(m_pObj->m_hWnd, &rc);
SetWindowOrgEx(hDC, 0, 0, NULL);
SetWindowExtEx(hDC, rc.right, rc.bottom, NULL);

m_pObj->Draw(hDC, TRUE, TRUE);
hMF=CloseMetaFile(hDC);
}

*phMF=hMF;
return NOERROR;
}






/*
* CImpIPolyline::RenderMetafilePict
*
* Purpose:
* Renders the current Polyline into a METAFILEPICT structure in
* global memory.
*
* Parameters:
* phMem HGLOBAL * in which to return the
* METAFILEPICT.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::RenderMetafilePict(HGLOBAL * phMem)
{
HGLOBAL hMem;
HMETAFILE hMF;
LPMETAFILEPICT pMF;
RECT rc;
HRESULT hr;

if (NULL==phMem)
return POLYLINE_E_INVALIDPOINTER;

//Get the metafile
hr=RenderMetafile(&hMF);

if (FAILED(hr))
return hr;

//Allocate the METAFILEPICT structure.
hMem=GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE
, sizeof(METAFILEPICT));

if (NULL==hMem)
{
DeleteMetaFile(hMF);
return E_FAIL;
}

/*
* Global lock only fails in PMODE if the selector is invalid
* (like it was discarded) or references a 0 length segment,
* neither of which can happen here.
*/
pMF=(LPMETAFILEPICT)GlobalLock(hMem);

pMF->hMF=hMF;
pMF->mm=MM_ANISOTROPIC;

//Insert the extents in MM_HIMETRIC units.
GetClientRect(m_pObj->m_hWnd, &rc);
m_pObj->RectConvertMappings(&rc, FALSE);
pMF->xExt=rc.right;
pMF->yExt=rc.bottom;

GlobalUnlock(hMem);

*phMem=hMem;
return NOERROR;
}









/*
* CImpIPolyline::New
*
* Purpose:
* Cleans out and reinitializes the data to defaults.
*
* Parameters:
* None
*
* Return Value:
* HRESULT NOERROR always
*/

STDMETHODIMP CImpIPolyline::New(void)
{
PPOLYLINEDATA ppl=&m_pObj->m_pl;
UINT i;
RECT rc;

ppl->wVerMaj=VERSIONMAJOR;
ppl->wVerMin=VERSIONMINOR;

//Our rectangle is the size of our window's client area.
GetClientRect(m_pObj->m_hWnd, &rc);
RECTTORECTS(rc, ppl->rc);

//Clean out the POLYLINEDATA structure and repaint the window.
for (i=0; i< CPOLYLINEPOINTS; i++)
{
ppl->rgpt[i].x=0;
ppl->rgpt[i].y=0;
}

ppl->cPoints =0;
ppl->rgbBackground=GetSysColor(COLOR_WINDOW);
ppl->rgbLine =GetSysColor(COLOR_WINDOWTEXT);
ppl->iLineStyle =PS_SOLID;

InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);
UpdateWindow(m_pObj->m_hWnd);

//Inform the advise sink of this data change.
if (NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnDataChange();
}

return NOERROR;
}






/*
* CImpIPolyline::Undo
*
* Purpose:
* Reverses previous actions in a Polyline.
*
* Parameters:
* None
*
* Return Value:
* HRESULT S_OK if we can Undo more, S_FALSE otherwise.
*/

STDMETHODIMP CImpIPolyline::Undo(void)
{
SCODE sc;

//Decrement the number of active points and repaint.
if (m_pObj->m_pl.cPoints > 0)
{
m_pObj->m_pl.cPoints--;
InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);
UpdateWindow(m_pObj->m_hWnd);
}

if (NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnPointChange();
}

//Return if we can undo any more.
sc=(0!=m_pObj->m_pl.cPoints) ? S_OK : S_FALSE;
return sc;
}






/*
* CImpIPolyline::Window
*
* Purpose:
* Returns the window handle associated with this polyline.
*
* Parameters:
* phWnd HWND * in which to return the window handle.
*
* Return Value:
* HRESULT NOERROR always.
*/

STDMETHODIMP CImpIPolyline::Window(HWND *phWnd)
{
*phWnd=m_pObj->m_hWnd;
return NOERROR;
}






/*
* CImpIPolyline::SetAdvise
*
* Purpose:
* Provides this object with an IPolylineAdviseSink that's
* interested in our notifications. We AddRef and store this
* pointer, Releasing the old one.
*
* Parameters:
* pAdv PPOLYLINEADVISESINK to notify.
*
* Return Value:
* HRESULT NOERROR always.
*/

STDMETHODIMP CImpIPolyline::SetAdvise(PPOLYLINEADVISESINK pAdv)
{
if (NULL!=m_pObj->m_pAdv)
m_pObj->m_pAdv->Release();

m_pObj->m_pAdv=pAdv;

if (NULL!=m_pObj->m_pAdv)
m_pObj->m_pAdv->AddRef();

return NOERROR;
}




/*
* CImpIPolyline::GetAdvise
*
* Purpose:
* Returns the IPolylineAdviseSink that was last passed to
* SetAdvise.
*
* Parameters:
* ppAdv PPOLYLINEADVISESINK * in which to return
* the pointer.
*
* Return Value:
* HRESULT NOERROR always.
*/

STDMETHODIMP CImpIPolyline::GetAdvise(PPOLYLINEADVISESINK *ppAdv)
{
*ppAdv=m_pObj->m_pAdv;
return NOERROR;
}






/*
* CImpIPolyline::RectGet
*
* Purpose:
* Returns the rectangle of the Polyline in parent coordinates.
*
* Parameters:
* pRect LPRECT in which to return the rectangle.
*
* Return Value:
* HRESULT NOERROR always
*/

STDMETHODIMP CImpIPolyline::RectGet(LPRECT pRect)
{
RECT rc;
POINT pt;

//Retrieve the size of our rectangle in parent coordinates.
GetWindowRect(m_pObj->m_hWnd, &rc);
pt.x=rc.left;
pt.y=rc.top;
ScreenToClient(GetParent(m_pObj->m_hWnd), &pt);

SetRect(pRect, pt.x, pt.y, pt.x+(rc.right-rc.left)
, pt.y+(rc.bottom-rc.top));

return NOERROR;
}





/*
* CImpIPolyline::SizeGet
*
* Purpose:
* Retrieves the size of the Polyline in parent coordinates.
*
* Parameters:
* pRect LPRECT in which to return the size. The right
* and bottom fields will contain the dimensions.
*
* Return Value:
* HRESULT NOERROR always
*/

STDMETHODIMP CImpIPolyline::SizeGet(LPRECT pRect)
{
RectGet(pRect);
return NOERROR;
}






/*
* CImpIPolyline::RectSet
*
* Purpose:
* Sets a new rectangle for the Polyline which sizes to fit.
*
* Parameters:
* pRect LPRECT containing the new rectangle.
* fNotify BOOL indicating if we're to notify anyone of
* the change.
*
* Return Value:
* HRESULT NOERROR always
*/

STDMETHODIMP CImpIPolyline::RectSet(LPRECT pRect, BOOL fNotify)
{
UINT cx, cy;
RECT rc;

//Scale the points from our current size to the new size
cx=pRect->right-pRect->left;
cy=pRect->bottom-pRect->top;

SetWindowPos(m_pObj->m_hWnd, NULL, pRect->left, pRect->top
, cx, cy, SWP_NOZORDER);

SetRect(&rc, 0, 0, cx, cy);
RECTTORECTS(rc, m_pObj->m_pl.rc);

if (fNotify && NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnSizeChange();
}

InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);

return NOERROR;
}







/*
* CImpIPolyline::SizeSet
*
* Purpose:
* Sets a new size for the Polyline which sizes to fit.
*
* Parameters:
* pRect LPRECT containing the new rectangle.
* fNotify BOOL indicating if we're to notify anyone of
* the change.
*
* Return Value:
* HRESULT NOERROR always
*/

STDMETHODIMP CImpIPolyline::SizeSet(LPRECT pRect, BOOL fNotify)
{
UINT cx, cy;

//Scale the points from our current size to the new size
cx=pRect->right-pRect->left;
cy=pRect->bottom-pRect->top;

SetWindowPos(m_pObj->m_hWnd, NULL, 0, 0, (UINT)cx, (UINT)cy
, SWP_NOMOVE | SWP_NOZORDER);

if (fNotify && NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnSizeChange();
}

InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);

return NOERROR;
}






/*
* CImpIPolyline::ColorSet
*
* Purpose:
* Changes for background or line color in the Polyline
*
* Parameters:
* iColor UINT index of the color to change.
* cr COLORREF new color to use.
* pcrPrev COLORREF * in whch to store the
* previous color.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::ColorSet(UINT iColor, COLORREF cr
, COLORREF *pcrPrev)
{
COLORREF crRet;

if (NULL==pcrPrev)
return POLYLINE_E_INVALIDPOINTER;

switch (iColor)
{
case POLYLINECOLOR_BACKGROUND:
crRet=m_pObj->m_pl.rgbBackground;
m_pObj->m_pl.rgbBackground=cr;
break;

case POLYLINECOLOR_LINE:
crRet=m_pObj->m_pl.rgbLine;
m_pObj->m_pl.rgbLine=cr;
break;
}

//If the color changed, repaint
if (crRet!=cr)
{
if (NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnColorChange();
}

InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);
UpdateWindow(m_pObj->m_hWnd);
}

*pcrPrev=crRet;
return NOERROR;
}







/*
* CImpIPolyline::ColorGet
*
* Purpose:
* Retrieves one of the colors currently in use by the Polyline.
*
* Parameters:
* iColor UINT identifying the color of interest.
* pcr COLORREF * in which to return the color.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::ColorGet(UINT iColor, COLORREF *pcr)
{
COLORREF crRet;

if (NULL==pcr)
return POLYLINE_E_INVALIDPOINTER;

crRet=(POLYLINECOLOR_BACKGROUND==iColor)
? m_pObj->m_pl.rgbBackground : m_pObj->m_pl.rgbLine;

*pcr=crRet;
return NOERROR;
}








/*
* CImpIPolyline::LineStyleSet
*
* Purpose:
* Changes the line style in use by the Polyline
*
* Parameters:
* iStyle UINT style of the line to use.
* piPrev UINT * in which to store the previous style.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::LineStyleSet(UINT iStyle, UINT *piPrev)
{
UINT uRet;

uRet=(UINT)m_pObj->m_pl.iLineStyle;

if (NULL==piPrev)
return POLYLINE_E_INVALIDPOINTER;

//Validate the line style
if (PS_SOLID==iStyle || PS_DASH==iStyle || PS_DOT==iStyle
|| PS_DASHDOT==iStyle || PS_DASHDOTDOT==iStyle)
{
m_pObj->m_pl.iLineStyle=iStyle;

if (uRet!=(UINT)m_pObj->m_pl.iLineStyle)
{
if (NULL!=m_pObj->m_pAdv)
{
m_pObj->m_fDirty=TRUE;
m_pObj->m_pAdv->OnLineStyleChange();
}

InvalidateRect(m_pObj->m_hWnd, NULL, TRUE);
UpdateWindow(m_pObj->m_hWnd);
}
}

*piPrev=uRet;
return NOERROR;
}







/*
* CImpIPolyline::LineStyleGet
*
* Purpose:
* Retrieves the current line style in use in the Polyline
*
* Parameters:
* piStyle UINT * in which to store the style.
*
* Return Value:
* HRESULT NOERROR if successful, otherwise a
* POLYLINE_E_ value.
*/

STDMETHODIMP CImpIPolyline::LineStyleGet(UINT *piStyle)
{
if (NULL==piStyle)
return POLYLINE_E_INVALIDPOINTER;

*piStyle=m_pObj->m_pl.iLineStyle;
return NOERROR;
}