AUTOFIG.CPP

/* 
* AUTOFIG.CPP
* Cosmo Chapter 14
*
* "Figure" object for Cosmo's OLE Automation support, derived
* from CAutoBase.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Right Reserved.
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: INTERNET>kraigb@microsoft.com
*/


#include "cosmo.h"


/*
* CAutoFigure::CAutoFigure
* CAutoFigure::~CAutoFigure
*
* Constructor Parameters:
* pDoc PCCosmoDoc to the dpcument object that we
* use to implement much of this interface.
*/

CAutoFigure::CAutoFigure(PCCosmoDoc pDoc)
: CAutoBase(pDoc, pDoc->m_hInst, IID_ICosmoFigure
, DIID_DICosmoFigure, ObjectDestroyed)
{
return;
}


/*
* CAutoFigure::QueryInterface
* CAutoFigure::AddRef
* CAutoFigure::Release
*/

STDMETHODIMP CAutoFigure::QueryInterface(REFIID riid, PPVOID ppv)
{
*ppv=NULL;

if (IID_IUnknown==riid || IID_ICosmoFigure==riid)
*ppv=(IUnknown *)this;

if (IID_IDispatch==riid || m_diid==riid)
*ppv=m_pImpIDispatch;

if (IID_IExternalConnection==riid)
*ppv=m_pImpIExtConn;

if (NULL!=*ppv)
{
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}

return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) CAutoFigure::AddRef(void)
{
return ++m_cRef;
}

STDMETHODIMP_(ULONG) CAutoFigure::Release(void)
{
/*
* Since this object might have come from a class factory,
* we count it's existence (see NewFigure below) for
* controlling shutdown of the application when we call
* ObjectDestroyed. Otherwise we always close the document.
*/
if (0L!=--m_cRef)
return m_cRef;

put_Visible(FALSE);
SendMessage(m_pDoc->Window(), WM_CLOSE, 0, 0L);

if (NULL!=m_pfnDestroy)
(*m_pfnDestroy)();

return 0L;
}


/*
* CAutoFigure::VTableInterface
*
* Purpose:
* Returns the right vtable pointer to use when calling
* ITypeInfo::Invoke (see CImpIDispatch::Invoke in AUTOBASE.CPP).
*/
void *CAutoFigure::VTableInterface(void)
{
return (ICosmoFigure *)this;
}



//All that follows is the ICosmoFigure implementation

/*
* CAutoFigure::Application
* CAutoFigure::Parent
* Properties, read-only
*
* The application object (CAutoApp) in which we're contained,
* which is stored in the frame object
*/

STDMETHODIMP_(IDispatch *) CAutoFigure::get_Application(void)
{
PCCosmoFrame pFR;

pFR=(PCCosmoFrame)m_pDoc->m_pFR;
return pFR->AutoApp()->get_Application();
}

STDMETHODIMP_(IDispatch *) CAutoFigure::get_Parent(void)
{
return get_Application();
}


/*
* CAutoFigure::FullName, Name, Path
* Properties, read-only
*
* Retrieve the full pathname of the figure file (FullName),
* just the file name (Name), or just the path (Path).
*
* Note that these functions are very similar to the same
* ones in CAutoApp, and there is probably some code that could
* be shared between them, but this sample won't bother with
* such an optimization.
*/

STDMETHODIMP_(BSTR) CAutoFigure::get_FullName(void)
{
if ((TCHAR)0==m_pDoc->m_szFile[0])
return NULL;

#ifdef WIN32ANSI
OLECHAR szTemp[512];

MultiByteToWideChar(CP_ACP, 0, m_pDoc->m_szFile, -1, szTemp, 512);
return SysAllocString(szTemp);
#else
return SysAllocString(m_pDoc->m_szFile);
#endif
}

STDMETHODIMP_(BSTR) CAutoFigure::get_Name(void)
{
BSTR bstrFull=NULL;
BSTR bstrName=NULL;
const int cch=256;
TCHAR szName[256];

//Get the full path
bstrFull=get_FullName();

if (NULL==bstrFull)
return NULL;

//Now retrieve just the filename
#ifdef WIN32ANSI
char szTemp[cch];
OLECHAR szTempW[cch];

WideCharToMultiByte(CP_ACP, 0, bstrFull, -1, szTemp
, cch, NULL, NULL);
if (0==GetFileTitle(szTemp, szName, cch))
{
MultiByteToWideChar(CP_ACP, 0, szName, -1, szTempW, cch);
bstrName=SysAllocString(szTempW);
}
#else
if (0==GetFileTitle(bstrFull, szName, cch))
bstrName=SysAllocString(szName);
#endif

SysFreeString(bstrFull);
return bstrName;
}

