LINES.CPP

/************************************************************************* 
**
** This is a part of the Microsoft Source Code Samples.
**
** Copyright 1992 - 1998 Microsoft Corporation. All rights reserved.
**
** This source code is only intended as a supplement to Microsoft Development
** Tools and/or WinHelp documentation. See these sources for detailed
** information regarding the Microsoft samples programs.
**
** OLE Automation Lines object
**
** lines.cpp
**
** CLines collection implementation
**
** Written by Microsoft Product Support Services, Windows Developer Support
**
*************************************************************************/

#include <windows.h>
#include <windowsx.h>
#ifdef WIN16
#include <ole2.h>
#include <compobj.h>
#include <dispatch.h>
#include <variant.h>
#include <olenls.h>
#endif
#include "lines.h"

/*
* CLines::Create
*
* Purpose:
* Creates an instance of a Lines collection object and initializes it.
*
* Parameters:
* lMaxSize Maximum number of items that can added to collection.
* lLBound Lower bound of index of collection.
* pPane Pointer to pane that contains this collection. Required because the pane coordinates
* the lines and points collection.
* ppLines Returns Lines collection object.
*
* Return Value:
* HRESULT
*
*/
HRESULT
CLines::Create(ULONG lMaxSize, long lLBound, CPane FAR* pPane, CLines FAR* FAR* ppLines)
{
HRESULT hr;
CLines FAR* pLines = NULL;
SAFEARRAYBOUND sabound[1];

*ppLines = NULL;

// Create new collection
pLines = new CLines();
if (pLines == NULL)
goto error;

pLines->m_cMax = lMaxSize;
pLines->m_cElements = 0;
pLines->m_lLBound = lLBound;
pLines->m_pPane = pPane;

// Load type information for the Lines collection from type library.
hr = LoadTypeInfo(&pLines->m_ptinfo, IID_ILines);
if (FAILED(hr))
goto error;

// Create a safe array of variants which is used to implement the collection.
sabound[0].cElements = lMaxSize;
sabound[0].lLbound = lLBound;
pLines->m_psa = SafeArrayCreate(VT_VARIANT, 1, sabound);
if (pLines->m_psa == NULL)
{
hr = E_OUTOFMEMORY;
goto error;
}

*ppLines = pLines;
return NOERROR;

error:
if (pLines == NULL)
return E_OUTOFMEMORY;
if (pLines->m_ptinfo)
pLines->m_ptinfo->Release();
if (pLines->m_psa)
SafeArrayDestroy(pLines->m_psa);

pLines->m_psa = NULL;
pLines->m_ptinfo = NULL;

delete pLines;
return hr;
}

/*
* CLines::CLines
*
* Purpose:
* Constructor for CLines object. Initializes members to NULL.
*
*/
#pragma warning (disable : 4355)
CLines::CLines() : m_SupportErrorInfo(this, IID_ILines)
#pragma warning (default : 4355)
{
m_cRef = 0;
m_psa = NULL;
m_ptinfo = NULL;
}

/*
* CLines::~CLines
*
* Purpose:
* Destructor for CLines object.
*
*/
CLines::~CLines()
{
if (m_ptinfo) m_ptinfo->Release();
if (m_psa) SafeArrayDestroy(m_psa);
}

/*
* CLines::QueryInterface, AddRef, Release
*
* Purpose:
* Implements IUnknown::QueryInterface, AddRef, Release
*
*/
STDMETHODIMP
CLines::QueryInterface(REFIID iid, void FAR* FAR* ppv)
{
*ppv = NULL;

if (iid == IID_IUnknown || iid == IID_ILines || iid == IID_IDispatch)
*ppv = this;
else if (iid == IID_ISupportErrorInfo)
*ppv = &m_SupportErrorInfo;
else return E_NOINTERFACE;

AddRef();
return NOERROR;
}


STDMETHODIMP_(ULONG)
CLines::AddRef(void)
{

#ifdef _DEBUG
TCHAR ach[50];
wsprintf(ach, TEXT("Ref = %ld, Lines\r\n"), m_cRef+1);
OutputDebugString(ach);
#endif

return ++m_cRef;
}


STDMETHODIMP_(ULONG)
CLines::Release(void)
{

#ifdef _DEBUG
TCHAR ach[50];
wsprintf(ach, TEXT("Ref = %ld, Lines\r\n"), m_cRef-1);
OutputDebugString(ach);
#endif

if(--m_cRef == 0)
{
delete this;
return 0;
}
return m_cRef;
}

