POLYLINE.C

/* 
* POLYLINE.C
*
* Window procedure for the polyline drawing window and support functions.
* This window is not complicated. On creation it allocates a block of
* memory for a POLYLINE structure that contains 20 POINTs. We do not
* attempt to reallocate this array at all just to maintain simplicity.
* This sample is to demonstrate OLE, not LocalReAlloc.
*
* Copyright(c) Microsoft Corp. 1992-1994 All Rights Reserved
* Win32 version, January 1994
*/


#include <windows.h>
#include "cosmo.h"

/*
* HACK: To fix some Invalid hDC rips, we need to supress certain
* operations on metafile DC's: GetMapMode, DPtoLP, LPtoDP.
* We use this flag to indicate supression.
*/
BOOL fMetaDC=FALSE;



/*
* HPolylineWindowCreate
*
* Purpose:
* Creates a Polyline window within the client area of hWndParent.
*
* Parameters:
* hWndParent HWND of the parent window.
* hInstance HINSTANCE of the application instance.
*
* Return Value:
* HWND Result of the CreateWindowEx call.
*
*/

HWND WINAPI HPolylineWindowCreate(HWND hWndParent, HINSTANCE hInstance)
{
RECT rc;
HWND hWndT;

/*
* Create the secondary window for this application in a
* shrunk client area.
*/
GetClientRect(hWndParent, &rc);
InflateRect(&rc, -8, -8);

//Create the editor window.
hWndT=CreateWindowEx(WS_EX_NOPARENTNOTIFY, rgpsz[IDS_CLASSPOLYLINE]
, rgpsz[IDS_CLASSPOLYLINE], WS_CHILD | WS_VISIBLE, rc.left
, rc.top, rc.right-rc.left, rc.bottom-rc.top
, hWndParent, (HMENU)ID_POLYLINE, hInstance, NULL);

return hWndT;
}





/*
* PolylineWndProc
*
* Purpose:
* Window procedure for the polyline drawing window.
*
* Parameters:
* The standard.
*
* Return Value:
* Standard.
*/

LRESULT WINAPI PolylineWndProc(HWND hWnd, UINT iMsg
, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
HWND hWndParent;
HLOCAL hMem;
LPPOLYLINE ppl;
RECT rc;
DWORD dwRet=0L;

#ifdef WIN32
ppl=(LPPOLYLINE)(PSTR)GetWindowLong(hWnd, 0);
#else
ppl=(LPPOLYLINE)(PSTR)GetWindowWord(hWnd, 0);
#endif

if (WM_USER <= iMsg)
return LPolylineUserMessage(hWnd, iMsg, wParam, lParam, ppl);


switch (iMsg)
{

case WM_NCCREATE:
hMem=LocalAlloc(LPTR, CBPOLYLINE);

if (NULL==hMem)
return 0L;

#ifdef WIN32
SetWindowLong(hWnd, 0, (LONG)hMem);
#else
SetWindowWord(hWnd, 0, (WORD)hMem);
#endif
return DefWindowProc(hWnd, iMsg, wParam, lParam);


case WM_NCDESTROY:
#ifdef WIN32
hMem=(HLOCAL)GetWindowLong(hWnd, 0);
#else
hMem=(HLOCAL)GetWindowWord(hWnd, 0);
#endif
LocalFree(hMem);
return DefWindowProc(hWnd, iMsg, wParam, lParam);


case WM_CREATE:
//Stash away the current window rectangle.
GetClientRect(hWnd, &rc);
RECTTORECTS(rc, ppl->rc);

ppl->wVerMaj=VERSIONMAJOR;
ppl->wVerMin=VERSIONMINOR;
ppl->cPoints=0;
break;


case WM_PAINT:
hDC=BeginPaint(hWnd, &ps);

if (0!=ppl->cPoints)
{
ppl->fDrawEntire=TRUE;
PolylineDraw(hWnd, hDC, ppl);
}

EndPaint(hWnd, &ps);
break;

case WM_LBUTTONDOWN:
//Stop if we are already at the limit.
if (CPOLYLINEPOINTS==ppl->cPoints)
{
MessageBeep(0);
break;
}

//Stuff the new point in the array.
ppl->rgpt[ppl->cPoints].x=LOWORD(lParam);
ppl->rgpt[ppl->cPoints].y=HIWORD(lParam);

ppl->cPoints++;

//Draw the lines to this new point only.
hDC=GetDC(hWnd);

ppl->fDrawEntire=FALSE;
PolylineDraw(hWnd, hDC, ppl);

ReleaseDC(hWnd, hDC);

hWndParent=GetParent(hWnd);

#ifdef WIN32
SendMessage(hWndParent, WM_COMMAND
, MAKELONG(ID_POLYLINE, PLN_POINTCHANGE), (LPARAM)hWnd);
#else
SendMessage(hWndParent, WM_COMMAND
, ID_POLYLINE, MAKELONG(hWnd, PLN_POINTCHANGE));
#endif
break;


default:
dwRet=DefWindowProc(hWnd, iMsg, wParam, lParam);
break;
}

return dwRet;
}