STDMETHODIMP_(BSTR) CAutoFigure::get_Path(void)
{
BSTR bstrFull=NULL;
BSTR bstrName=NULL;
BSTR bstrPath=NULL;

bstrFull=get_FullName();

if (NULL==bstrFull)
return NULL;

bstrName=get_Name();

if (NULL!=bstrName)
{
LPOLESTR psz;

/*
* Find the position of bstrName in bstrFull then copy
* only charaters up to that point into bstrPath.
*/
#ifdef WIN32ANSI
psz=wcsstr(bstrFull, bstrName);
#else
psz=_tcsstr(bstrFull, bstrName);
#endif

//The -1 accounts for the \ before the filename
bstrPath=SysAllocStringLen(bstrFull
, ((psz-bstrFull)/sizeof(TCHAR))-1);
SysFreeString(bstrName);
}

SysFreeString(bstrFull);
return bstrPath;
}


/*
* CAutoFigure::Saved
* Property, read-only
*
* TRUE if the document is clean, FALSE otherwise.
*/

STDMETHODIMP_(VARIANT_BOOL) CAutoFigure::get_Saved(void)
{
return !m_pDoc->FDirtyGet();
}


/*
* CAutoFigure::NumberOfPoints
* Property, read-only
*
* Number of points in the current figure.
*/

STDMETHODIMP_(short)CAutoFigure::get_NumberOfPoints(void)
{
POLYLINEDATA pl;

m_pDoc->m_pPL->DataGet(&pl, VERSIONCURRENT);
return pl.cPoints;
}


/*
* CAutoFigure::BackColor
* CAutoFigure::LineColor
* Properties, read-write
*
* Colors used in the figure.
*/

STDMETHODIMP_(long) CAutoFigure::get_BackColor(void)
{
return m_pDoc->ColorGet(POLYLINECOLOR_BACKGROUND);
}

STDMETHODIMP_(void) CAutoFigure::put_BackColor(long clrBack)
{
m_pDoc->ColorSet(POLYLINECOLOR_BACKGROUND, clrBack);
return;
}

STDMETHODIMP_(long) CAutoFigure::get_LineColor(void)
{
return m_pDoc->ColorGet(POLYLINECOLOR_LINE);
}

STDMETHODIMP_(void) CAutoFigure::put_LineColor(long clrLine)
{
m_pDoc->ColorSet(POLYLINECOLOR_LINE, clrLine);
return;
}


/*
* CAutoFigure::LineStyle
* Property, read-write
*
* Line style used to draw the figure
*/

STDMETHODIMP_(short)CAutoFigure::get_LineStyle(void)
{
return m_pDoc->LineStyleGet();
}

STDMETHODIMP_(void) CAutoFigure::put_LineStyle(short iStyle)
{
m_pDoc->LineStyleSet(iStyle);
return;
}


/*
* CAutoFigure::Left, Top, Width, Height
* Properties, read-write
*
* Horizontal (Left) and vertical (Top) positions of the frame
* window from the left and top edges of the application client
* area; horizontal (Width) and vertical (Height) dimensions of
* document window. All of these functions call our private member
* MoveSize, a helper function.
*/

STDMETHODIMP_(long) CAutoFigure::get_Left(void)
{
return MoveSize(MOVESIZEACTION_GETLEFT, 0, 0, 0, 0);
}

STDMETHODIMP_(void) CAutoFigure::put_Left(long x)
{
MoveSize(MOVESIZEACTION_LEFT, x, 0, 0, 0);
return;
}

STDMETHODIMP_(long) CAutoFigure::get_Top(void)
{
return MoveSize(MOVESIZEACTION_GETTOP, 0, 0, 0, 0);
}

STDMETHODIMP_(void) CAutoFigure::put_Top(long y)
{
MoveSize(MOVESIZEACTION_TOP, 0, y, 0, 0);
return;
}

STDMETHODIMP_(long) CAutoFigure::get_Width(void)
{
return MoveSize(MOVESIZEACTION_GETWIDTH, 0, 0, 0, 0);
}

STDMETHODIMP_(void) CAutoFigure::put_Width(long cx)
{
MoveSize(MOVESIZEACTION_WIDTH, 0, 0, cx, 0);
return;
}

STDMETHODIMP_(long) CAutoFigure::get_Height(void)
{
return MoveSize(MOVESIZEACTION_GETHEIGHT, 0, 0, 0, 0);
}

STDMETHODIMP_(void) CAutoFigure::put_Height(long cy)
{
MoveSize(MOVESIZEACTION_HEIGHT, 0, 0, 0, cy);
return;
}