/*
* CLines::GetTypeInfoCount
*
* Purpose:
* Implements IDispatch::GetTypeInfoCount.
*
*/
STDMETHODIMP
CLines::GetTypeInfoCount(UINT FAR* pctinfo)
{
*pctinfo = 1;
return NOERROR;
}

/*
* CLines::GetTypeInfo
*
* Purpose:
* Implements IDispatch::GetTypeInfo.
*
*/
STDMETHODIMP
CLines::GetTypeInfo(
UINT itinfo,
LCID lcid,
ITypeInfo FAR* FAR* pptinfo)
{
*pptinfo = NULL;

if(itinfo != 0)
return DISP_E_BADINDEX;

m_ptinfo->AddRef();
*pptinfo = m_ptinfo;

return NOERROR;
}

/*
* CLines::GetIDsOfNames
*
* Purpose:
* Implements IDispatch::GetIDsOfNames. The standard implementation, DispGetIDsOfNames,
* is used.
*
*/
STDMETHODIMP
CLines::GetIDsOfNames(
REFIID riid,
OLECHAR FAR* FAR* rgszNames,
UINT cNames,
LCID lcid,
DISPID FAR* rgdispid)
{
return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);
}

/*
* CLines::Invoke
*
* Purpose:
* Implements IDispatch::Invoke. The standard implementation, DispInvoke,
* is used.
*
*/
STDMETHODIMP
CLines::Invoke(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS FAR* pdispparams,
VARIANT FAR* pvarResult,
EXCEPINFO FAR* pexcepinfo,
UINT FAR* puArgErr)
{
return DispInvoke(
this, m_ptinfo,
dispidMember, wFlags, pdispparams,
pvarResult, pexcepinfo, puArgErr);
}

/*
*
* Properties and methods exposed through automation.
*
*/

/*
* CLines::Add
*
* Purpose:
* Adds a line to the lines collection.
*
* Parameters:
* pLineNew IDispatch of line to be added to collection.
*
*/
STDMETHODIMP
CLines::Add(ILine FAR* pLineNew)
{
HRESULT hr;
LONG l;
CLine FAR* pLine = NULL;
VARIANT v;
HDC hdc;

// Is the collection full?
if (m_cElements == m_cMax)
return RaiseException(IDS_CollectionFull, IID_ILines);

hr = pLineNew->QueryInterface(IID_ILine, (void FAR* FAR*)&pLine);
if (FAILED(hr))
return RaiseException(IDS_LineFromOtherInstance, IID_ILines);

// Add end points of line to the pane's point collection
if (FALSE == pLine->AddEndPointsToPane(m_pPane))
{hr = RaiseException(IDS_CantAddEndPoints, IID_ILines); goto error;}

// Add new line to collection
l = m_lLBound+m_cElements;
VariantInit(&v);
V_VT(&v) = VT_DISPATCH;
V_DISPATCH(&v) = pLineNew;
hr = SafeArrayPutElement(m_psa, &l, &v);
if (FAILED(hr))
{hr = RaiseException(IDS_Unexpected, IID_ILines); goto error;}
m_cElements++;

// Draw the new line
hdc = m_pPane->GetDC();
pLine->Draw(hdc);
m_pPane->ReleaseDC(hdc);

pLine->Release();
return NOERROR;

error:
if (pLine)
pLine->Release();
return hr;
}

/*
* CLines::get_Count
*
* Purpose:
* Returns number of items in collection.
*
*/
STDMETHODIMP
CLines::get_Count(long FAR* lCount)
{
*lCount = m_cElements;
return NOERROR;
}

/*
* CLines::get_Item
*
* Purpose:
* Retrieves item from collection, given an index.
*
* Parameters:
* lIndex Index of item to be retrieved.
* ppLine Returns IDispatch of item retrieved from collection.
*
*/
STDMETHODIMP
CLines::get_Item(long lIndex, ILine FAR* FAR* ppLine)
{
HRESULT hr;
VARIANT v;

// Check if index is within range
if (lIndex < m_lLBound || lIndex >= (long)(m_lLBound+m_cElements))
return RaiseException(IDS_InvalidIndex, IID_ILines);

// Retrieve and return item. Note that SafeArrayGetElement AddRefs, so an additional AddRef
// is not required.
VariantInit(&v);
hr = SafeArrayGetElement(m_psa, &lIndex, &v);
if (FAILED(hr))
return RaiseException(IDS_Unexpected, IID_ILines);
*ppLine = (ILine FAR*) V_DISPATCH(&v);
return NOERROR;
}