/*
* LPolylineUserMessage
*
* Purpose:
* Handles all window-specific messages WM_USER and greater,
* for the Polyline window:
*
* PLM_RECTSET: Changes the size of the window and scales the
* data points.
*
* PLM_POLYLINESET: Sets the current data points and the rectangle
* used to generate the figure.
*
* PLM_POLYLINEGET: Retrieves the current data points and rectangle
* used to generate the figure.
*
* PLM_POLYLINENEW: Resets the data points to defaults, meaning
* a blank figure.
*
* PLM_BACKUPUNDO: Backs the figure up one point.
*
* PLM_BITMAPGET: Retrieves a bitmap (DDB) of the current image.
*
* PLM_METAFILEGET: Retrieves a metafile for the current image.
*
* PLM_METAFILEPICTGET:Retrieves a METAFILEPICT structure of the image for
* use in clipboard I/O.
*
* Parameters:
* hWnd HWND of the Polyline window.
* iMsg UINT message to process.
* wParam WPARAM parameter of the message.
* lParam LPARAM pameter of the message.
* ppl LPPOLYLINE to the window's extra data structure.
*
* Return Value:
* DWORD Value to return from the window procedure
* that recieved the message.
*/

DWORD PASCAL LPolylineUserMessage(HWND hWnd, UINT iMsg, WPARAM wParam
, LPARAM lParam, LPPOLYLINE ppl)
{
DWORD dwRet=0L;
HWND hWndParent;
HBITMAP hBmp, hBmpT;
HDC hDC, hMemDC;
LPPOLYLINE pplT;
LPMETAFILEPICT pMF;
HGLOBAL hMem;
HMETAFILE hMF;
RECT rc;
LPRECT pRect;
UINT i;
LONG l, cx, cy, cxT, cyT;

hWndParent=GetParent(hWnd);

switch (iMsg)
{
case PLM_RECTSET:
/*
* Resize the window to the given size, letting WM_SIZE handlers
* take care of the rest.
*/
pRect=(LPRECT)lParam;

/*
* Scale all the current points to new dimensions. ppl->rc
* has the old dimensions, pRect points to the new. We
* force each of cx and cy to 1 if they are zero to prevent
* exceptions.
*/

RECTSTORECT(ppl->rc, rc);
cxT=rc.right - rc.left;
cyT=rc.bottom - rc.top;

RECTTORECTS(ppl->rc, *pRect);
cx=pRect->right - pRect->left;
cy=pRect->bottom - pRect->top;

//Prevent crashes
if (0L==cxT)
cxT=1;

if (0L==cyT)
cyT=1;

//Loop through each point, scaling if necessary.
for (i=0; i< ppl->cPoints; i++)
{
//Must use DWORD to insure proper scaling.
if (cx!=cxT)
{
l=((LONG)ppl->rgpt[i].x*cx);
ppl->rgpt[i].x=(short)(l/cxT);
}

if (cy!=cyT)
{
l=((LONG)ppl->rgpt[i].y*cy);
ppl->rgpt[i].y=(short)(l/cyT);
}
}


SetWindowPos(hWnd, NULL, pRect->left, pRect->top, (int)cx
, (int)cy, SWP_NOMOVE | SWP_NOZORDER);


//Check if we need to notify the parent.
if (0!=wParam)
{
#ifdef WIN32
SendMessage(hWndParent, WM_COMMAND
, MAKELONG(ID_POLYLINE, PLN_SIZECHANGE), (LPARAM)hWnd);
#else
SendMessage(hWndParent, WM_COMMAND
, ID_POLYLINE, MAKELONG(hWnd, PLN_SIZECHANGE));
#endif
}

//Insure repaint
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;


case PLM_POLYLINESET:
/*
* Copy the structure in lParam to ppl and repaint to
* reflect the new point set. Note that unlike the
* PLM_RECTSET message, we do no scaling, assuming that
* the rectangle in the POLYLINE structure is appropriate
* for the data.
*/
pplT=(LPPOLYLINE)lParam;
*ppl=*pplT;

//Resize this window to fit the data and notify the parent.
SetWindowPos(hWnd, NULL, ppl->rc.left, ppl->rc.top
, ppl->rc.right-ppl->rc.left, ppl->rc.bottom-ppl->rc.top
, SWP_NOMOVE | SWP_NOZORDER);

if (0!=wParam)
{
#ifdef WIN32
SendMessage(hWndParent, WM_COMMAND
, MAKELONG(ID_POLYLINE, PLN_SIZECHANGE), (LPARAM)hWnd);
#else
SendMessage(hWndParent, WM_COMMAND
, ID_POLYLINE, MAKELONG(hWnd, PLN_SIZECHANGE));
#endif
}

//Insure a repaint.
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;


case PLM_POLYLINEGET:
//Copy the structure in ppl to lParam. No repaint needed.
pplT=(LPPOLYLINE)lParam;
*pplT=*ppl;
break;


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

ppl->cPoints=0;

InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
break;


case PLM_BACKUPUNDO:
//Decrement the number of active points and repaint.
if (ppl->cPoints > 0)
{
ppl->cPoints--;
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}

//Notify parent window of the change.
#ifdef WIN32
SendMessage(hWndParent, WM_COMMAND
, MAKELONG(ID_POLYLINE, PLN_POINTCHANGE), (LPARAM)hWnd);
#else
SendMessage(hWndParent, WM_COMMAND
, ID_POLYLINE, MAKELONG(hWnd, PLN_POINTCHANGE));
#endif
break;


case PLM_BITMAPGET:
/*
* Create and return a bitmap of the window contents.
* The bitmap is the size of the POLYLINE edit window.
*/

hDC=GetDC(hWnd);
hMemDC=CreateCompatibleDC(hDC);

GetClientRect(hWnd, &rc);
hBmp=CreateCompatibleBitmap(hDC, rc.right, rc.bottom);

if (NULL!=hBmp)
{
//Draw the POLYLINE into the bitmap.
hBmpT=SelectObject(hMemDC, hBmp);

ppl->fDrawEntire=TRUE;
PolylineDraw(hWnd, hMemDC, ppl);
ppl->fDrawEntire=FALSE;

SelectObject(hMemDC, hBmpT);
}

DeleteDC(hMemDC);
ReleaseDC(hWnd, hDC);

//Return the created bitmap handle.
dwRet=(DWORD)(UINT)hBmp;
break;


case PLM_METAFILEGET:
//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.
*/
fMetaDC=TRUE;
SetMapMode(hDC, MM_ANISOTROPIC);
GetClientRect(hWnd, &rc);
SetWindowOrgEx(hDC, 0, 0, NULL);
SetWindowExtEx(hDC, rc.right, rc.bottom, NULL);

ppl->fDrawEntire=TRUE;
PolylineDraw(hWnd, hDC, ppl);
ppl->fDrawEntire=FALSE;

hMF=CloseMetaFile(hDC);
fMetaDC=FALSE;
}

dwRet=(DWORD)(UINT)hMF;
break;


case PLM_METAFILEPICTGET:
/*
* Create a METAFILEPICT structure for the clipboard.
* First attempt to get a metafile.
*/
lParam=SendMessage(hWnd, PLM_METAFILEGET, 0, 0L);
hMF=(HMETAFILE)(UINT)lParam;

if (NULL==hMF)
break;

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

if (NULL==hMem)
{
DeleteMetaFile(hMF);
break;
}

/*
* 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; //Required by OLE libraries.

//Insert the extents in MM_HIMETRIC units.
GetClientRect(hWnd, &rc);

RectConvertToHiMetric(hWnd, &rc); //Found in CLIP.C
pMF->xExt=rc.right;
pMF->yExt=rc.bottom;

GlobalUnlock(hMem);

dwRet=(DWORD)(UINT)hMem;
break;


default:
break;
}

return dwRet;
}






/*
* PolylineDraw
*
* Purpose:
* Paints the current line in the polyline window.
*
* Parameters:
* hWnd HWND of the polyline window.
* hDC HDC to draw on, could be a metafile or printer DC.
* ppl LPPOLYLINE to the polyline structure.
*
* Return Value:
* none
*/

void PASCAL PolylineDraw(HWND hWnd, HDC hDC, LPPOLYLINE ppl)
{

HBRUSH hBrush, hBrushT;
HPEN hPen, hPenT;
UINT i, j;
UINT nMM;
POINTS pt;
POINT rgpt[CPOLYLINEPOINTS];
RECT rc;
COLORREF cr;


GetClientRect(hWnd, &rc);

//Get the line color.
cr=GetSysColor(COLOR_WINDOWTEXT);

//Make a 32-bit working copy of the point array for DPtoLP.
for (i=0; i < ppl->cPoints; i++)
{
rgpt[i].x=ppl->rgpt[i].x;
rgpt[i].y=ppl->rgpt[i].y;
}

/*
* If the mapping mode is not MM_TEXT, convert the points to
* whatever mapping mode in in effect before drawing.
* This specifically supports metafiles in MM_ANISOTROPIC.
*/
nMM=fMetaDC ? MM_TEXT : GetMapMode(hDC);

if (MM_TEXT!=nMM)
DPtoLP(hDC, rgpt, ppl->cPoints);

hPen=CreatePen(PS_SOLID, 1, cr);
hPenT=SelectObject(hDC, hPen);

hBrush=CreateSolidBrush(GetSysColor(COLOR_WINDOW));
hBrushT=SelectObject(hDC, hBrush);

/*
* Either draw the entire figure or just a single point. The
* entire figure also includes erasing the background completely,
* since hDC may be a metafile DC. Drawing a single point just
* updates the figure for that new point.
*/
if (ppl->fDrawEntire)
{
//Erase the background for bitmaps and metafiles.
SelectObject(hDC, GetStockObject(NULL_PEN));
Rectangle(hDC, rc.left, rc.top, rc.right+1, rc.bottom+1);
SelectObject(hDC, hPen);


/*
* If we are drawing the entire figure, then loop through each
* point drawing a line to each successive point.
*/

for (i=0; i < ppl->cPoints; i++)
{
for (j=i; j < ppl->cPoints; j++)
{
MoveToEx(hDC, rgpt[i].x, rgpt[i].y, NULL);
LineTo(hDC, rgpt[j].x, rgpt[j].y);
}
}
}
else
{
/*
* If we are only drawing the last point, just cycle once
* through previous points.
*/

//Get the last point entered in the array.
j=ppl->cPoints-1;
pt.x=(short)rgpt[j].x;
pt.y=(short)rgpt[j].y;

for (i=0; i < j; i++)
{
MoveToEx(hDC, pt.x, pt.y, NULL);
LineTo(hDC, rgpt[i].x, rgpt[i].y);
}
}

//If we only had one point, draw a dot to indicate it's position.
if (1==ppl->cPoints)
SetPixel(hDC, rgpt[0].x, rgpt[0].y, cr);


SelectObject(hDC, hPenT);
SelectObject(hDC, hBrushT);
DeleteObject(hBrush);
DeleteObject(hPen);
return;
}