/*
* CAutoFigure::Visible
* Properties, read-write
*
* Controls visibility of the figure window (which is hidden by
* default when created through automation).
*/

STDMETHODIMP_(VARIANT_BOOL) CAutoFigure::get_Visible(void)
{
return (VARIANT_BOOL)IsWindowVisible(m_pDoc->Window());
}

STDMETHODIMP_(void) CAutoFigure::put_Visible(VARIANT_BOOL fShow)
{
ShowWindow(m_pDoc->Window(), fShow ? SW_SHOW : SW_HIDE);
return;
}


/*
* CAutoFigure::Activate
* Method
*
* Activate this window, that is, bring it to the foreground
*/

STDMETHODIMP_(void) CAutoFigure::Activate(void)
{
HWND hWndDoc;

hWndDoc=m_pDoc->Window();
SendMessage(GetParent(hWndDoc), WM_MDIACTIVATE
, (WPARAM)hWndDoc, 0L);
return;
}



/*
* CAutoFigure::Close
* Method
*
* Closes this document.
*
* Parameters (optional)
* fSaveChanges BOOL that indicates if we're to save
* changes or not. If not, then we just nuke
* the figure.
* bstrPath BSTR with the filename into which to save
* the figure if fSaveChanges is TRUE. Note that
* this overrides any other pathname we are
* already using.
*/

STDMETHODIMP_(void) CAutoFigure::Close(VARIANT varSave
, VARIANT varPath)
{
//If we got varSave and it's TRUE, then save
if (VT_ERROR!=varSave.vt && varSave.boolVal)
{
/*
* If we got a filename, call SaveAs. Otherwise
* call Save. If we don't have a filename for
* Save, then Save just fails and we don't have
* to care.
*/
if (VT_ERROR!=varPath.vt)
SaveAs(varPath.bstrVal);
else
Save();
}

//Hiding the document first suppresses any UI on closure.
put_Visible(FALSE);
SendMessage(m_pDoc->Window(), WM_CLOSE, 0, 0L);
return;
}



/*
* CAutoFigure::RevertToSaved
* Method
*
* Reloads the contents of the document from the saved state.
*/

STDMETHODIMP_(void) CAutoFigure::RevertToSaved(void)
{
//Can't do this if we don't have a file
if ((TCHAR)0==m_pDoc->m_szFile[0])
return;

/*
* Since we "open" a document by loading it and closing
* it, then we can just "re-open" it with Load again.
*/
m_pDoc->Load(FALSE, m_pDoc->m_szFile);

/*
* Load set the document as dirt when loading from a file
* in this manner (the behavior of Import). But this actually
* makes the file clean, so we change the dirty flag here.
*/
m_pDoc->FDirtySet(FALSE);
return;
}



/*
* CAutoFigure::Save
* Method
*
* Saves the document to a known file if one exists.
*/

STDMETHODIMP_(void) CAutoFigure::Save(void)
{
if ((TCHAR)0==m_pDoc->m_szFile[0])
return;

m_pDoc->Save(0, (LPTSTR)NULL);
return;
}



/*
* CAutoFigure::SaveAs
* Method
*
* Saves the current figure to a new file.
*/

STDMETHODIMP_(void) CAutoFigure::SaveAs(BSTR bstrPath)
{
if (NULL==bstrPath)
return;

//This also renames the document
#ifdef WIN32ANSI
char szTemp[512];

WideCharToMultiByte(CP_ACP, 0, bstrPath, -1, szTemp
, 512, NULL, NULL);
m_pDoc->Save(0, szTemp);
#else
m_pDoc->Save(0, bstrPath);
#endif
return;
}



/*
* CAutoFigure::Import
* Method
*
* Initializes the figure from the contents of a file
*/

STDMETHODIMP_(void) CAutoFigure::Import(BSTR bstrImportPath)
{
if (NULL==bstrImportPath)
return;

/*
* Like we have for RevertToSaved, we can just "open"
* this file and the new information becomes current.
*/
#ifdef WIN32ANSI
char szTemp[512];

WideCharToMultiByte(CP_ACP, 0, bstrImportPath, -1
, szTemp, 512, NULL, NULL);
m_pDoc->Load(FALSE, szTemp);
#else
m_pDoc->Load(FALSE, bstrImportPath);
#endif
return;
}



/*
* CAutoFigure::Copy
* CAutoFigure::Cut
* CAutoFigure::Paste
* Methods
*
* Perform clipboard operations
*/

STDMETHODIMP_(void) CAutoFigure::Copy(void)
{
m_pDoc->Clip(m_pDoc->m_pFR->Window(), FALSE);
return;
}