/*
* CLines::get_NewEnum
*
* Purpose:
* Returns an enumerator (IEnumVARIANT) for the items curently in the collection.
* The NewEnum property is restricted and so is invisible to users of an
* automation controller's scripting language. Automation controllers that support
* a 'For Each' statement to iterate through the elements of a collection will use
* the enumerator returned by NewEnum. The enumerator creates a snapshot of the
* the current state of the collection.
*
*/
STDMETHODIMP
CLines::get__NewEnum(IUnknown FAR* FAR* ppunkEnum)
{
CEnumVariant FAR* penum = NULL;;
HRESULT hr;

*ppunkEnum = NULL;

// Create new enumerator for items currently in collection and QI for IUnknown
hr = CEnumVariant::Create(m_psa, m_cElements, &penum);
if (FAILED(hr))
{hr = RaiseException(IDS_OutOfMemory, IID_ILines); goto error;}
hr = penum->QueryInterface(IID_IUnknown, (VOID FAR* FAR*)ppunkEnum);
if (FAILED(hr))
{hr = RaiseException(IDS_Unexpected, IID_ILines); goto error;}
return NOERROR;

error:
if (penum)
delete penum;
return hr;
}

/*
* CLines::Remove
*
* Purpose:
* Removes specified item from collection.
*
* Parameters:
* lIndex Index of item to be removed.
*
*/
STDMETHODIMP
CLines::Remove(long lIndex)
{
HRESULT hr;
long l;
VARIANT HUGEP *pvar;
CLine FAR* pLine = NULL;
RECT rc;

// Check if integer index is within range.
if (lIndex < m_lLBound || lIndex >= (long)(m_lLBound+m_cElements))
return RaiseException(IDS_InvalidIndex, IID_ILines);

hr = SafeArrayAccessData(m_psa, (void HUGEP* FAR*)&pvar);
if (FAILED(hr))
return RaiseException(IDS_Unexpected, IID_ILines);
V_DISPATCH(&pvar[lIndex-m_lLBound])->QueryInterface(IID_ILine, (void FAR* FAR*)&pLine);

// Ask the pane to invalidate the area where the line is drawn.
pLine->GetInvalidateRect(&rc);
m_pPane->InvalidateRect(&rc);

// Remove end points of line from the pane's point collection.
pLine->RemoveEndPointsFromPane(m_pPane);
// Remove Line
V_DISPATCH(&pvar[lIndex-m_lLBound])->Release();
// Move up the array elements, after the element to be removed.
for (l=lIndex-m_lLBound; l<(long)(m_cElements-1); l++)
pvar[l] = pvar[l+1];
// Remove last element.
V_VT(&pvar[l]) = VT_EMPTY;

pLine->Release();
hr = SafeArrayUnaccessData(m_psa);
if (FAILED(hr))
return RaiseException(IDS_Unexpected, IID_ILines);

m_cElements--;
m_pPane->Update(); // Ask the pane to repaint invalidated areas caused by removal of line.
return NOERROR;
}

/*
*
* The following methods are not exposed through Automation
*
*/

/*
* CLines::Draw
*
* Purpose:
* Draws all lines in collection.
*
*/
STDMETHODIMP_(void)
CLines::Draw(HDC hdc)
{
HRESULT hr;
long l;
CLine FAR* pLine = NULL;
VARIANT HUGEP *pvar;

// Draw each line in the Lines collection
hr = SafeArrayAccessData(m_psa, (void HUGEP* FAR*)&pvar);
if (FAILED(hr))
return;
for (l=0; l<(long)m_cElements; l++)
{
hr = V_DISPATCH(&pvar[l])->QueryInterface(IID_ILine, (void FAR* FAR*)&pLine);
if (FAILED(hr))
continue;
pLine->Draw(hdc);
pLine->Release();
}
hr = SafeArrayUnaccessData(m_psa);
if (FAILED(hr))
return;
return;
}

/*
* CLines::Clear
*
* Purpose:
* Removes all items from collection.
*
*/
STDMETHODIMP_(void)
CLines::Clear(void)
{
SafeArrayDestroyData(m_psa);
SafeArrayAllocData(m_psa);
m_cElements = 0;
}