STDMETHODIMP_(void) CAutoFigure::Cut(void)
{
m_pDoc->Clip(m_pDoc->m_pFR->Window(), TRUE);
return;
}

STDMETHODIMP_(void) CAutoFigure::Paste(void)
{
if (m_pDoc->FQueryPaste())
m_pDoc->Paste(m_pDoc->m_pFR->Window());

return;
}




/*
* CAutoFigure::AddPoint
* Method
*
* Adds a new point, expressed on a 0-32767 scale, to the figure.
*/

STDMETHODIMP_(VARIANT_BOOL) CAutoFigure::AddPoint(short x, short y)
{
RECT rc;
POINTS pt;
HWND hWndPL;

/*
* This is the only semi-tricky method to implement here because
* normally this processing is done inside the Polyline's
* WM_LBUTTONDOWN. In order to get the same behavior, we'll
* just send the same message to it. However, we have to
* convert from a 0-32767 range to a window coordinate range
* which is nicely handled by CPolyline::PointScale (the only
* small modification to CPolyline I had to make was to make
* PointScale public instead of private).
*
* This is a non-invasive way to add Automation support.
* The invasive way would require a change to the CPolyline
* class and it's window procedure so both this function
* and the WM_LBUTTONDOWN handling called the same function
* with 0-32767 scaled points.
*/

//See if we can add any more
if (CPOLYLINEPOINTS==get_NumberOfPoints())
return FALSE;

hWndPL=m_pDoc->m_pPL->Window();
GetClientRect(hWndPL, &rc);
SETPOINT(pt, x, y);
m_pDoc->m_pPL->PointScale(&rc, &pt, TRUE);
SendMessage(hWndPL, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
return TRUE;
}



/*
* CAutoFigure::RemovePoint
* Method
*
* Removes the last point added to a figure
*/

STDMETHODIMP_(void) CAutoFigure::RemovePoint(void)
{
m_pDoc->Undo();
return;
}




/*
* CAutoFigure::MoveSize
*
* Purpose:
* Helper function for the Left, Top, Width, and Height properties
* that centralizes calls to GetWindowRect and SetWindowPos,
* reducing overall code somewhat.
*
* Parameters:
* iAction MOVESIZEACTION enum value to indicate what
* type of moving/sizing to perform
* x, y long position coordinates; x is only meaningful
* with MOVESIZEACTION_LEFT, y only with _TOP
* cx, cy long extents; cx is only meaningful
* with MOVESIZEACTION_WIDTH, cy only with _HEIGHT
*
* Return Value:
* long The current x, y, cx, or cy value depending on
* iAction being _GETLEFT, _GETTOP, _GETWIDTH, or
* _GETHEIGHT.
*/

long CAutoFigure::MoveSize(MOVESIZEACTION iAction, long x, long y
, long cx, long cy)
{
RECT rc;
POINT pt1, pt2;
long x1, y1, cx1, cy1;
UINT uFlags;

GetWindowRect(m_pDoc->Window(), &rc);
SETPOINT(pt1, rc.left, rc.top);
ScreenToClient(GetParent(m_pDoc->Window()), &pt1);
SETPOINT(pt2, rc.right, rc.bottom);
ScreenToClient(GetParent(m_pDoc->Window()), &pt2);

//By default we'll do nothing
x1=pt1.x;
y1=pt1.y;
cx1=pt2.x-pt1.x;
cy1=pt2.y-pt1.y;
uFlags=0L;

switch (iAction)
{
/*
* Each individual property modifies the appropriate
* variable x1, y1, cx1, cy1, as well as uFlags to set
* up SetWindowPos call.
*/
case MOVESIZEACTION_LEFT:
x1=x;
uFlags=SWP_NOSIZE;
break;

case MOVESIZEACTION_TOP:
y1=y;
uFlags=SWP_NOSIZE;
break;

case MOVESIZEACTION_WIDTH:
cx1=cx;
uFlags=SWP_NOMOVE;
break;

case MOVESIZEACTION_HEIGHT:
cy1=cy;
uFlags=SWP_NOMOVE;
break;


case MOVESIZEACTION_GETLEFT:
return x1;

case MOVESIZEACTION_GETTOP:
return y1;

case MOVESIZEACTION_GETWIDTH:
return cx1;

case MOVESIZEACTION_GETHEIGHT:
return cy1;

default:
return 0;
}

//We only get here on propety changes
SetWindowPos(m_pDoc->Window(), NULL
, (int)x1, (int)y1, (int)cx1, (int)cy1
, SWP_NOZORDER | SWP_NOACTIVATE | uFlags);

//Irrelevant for property changes.
return 0;
}