home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
JIGSAW.ZIP
/
JIGSAW.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-05-02
|
107KB
|
3,192 lines
/********************************** Jigsaw ************************************/
/* */
/* Created 1988, Microsoft Corporation. */
/* */
/* Purpose: To illustrate the use of Gpi. */
/* */
/* Summary: This program provides a jigsaw puzzle, based on a decomposition */
/* of an arbitrary bitmap loaded from a file. The user can jumble the */
/* pieces, then drag them individually by means of the mouse. The image */
/* can be zoomed in and out and scrolled up/down and left/right. */
/* */
/* JIGSAW uses BitBlt with clip paths to create a collection of picture */
/* fragments which are the puzzle pieces. Each piece is associated with */
/* with a single retained segment and an auxiliary data structure, used */
/* for drawing and for selection with the mouse. To retain responsiveness */
/* to user requests, the real work is done in a second thread, with */
/* work requests transmitted from the main thread in the form of */
/* messages. This arrangement makes it possible for the user to over- */
/* ride lengthy drawing operations with a higher-priority request */
/* (eg. program termination, magnification change, etc.). */
/* */
/* Individual pieces are made to "move" by changing their model transforms. */
/* Scrolling and zooming of the whole picture is done by changing the */
/* default viewing transform. */
/* */
/* */
/******************************************************************************/
#define INCL_BITMAPFILEFORMAT
#define INCL_DOSINFOSEG
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSMEMMGR
#define INCL_DEV
#define INCL_WINWINDOWMGR
#define INCL_WINMESSAGEMGR
#define INCL_WININPUT
#define INCL_WINRECTANGLES
#define INCL_WINPOINTERS
#define INCL_WINMENUS
#define INCL_WINSCROLLBARS
#define INCL_WINFRAMEMGR
#define INCL_WINSWITCHLIST
#define INCL_WINSYS
#define INCL_GPIBITMAPS
#define INCL_GPICONTROL
#define INCL_GPITRANSFORMS
#define INCL_GPIPRIMITIVES
#define INCL_GPIPATHS
#define INCL_GPIREGIONS
#define INCL_GPISEGMENTS
#define INCL_GPICORRELATION
#define INCL_GPILCIDS
#define INCL_ERRORS
#include <os2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <opendlg.h>
#include "jigsaw.h"
/*----------------------- helper macros --------------------------------------*/
#define ROUND_DOWN_MOD( arg, mod) \
{ \
if( arg < 0) \
{ \
arg -= mod - 1; \
arg += arg % mod; \
} else \
arg -= arg % mod; \
}
#define ROUND_UP_MOD( arg, mod) \
{ \
if( arg < 0) \
{ \
arg -= arg % mod; \
} else \
{ \
arg += mod - 1; \
arg -= arg % mod; \
} \
}
/*----------------------- inter-thread messages ------------------------------*/
#define UM_DIE WM_USER+1 /* instruct async thread to terminate */
#define UM_DRAW WM_USER+2 /* draw the current picture */
#define UM_VSCROLL WM_USER+3 /* perform scroll by recalculating the */
/* default viewing transform */
#define UM_HSCROLL WM_USER+4 /* perform scroll by recalculating the */
/* default viewing transform */
#define UM_SIZING WM_USER+5 /* perform sizing by recalculating the */
/* default viewing transform */
#define UM_ZOOM WM_USER+6 /* zoom the picture by recalculating */
/* the default viewing transform */
#define UM_REDRAW WM_USER+8 /* redraw the entire client window */
#define UM_JUMBLE WM_USER+9 /* scatter the pieces on the window */
#define UM_LOAD WM_USER+10 /* load a bitmap from disk */
#define UM_LEFTDOWN WM_USER+11 /* correlate and prepare to drag */
#define UM_MOUSEMOVE WM_USER+12 /* remove, reposition, and redraw */
#define UM_LEFTUP WM_USER+13 /* stop dragging */
#define UM_LAST WM_USER+15
/*------------------------- correlation parameters ---------------------------*/
#define HITS 1L /* maximum number of hits to return */
#define DEPTH 2L /* max depth of seg calls to return */
/*-------------------------- general definitions -----------------------------*/
HAB habMain=NULL; /* main thread anchor block handle */
HMQ hmqMain=NULL; /* main thread queue handle */
HWND hwndFrame=NULL; /* frame control handle */
HWND hwndClient=NULL; /* client area handle */
HDC hdcClient=NULL; /* window dc handle */
HPS hpsClient=NULL; /* client area Gpi ps handle */
SIZEL sizlMaxClient; /* max client area size */
HPS hpsPaint=NULL; /* ps for use in Main Thread */
HRGN hrgnInvalid = NULL; /* handle to the invalid region */
HAB habAsync=NULL; /* async thread anchor block handle */
HMQ hmqAsync=NULL; /* async thread queue handle */
TID tidAsync; /* async thread id */
SEL selStack; /* async thread stack selector */
#define STACKSIZE 4096 /* async thread stack size */
SHORT sPrty = -1; /* async thread priority */
HWND hwndHorzScroll=NULL; /* horizontal scroll bar window */
HWND hwndVertScroll=NULL; /* vertical scroll bar window */
POINTS ptsScrollPos, ptsOldScrollPos;
POINTS ptsScrollMax, ptsHalfScrollMax;
POINTS ptsScrollLine;
POINTS ptsScrollPage;
#define UNITY 65536L
MATRIXLF matlfIdentity = { UNITY, 0, 0, 0, UNITY, 0, 0, 0, 1 };
LONG lScale; /* current zoom level */
POINTL ptlScaleRef; /* scale factor, detects size change */
#define ZOOM_MAX 8
#define ZOOM_IN 1
#define ZOOM_OUT -1
POINTL ptlOffset;
POINTL ptlBotLeft = { 0, 0};
POINTL ptlTopRight = { 3000, 3000};
POINTL ptlMoveStart; /* model space point at start of move */
LONG lLastSegId; /* last segment id assigned to a piece */
LONG lPickedSeg = 0L; /* seg id of piece selected for drag */
POINTL ptlOffStart; /* segment xform xlate at move start */
RECTL rclBounds; /* pict bounding box in model coords. */
POINTL ptlOldMouse = {0L, 0L}; /* current mouse posn */
BOOL fButtonDownMain = FALSE; /* only drag if mouse down */
BOOL fButtonDownAsync = FALSE; /* only drag if mouse down */
POINTL ptlUpdtRef;
POINTL aptlUpdt[3];
BOOL fUpdtFirst;
BOOL fFirstLoad = TRUE;
/*-------------------------- segment list ------------------------------------*/
typedef struct _SEGLIST { /* sl */
LONG lSegId;
struct _SEGLIST FAR * pslPrev;
struct _SEGLIST FAR * pslNext;
POINTL ptlLocation; /* piece location, world coordinates */
RECTL rclCurrent; /* segment bounding box, model coords */
RECTL rclBitBlt; /* segment bounding box, world coords */
POINTL aptlBitBlt[4]; /* BitBlt parameters */
LONG lAdjacent[8]; /* adjacent segments in original pict */
struct _SEGLIST FAR * pslNextIsland; /* next member in island list */
BOOL fIslandMark; /* used for marking island members */
POINTL aptlSides[12]; /* control points for piece outline */
HDC hdcHole; /* used to punch hole in target */
HPS hpsHole;
HBITMAP hbmHole;
HDC hdcFill; /* used to fill hole */
HPS hpsFill;
HBITMAP hbmFill;
POINTL ptlModelXlate; /* lM31 and lM31 for piece */
} SEGLIST ;
typedef SEGLIST FAR *PSEGLIST; /* psl */
typedef PSEGLIST FAR *PPSEGLIST; /* ppsl */
PSEGLIST pslHead = NULL; /* head of the list */
PSEGLIST pslTail = NULL; /* tail of the list */
PSEGLIST pslPicked = NULL; /* picked segment's list member */
#define ADD_HEAD_SEG 1
#define ADD_TAIL_SEG 2
#define DEL_SEG 3
#define MAKE_TAIL_SEG 4
/*-------------------------- bitmap-related data -----------------------------*/
typedef struct _LOADINFO { /* li */
HFILE hf;
CHAR szFileName[MAX_FNAME_LEN];
} LOADINFO ;
typedef LOADINFO FAR *PLOADINFO; /* pli */
HPS hpsBitmapFile=NULL; /* bitmap straight from the file */
HDC hdcBitmapFile=NULL;
HBITMAP hbmBitmapFile=NULL;
BITMAPINFOHEADER bmpBitmapFile = {12L, 0, 0, 0, 0};
HPS hpsBitmapSize=NULL; /* bitmap sized to the current size */
HDC hdcBitmapSize=NULL;
HBITMAP hbmBitmapSize=NULL;
HPS hpsBitmapBuff=NULL; /* image composed here, copied to scrn */
HDC hdcBitmapBuff=NULL;
HBITMAP hbmBitmapBuff=NULL;
HPS hpsBitmapSave=NULL; /* save part of screen during dragging */
HDC hdcBitmapSave=NULL;
HBITMAP hbmBitmapSave=NULL;
BITMAPINFOHEADER bmpBitmapSave = {12L, 0, 0, 0, 0};
DEVOPENSTRUC dop = { NULL
, "DISPLAY"
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL };
/*-------------------------- old-style bitmap header -------------------------*/
typedef struct {
USHORT wType;
ULONG dwSize;
int xHotspot;
int yHotspot;
ULONG dwBitsOffset;
USHORT bmWidth;
USHORT bmHeight;
USHORT bmPlanes;
USHORT bmBitcount;
} RCBITMAP;
typedef RCBITMAP FAR *PRCBITMAP;
/*--------------------------- Miscellaneous ----------------------------------*/
ULONG ulTerminateSem = 0; /* main thread blocks while async dies */
HSEM hsemTerminate = &ulTerminateSem;
ULONG ulSzFmt = 0; /* serializes access to sprintf() */
HSEM hsemSzFmt = &ulSzFmt;
CHAR szFmt[50]; /* buffer used by sprintf() */
ULONG ulDrawOn = 0; /* set iff draw enabled */
HSEM hsemDrawOn = &ulDrawOn;
PSZ pszLoadMsg = "Patience, preparing puzzle pieces ...";
ULONG ulLoadMsg = 0; /* set iff load in progress */
HSEM hsemLoadMsg = &ulLoadMsg;
SWCNTRL swctl = { 0, 0, 0, 0, 0, SWL_VISIBLE, SWL_JUMPABLE, NULL, 0 };
HSWITCH hsw; /* handle to a switch list entry */
char szTitle[80]; /* Title bar text */
BOOL fErrMem = FALSE; /* set if alloc async stack fails */
LONG lByteAlignX, lByteAlignY; /* memory alignment constants */
/*------------------------- Function Prototypes ------------------------------*/
VOID CalcBounds( VOID);
VOID CalcSize( MPARAM, MPARAM);
VOID CalcTransform( HWND);
MRESULT CALLBACK ClientWndProc( HWND, USHORT, MPARAM, MPARAM);
BOOL CreateBitmapHdcHps( HDC, HPS);
BOOL CreateThread( VOID);
BOOL CreatePicture( VOID);
BOOL DoDraw( HPS, HRGN, BOOL);
VOID DoHorzScroll( VOID);
VOID DoVertScroll( VOID);
VOID DrawPiece( HPS, PSEGLIST, BOOL);
BOOL DumpPicture( VOID);
VOID Finalize( VOID);
BOOL Initialize( VOID);
VOID Jumble( VOID);
VOID LeftDown( MPARAM);
VOID LeftUp( VOID);
VOID Load( PLOADINFO);
VOID cdecl main( VOID);
VOID MarkIsland( PSEGLIST, BOOL);
VOID MessageInt( HWND, INT, PCH);
VOID MouseMove( MPARAM);
VOID MyMessageBox( HWND, PSZ);
VOID FAR NewThread( VOID);
BOOL PrepareBitmap( VOID);
BOOL ReadBitmap( HFILE);
VOID Redraw( VOID);
VOID ReportError( HAB);
BOOL SegListCheck( INT);
PSEGLIST SegListGet( LONG);
PSEGLIST SegListUpdate( USHORT, PSEGLIST);
BOOL SendCommand( USHORT, MPARAM, MPARAM);
VOID SetDVTransform( FIXED, FIXED, FIXED, FIXED, LONG, LONG, LONG);
VOID SetRect( PSEGLIST);
VOID ShowMessage( HPS, PSZ);
VOID ToggleMenuItem( USHORT, USHORT, PBOOL);
MRESULT WndProcCommand( HWND, USHORT, MPARAM, MPARAM);
MRESULT WndProcCreate( HWND);
MRESULT WndProcPaint( VOID);
MRESULT WndProcSize( MPARAM, MPARAM);
VOID Zoom( SHORT);
VOID ZoomMenuItems( VOID);
/******************************************************************************/
/* */
/* MyMessageBox */
/* */
/* Displays a message box with the given string. To simplify matters, */
/* the box will always have the same title ("Jigsaw"), will always */
/* have a single button ("Ok"), will always have an exclamation point */
/* icon, and will always be application modal. */
/* */
/******************************************************************************/
VOID
MyMessageBox( hWnd, psz)
HWND hWnd;
PSZ psz;
{
WinMessageBox( HWND_DESKTOP
, hWnd
, psz
, szTitle
, NULL
, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL );
}
/******************************************************************************/
/* */
/* Main thread will initialize the process for PM services and process */
/* the application message queue until a WM_QUIT message is received. It will */
/* then destroy all PM resources and terminate. Any error during */
/* initialization will be reported and the process terminated. */
/* */
/******************************************************************************/
VOID cdecl
main()
{
QMSG qmsg;
if( Initialize())
while( WinGetMsg( habMain, &qmsg, NULL, NULL, NULL))
WinDispatchMsg( habMain, &qmsg);
else
ReportError( habMain);
Finalize();
}
/******************************************************************************/
/* */
/* The Initialize function will initialize the PM interface, */
/* create an application message queue, a standard frame window and a new */
/* thread to control drawing operations. It will also initialize static */
/* strings. */
/* */
/******************************************************************************/
BOOL
Initialize()
{
ULONG flCreate;
PID pid;
TID tid;
WinShowPointer( HWND_DESKTOP, TRUE);
habMain = WinInitialize( NULL);
if( !habMain)
return( FALSE);
hmqMain = WinCreateMsgQueue( habMain,0);
if( !hmqMain)
return( FALSE);
WinLoadString( habMain, NULL, TITLEBAR, sizeof(szTitle), szTitle);
if( !WinRegisterClass( habMain
, (PCH)szTitle
, (PFNWP)ClientWndProc
, CS_SIZEREDRAW
, 0 ))
return( FALSE);
flCreate = (FCF_STANDARD | FCF_VERTSCROLL | FCF_HORZSCROLL)
& ~(ULONG)FCF_TASKLIST;
hwndFrame = WinCreateStdWindow( HWND_DESKTOP
, WS_VISIBLE
, &flCreate
, szTitle
, szTitle
, WS_VISIBLE
, NULL
, APPMENU
, &hwndClient);
if( !hwndFrame)
return( FALSE);
WinQueryWindowProcess( hwndFrame, &pid, &tid);
swctl.hwnd = hwndFrame;
swctl.idProcess = pid;
lstrcpy( swctl.szSwtitle, szTitle);
hsw = WinAddSwitchEntry( &swctl);
ZoomMenuItems();
if( !CreateThread()) /* create async thread */
return ( FALSE);
if( !CreateBitmapHdcHps( &hdcBitmapFile, &hpsBitmapFile))
return( FALSE);
if( !CreateBitmapHdcHps( &hdcBitmapSize, &hpsBitmapSize))
return( FALSE);
if( !CreateBitmapHdcHps( &hdcBitmapBuff, &hpsBitmapBuff))
return( FALSE);
if( !CreateBitmapHdcHps( &hdcBitmapSave, &hpsBitmapSave))
return( FALSE);
return( TRUE);
}
/******************************************************************************/
/* */
/* Finalize will destroy the asynchronous drawing thread, all Presentation */
/* Manager resources, and terminate the process. */
/* */
/******************************************************************************/
VOID
Finalize()
{
if( tidAsync)
{
DosSemClear( hsemDrawOn);
DosSemSet( hsemTerminate);
}
while( pslHead != NULL )
{
GpiSetBitmap( pslHead->hpsFill, NULL);
GpiDeleteBitmap( pslHead->hbmFill);
GpiDestroyPS( pslHead->hpsFill);
DevCloseDC( pslHead->hdcFill);
GpiSetBitmap( pslHead->hpsHole, NULL);
GpiDeleteBitmap( pslHead->hbmHole);
GpiDestroyPS( pslHead->hpsHole);
DevCloseDC( pslHead->hdcHole);
SegListUpdate( DEL_SEG, pslHead);
}
GpiDeleteSegments( hpsClient, 1L, lLastSegId);
if( hrgnInvalid)
GpiDestroyRegion( hpsClient, hrgnInvalid);
if( hpsClient)
{
GpiAssociate( hpsClient, NULL);
GpiDestroyPS( hpsClient);
}
if( hpsPaint)
GpiDestroyPS( hpsPaint);
if( hpsBitmapFile)
{
GpiSetBitmap( hpsBitmapFile, NULL);
GpiDeleteBitmap( hbmBitmapFile);
GpiDestroyPS( hpsBitmapFile);
DevCloseDC( hdcBitmapFile);
}
if( hpsBitmapSize)
{
GpiSetBitmap( hpsBitmapSize, NULL);
GpiDeleteBitmap( hbmBitmapSize);
GpiDestroyPS( hpsBitmapSize);
DevCloseDC( hdcBitmapSize);
}
if( hpsBitmapBuff)
{
GpiSetBitmap( hpsBitmapBuff, NULL);
GpiDeleteBitmap( hbmBitmapBuff);
GpiDestroyPS( hpsBitmapBuff);
DevCloseDC( hdcBitmapBuff);
}
if( hpsBitmapSave)
{
GpiSetBitmap( hpsBitmapSave, NULL);
GpiDeleteBitmap( hbmBitmapSave);
GpiDestroyPS( hpsBitmapSave);
DevCloseDC( hdcBitmapSave);
}
if( hwndFrame)
WinDestroyWindow( hwndFrame);
if( hmqMain)
WinDestroyMsgQueue( hmqMain);
if( habMain)
WinTerminate( habMain);
DosExit( EXIT_PROCESS, 0);
}
/******************************************************************************/
/* */
/* ReportError will display the latest error information for the required */
/* thread. No resources to be loaded if out of memory error. */
/* */
/******************************************************************************/
VOID
ReportError( hab)
HAB hab;
{
PERRINFO perriBlk;
PSZ pszErrMsg;
USHORT * TempPtr;
if( !hwndFrame)
return;
if( !fErrMem)
{
perriBlk = WinGetErrorInfo(hab);
if( !perriBlk)
return;
SELECTOROF( pszErrMsg) = SELECTOROF(perriBlk);
SELECTOROF( TempPtr) = SELECTOROF(perriBlk);
OFFSETOF( TempPtr) = perriBlk->offaoffszMsg;
OFFSETOF( pszErrMsg) = *TempPtr;
WinMessageBox( HWND_DESKTOP
, hwndFrame
, pszErrMsg
, szTitle
, 0
, MB_CUACRITICAL | MB_ENTER);
WinFreeErrorInfo( perriBlk);
} else
WinMessageBox( HWND_DESKTOP
, hwndFrame
, "ERROR - Out Of Memory"
, szTitle
, 0
, MB_CUACRITICAL | MB_ENTER);
}
/******************************************************************************/
/* */
/* CreateThread creates the asynchronous drawing thread. It will allocate */
/* stack space and create the thread. */
/* */
/******************************************************************************/
BOOL
CreateThread()
{
PBYTE pbAsyncStack; /* long pointer to stack for new thread */
if( DosAllocSeg( STACKSIZE, (PSEL)&selStack, 0 ))
{
fErrMem = TRUE;
return( FALSE);
}
OFFSETOF(pbAsyncStack) = STACKSIZE-2;
SELECTOROF(pbAsyncStack) = selStack;
if( DosCreateThread( (PFNTHREAD)NewThread, &tidAsync, pbAsyncStack ))
return( FALSE);
return( TRUE);
}
/******************************************************************************/
/* */
/* SendCommand will attempt to post the required command and parameters to */
/* the asynchronous drawing thread's message queue. The command will only */
/* be posted if the queue exists. */
/* */
/******************************************************************************/
BOOL
SendCommand( usCommand, mp1, mp2)
USHORT usCommand;
MPARAM mp1, mp2;
{
if( !hmqAsync)
return( FALSE);
switch( usCommand)
{
case UM_DIE:
case UM_LEFTDOWN:
case UM_LEFTUP:
case UM_MOUSEMOVE:
case UM_DRAW:
case UM_HSCROLL:
case UM_VSCROLL:
case UM_ZOOM:
case UM_REDRAW:
case UM_SIZING:
case UM_JUMBLE:
case UM_LOAD:
return( WinPostQueueMsg( hmqAsync
, usCommand
, mp1
, mp2 ) );
break;
default:
return( TRUE);
}
}
/******************************************************************************/
/* */
/* ClientWndProd is the window procedure associated with the client window. */
/* */
/******************************************************************************/
MRESULT CALLBACK
ClientWndProc( hwnd, msg, mp1, mp2)
HWND hwnd;
USHORT msg;
MPARAM mp1;
MPARAM mp2;
{
CHAR szTemp[128];
switch( msg)
{
case WM_CREATE:
return( WndProcCreate( hwnd));
break;
case WM_CLOSE:
WinLoadString( habMain, NULL, TERMINATE, sizeof(szTemp), (PSZ)szTemp );
if( WinMessageBox( HWND_DESKTOP
, hwndFrame
, szTemp
, szTitle
, 0
, MB_CUAWARNING | MB_YESNO | MB_DEFBUTTON2)
== MBID_YES)
WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
break;
case WM_PAINT:
return( WndProcPaint());
break;
/**************************************************************************/
/* */
/**************************************************************************/
case WM_ERASEBACKGROUND:
return( TRUE);
break;
/**************************************************************************/
/* */
/**************************************************************************/
case WM_MINMAXFRAME:
if( (((PSWP)mp1)->fs & SWP_RESTORE) ||
(((PSWP)mp1)->fs & SWP_MAXIMIZE) )
{
DosSemClear( hsemDrawOn);
SendCommand( UM_SIZING, 0, 0);
}
break;
/**************************************************************************/
/* Process menu item commands, and commands generated from the keyboard */
/* via the accelerator table. Most are handled by the async thread */
/**************************************************************************/
case WM_COMMAND:
return( WndProcCommand( hwnd, msg, mp1, mp2));
break;
/**************************************************************************/
/* Scrolling is handled by the async drawing thread. Simply pass on the */
/* command and parameters */
/**************************************************************************/
case WM_HSCROLL:
SendCommand( UM_HSCROLL, mp2, 0);
break;
case WM_VSCROLL:
SendCommand( UM_VSCROLL, mp2, 0);
break;
/************************************************************************/
/* The client area is being resized. */
/************************************************************************/
case WM_SIZE:
DosSemClear( hsemDrawOn);
SendCommand( UM_SIZING, mp1, mp2);
break;
/**************************************************************************/
/* Mouse commands are handled by the async thread. Simply send on the */
/* command and parameters. */
/**************************************************************************/
case WM_BUTTON1DBLCLK:
case WM_BUTTON1DOWN:
if( hwnd != WinQueryFocus( HWND_DESKTOP, FALSE))
WinSetFocus( HWND_DESKTOP, hwnd);
if( !fButtonDownMain)
{
fButtonDownMain = TRUE;
SendCommand( UM_LEFTDOWN, mp1, 0);
}
return( TRUE);
break;
case WM_BUTTON1UP:
if( !fButtonDownMain)
return( TRUE);
if( SendCommand( UM_LEFTUP, mp1, 0))
fButtonDownMain = FALSE;
else
WinAlarm( HWND_DESKTOP, WA_WARNING);
return( TRUE);
break;
case WM_MOUSEMOVE:
if( fButtonDownMain)
SendCommand( UM_MOUSEMOVE, mp1, 0);
return( WinDefWindowProc( hwnd, msg, mp1, mp2));
break;
/**************************************************************************/
/* Default for the rest */
/**************************************************************************/
default:
return( WinDefWindowProc( hwnd, msg, mp1, mp2));
}
return( FALSE);
}
/******************************************************************************/
/* */
/* Get the maximum client area size. Create a window DC for the client */
/* area and a normal GPI Presentation Space and associate the two. The GPI */
/* PS will be the maximum client area size and be in pels. */
/* */
/******************************************************************************/
MRESULT
WndProcCreate( hwnd)
HWND hwnd;
{
SIZEL sizlPickApp; /* pick aperture size */
sizlMaxClient.cx = WinQuerySysValue( HWND_DESKTOP, SV_CXFULLSCREEN);
sizlMaxClient.cy = WinQuerySysValue( HWND_DESKTOP, SV_CYFULLSCREEN);
lByteAlignX = WinQuerySysValue( HWND_DESKTOP, SV_CXBYTEALIGN);
lByteAlignY = WinQuerySysValue( HWND_DESKTOP, SV_CYBYTEALIGN);
hdcClient = WinOpenWindowDC( hwnd);
hpsClient = GpiCreatePS( habMain
, hdcClient
, &sizlMaxClient
, GPIA_ASSOC | PU_PELS );
if( !hpsClient)
return( TRUE);
GpiSetAttrMode( hpsClient, AM_PRESERVE);
hwndHorzScroll = WinWindowFromID( WinQueryWindow( hwnd, QW_PARENT, FALSE)
, FID_HORZSCROLL);
hwndVertScroll = WinWindowFromID( WinQueryWindow( hwnd, QW_PARENT, FALSE)
, FID_VERTSCROLL);
hpsPaint = GpiCreatePS( habMain, NULL, &sizlMaxClient, PU_PELS);
hrgnInvalid = GpiCreateRegion( hpsClient, 0L, NULL);
sizlPickApp.cx = sizlPickApp.cy = 1;
GpiSetPickApertureSize( hpsClient, PICKAP_REC, &sizlPickApp);
return( FALSE);
}
/******************************************************************************/
/* */
/* WM_PAINT message */
/* */
/******************************************************************************/
MRESULT
WndProcPaint()
{
HRGN hrgnUpdt;
SHORT sRgnType;
hrgnUpdt = GpiCreateRegion( hpsPaint, 0L, NULL);
sRgnType = WinQueryUpdateRegion( hwndClient, hrgnUpdt);
SendCommand( UM_DRAW, (MPARAM)hrgnUpdt, 0);
if( DosSemWait( hsemLoadMsg, SEM_IMMEDIATE_RETURN))
ShowMessage( hpsClient, pszLoadMsg);
WinValidateRegion( hwndClient, hrgnUpdt, FALSE);
return( FALSE);
}
/******************************************************************************/
/* Process menu item commands, and commands generated from the keyboard via */
/* the accelerator table. Most are handled by the async thread */
/******************************************************************************/
MRESULT
WndProcCommand( hwnd, msg, mp1, mp2)
HWND hwnd;
USHORT msg;
MPARAM mp1, mp2;
{
CHAR szTemp[128];
DLF dlf;
SEL sel;
PLOADINFO pli;
PSZ pszError, psz1, psz2;
switch( SHORT1FROMMP(mp1))
{
case MENU_JUMBLE:
DosSemClear( hsemDrawOn);
SendCommand( UM_JUMBLE, 0, 0);
break;
case MENU_LOAD:
DosAllocSeg( sizeof( LOADINFO), &sel, 0);
pli = MAKEP( sel, 0);
dlf.rgbAction = DLG_OPENDLG;
dlf.rgbFlags = ATTRDIRLIST;
dlf.phFile = &(pli->hf);
dlf.pszExt = (PSZ)"\\*.bmp";
dlf.pszAppName = szTitle;
dlf.pszTitle = "Load Bitmap";
dlf.pszInstructions = NULL;
dlf.szFileName[0] = '\0';
dlf.szOpenFile[0] = '\0';
pszError = "Error reading file.";
switch( DlgFile( hwnd, &dlf))
{
case TDF_ERRMEM:
case TDF_INVALID:
MyMessageBox( hwnd, pszError);
break;
case TDF_NOOPEN:
break;
default:
for( psz1 = dlf.szFileName, psz2 = pli->szFileName
; *psz2++ = *psz1++
; )
;
DosSemClear( hsemDrawOn);
SendCommand( UM_LOAD, (MPARAM)pli, 0);
break;
}
break;
/**********************************************************************/
/* EXIT command, menu item or F3 key pressed. Give the operator a */
/* second chance, if confirmed post a WM_QUIT msg to the application */
/* msg queue. This will force the MAIN thread to terminate. */
/**********************************************************************/
case MENU_EXIT:
WinLoadString( habMain, NULL, TERMINATE, sizeof(szTemp), szTemp);
if( WinMessageBox( HWND_DESKTOP
, hwndFrame
, szTemp
, szTitle
, 0
, MB_CUAWARNING | MB_YESNO | MB_DEFBUTTON2)
== MBID_YES)
WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
break;
/**********************************************************************/
/* Pass on the rest to the async thread. */
/**********************************************************************/
case MENU_ZOOMIN:
DosSemClear( hsemDrawOn);
SendCommand( UM_ZOOM, (MPARAM)ZOOM_IN, 0);
break;
case MENU_ZOOMOUT:
DosSemClear( hsemDrawOn);
SendCommand( UM_ZOOM, (MPARAM)ZOOM_OUT , 0);
break;
/**********************************************************************/
/* Unrecognised => default */
/**********************************************************************/
default:
return( WinDefWindowProc(hwnd, msg, mp1, mp2));
}
return( FALSE);
}
/******************************************************************************/
/* Put a message on the screen */
/******************************************************************************/
VOID
ShowMessage( hps, psz)
HPS hps;
PSZ psz;
{
HRGN hrgn;
RECTL rclClient;
POINTL ptl;
WinQueryWindowRect( hwndClient, &rclClient);
hrgn = GpiCreateRegion( hps, 1L, &rclClient);
GpiSetColor( hps, CLR_BACKGROUND);
GpiPaintRegion( hps, hrgn);
GpiDestroyRegion( hps, hrgn);
GpiSetColor( hps, CLR_NEUTRAL);
ptl.x = 0L;
ptl.y = 0L;
GpiMove( hps, (PPOINTL)&ptl);
GpiCharString( hps, (LONG)strlen( psz), psz);
}
/******************************************************************************/
/* Load a bitmap */
/******************************************************************************/
VOID
Load( pli)
PLOADINFO pli;
{
PSZ pszError;
RECTL rclClient;
pszError = (PSZ)"Error reading file.";
ShowMessage( hpsClient, pszLoadMsg);
DosSemSet( hsemLoadMsg);
if( !fFirstLoad)
DumpPicture();
if( !ReadBitmap( pli->hf) )
{
MyMessageBox( hwndClient, pszError);
DosSemClear( hsemLoadMsg);
return;
}
if( !PrepareBitmap() )
{
MyMessageBox( hwndClient, pszError);
DosSemClear( hsemLoadMsg);
return;
}
lstrcpy( swctl.szSwtitle, szTitle);
lstrcat( swctl.szSwtitle, ": ");
lstrcat( swctl.szSwtitle, pli->szFileName);
WinChangeSwitchEntry( hsw, &swctl);
WinSetWindowText( hwndFrame, swctl.szSwtitle);
WinQueryWindowRect( hwndClient, &rclClient);
ptsScrollMax.x = (SHORT)(rclClient.xRight - rclClient.xLeft);
ptsHalfScrollMax.x = ptsScrollMax.x >> 1;
ptsScrollPage.x = ptsScrollMax.x >> 3;
ROUND_DOWN_MOD( ptsScrollPage.x, (SHORT)lByteAlignX);
ptsScrollLine.x = ptsScrollMax.x >> 5;
ROUND_DOWN_MOD( ptsScrollLine.x, (SHORT)lByteAlignX);
ptsScrollPos.x = ptsHalfScrollMax.x;
ptsOldScrollPos.x = ptsHalfScrollMax.x;
WinSendMsg( hwndHorzScroll
, SBM_SETSCROLLBAR
, MPFROMSHORT( ptsScrollPos.x)
, MPFROM2SHORT( 1, ptsScrollMax.x) );
ptsScrollMax.y = (SHORT)(rclClient.yTop - rclClient.yBottom);
ptsHalfScrollMax.y = ptsScrollMax.y >> 1;
ptsScrollPage.y = ptsScrollMax.y >> 3;
ROUND_DOWN_MOD( ptsScrollPage.y, (SHORT)lByteAlignY);
ptsScrollLine.y = ptsScrollMax.y >> 5;
ROUND_DOWN_MOD( ptsScrollLine.y, (SHORT)lByteAlignY);
ptsScrollPos.y = ptsHalfScrollMax.y;
ptsOldScrollPos.y = ptsHalfScrollMax.y;
WinSendMsg( hwndVertScroll
, SBM_SETSCROLLBAR
, MPFROMSHORT( ptsScrollPos.y)
, MPFROM2SHORT( 1, ptsScrollMax.y) );
CreatePicture();
lScale = 0;
CalcBounds();
ptlScaleRef.x = ptlScaleRef.y = 0L;
CalcTransform( hwndClient);
DosFreeSeg( SELECTOROF( pli));
fFirstLoad = FALSE;
DosSemClear( hsemLoadMsg);
}
/******************************************************************************/
/* Throw the pieces around the screen. */
/******************************************************************************/
VOID
Jumble()
{
LONG lWidth, lHeight;
DATETIME date;
POINTL ptl;
RECTL rclClient;
PSEGLIST psl;
MATRIXLF matlf;
if( WinQueryWindowRect( hwndClient, &rclClient) )
{
lWidth = rclClient.xRight - rclClient.xLeft;
lHeight = rclClient.yTop - rclClient.yBottom;
if( (lWidth > 0) && (lHeight > 0) )
{
matlf = matlfIdentity;
DosGetDateTime( &date);
srand( (USHORT)date.hundredths);
for( psl = pslHead; psl != NULL; psl = psl->pslNext)
{
psl->pslNextIsland = psl; /* reset island pointer */
psl->fIslandMark = FALSE; /* clear island mark */
ptl.x = rclClient.xLeft + (rand() % lWidth);
ptl.y = rclClient.yBottom + (rand() % lHeight);
GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptl);
ptl.x = 50 * (ptl.x / 50) - 250;
ptl.y = 50 * (ptl.y / 50) - 250;
matlf.lM31 = ptl.x - psl->ptlLocation.x;
matlf.lM32 = ptl.y - psl->ptlLocation.y;
GpiSetSegmentTransformMatrix( hpsClient
, psl->lSegId
, 9L
, &matlf
, TRANSFORM_REPLACE);
psl->ptlModelXlate.x = matlf.lM31;
psl->ptlModelXlate.y = matlf.lM32;
SetRect( psl);
}
}
}
}
/******************************************************************************/
/* */
/* NewThread is the asynchronous drawing thread. It is responsible for all */
/* drawing. It will initialize its PM interface and create an application */
/* message queue. It will then monitor its message queue and process any */
/* commands received. */
/* */
/******************************************************************************/
VOID FAR
NewThread()
{
QMSG qmsgAsync, qmsgPeek;
BOOL fDone;
SHORT sZoomArg;
POINTL aptlDraw[3];
/****************************************************************************/
/* Initialize the PM interface. If it fails, terminate both threads. */
/****************************************************************************/
habAsync = WinInitialize( NULL);
if( !habAsync)
{
WinPostMsg( hwndClient, WM_QUIT, NULL, NULL);
DosExit( EXIT_THREAD, 0);
}
/****************************************************************************/
/* Create a message queue. If it fails, terminate both threads. */
/****************************************************************************/
hmqAsync = WinCreateMsgQueue( habAsync, 150);
if( !hmqAsync)
{
WinPostMsg( hwndClient, WM_QUIT, NULL, NULL);
WinTerminate( habAsync);
DosExit( EXIT_THREAD, 0);
}
DosSetPrty( PRTYS_THREAD, PRTYC_NOCHANGE, sPrty, (TID)NULL);
while( TRUE)
{
WinGetMsg( habAsync, &qmsgAsync, NULL, 0, 0);
if( WinPeekMsg( habAsync, &qmsgPeek, NULL, UM_DIE, UM_DIE, PM_NOREMOVE))
qmsgAsync = qmsgPeek;
if( WinPeekMsg( habAsync, &qmsgPeek, NULL, UM_SIZING, UM_LOAD, PM_NOREMOVE))
DosSemClear( hsemDrawOn);
else
DosSemSet( hsemDrawOn);
/**************************************************************************/
/* process the commands */
/**************************************************************************/
switch( qmsgAsync.msg)
{
/************************************************************************/
/************************************************************************/
case UM_LOAD:
Load( (PLOADINFO)qmsgAsync.mp1);
Redraw();
break;
/************************************************************************/
case UM_JUMBLE:
Jumble();
Redraw();
break;
/************************************************************************/
case UM_REDRAW:
Redraw();
break;
/************************************************************************/
/* DRAW will use the passed region containing the invalidated area of */
/* the screen, repaint it and then destroy the region. */
/************************************************************************/
case UM_DRAW:
if( qmsgAsync.mp1)
{
DoDraw( hpsBitmapBuff, (HRGN)qmsgAsync.mp1, TRUE);
GpiQueryRegionBox( hpsClient, (HRGN)qmsgAsync.mp1, (PRECTL)aptlDraw);
GpiDestroyRegion( hpsClient, (HRGN)qmsgAsync.mp1);
WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlDraw, 3);
ROUND_DOWN_MOD( aptlDraw[0].x, lByteAlignX); /* round down */
ROUND_DOWN_MOD( aptlDraw[0].y, lByteAlignY); /* round down */
ROUND_UP_MOD( aptlDraw[1].x, lByteAlignX); /* round up */
ROUND_UP_MOD( aptlDraw[1].y, lByteAlignY); /* round up */
WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlDraw, 3);
aptlDraw[2] = aptlDraw[0];
GpiBitBlt( hpsClient
, hpsBitmapBuff
, 3L
, aptlDraw
, ROP_SRCCOPY
, BBO_IGNORE );
}
break;
/************************************************************************/
/* Get new scroll posn from command ( i.e. +/-1 +/-page) or new */
/* absolute position from parameter, update scroll posn, change the */
/* transform and update the thumb posn. Finally update the window. */
/************************************************************************/
case UM_HSCROLL:
switch( SHORT2FROMMP( qmsgAsync.mp1) )
{
case SB_LINEUP:
ptsScrollPos.x -= ptsScrollLine.x;
break;
case SB_LINEDOWN:
ptsScrollPos.x += ptsScrollLine.x;
break;
case SB_SLIDERTRACK:
case SB_SLIDERPOSITION:
for( fDone = FALSE; !fDone ;)
{
if( WinPeekMsg( habAsync
, &qmsgPeek
, NULL
, UM_HSCROLL
, UM_HSCROLL
, PM_NOREMOVE))
if( (SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERTRACK)
||(SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERPOSITION) )
WinPeekMsg( habAsync
, &qmsgAsync
, NULL
, UM_HSCROLL
, UM_HSCROLL
, PM_REMOVE);
else
fDone = TRUE;
else
fDone = TRUE;
}
ptsScrollPos.x = SHORT1FROMMP( qmsgAsync.mp1);
ROUND_DOWN_MOD( ptsScrollPos.x, (SHORT)lByteAlignX);
break;
case SB_PAGEUP:
ptsScrollPos.x -= ptsScrollPage.x;
break;
case SB_PAGEDOWN:
ptsScrollPos.x += ptsScrollPage.x;
break;
case SB_ENDSCROLL:
break;
default:
break;
}
DoHorzScroll();
break;
case UM_VSCROLL:
switch( SHORT2FROMMP( qmsgAsync.mp1) )
{
case SB_LINEUP:
ptsScrollPos.y -= ptsScrollLine.y;
break;
case SB_LINEDOWN:
ptsScrollPos.y += ptsScrollLine.y;
break;
case SB_SLIDERTRACK:
case SB_SLIDERPOSITION:
for( fDone = FALSE; !fDone ;)
{
if( WinPeekMsg( habAsync
, &qmsgPeek
, NULL
, UM_VSCROLL
, UM_VSCROLL
, PM_NOREMOVE))
if( (SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERTRACK)
||(SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERPOSITION) )
WinPeekMsg( habAsync
, &qmsgAsync
, NULL
, UM_VSCROLL
, UM_VSCROLL
, PM_REMOVE);
else
fDone = TRUE;
else
fDone = TRUE;
}
ptsScrollPos.y = SHORT1FROMMP( qmsgAsync.mp1);
ROUND_DOWN_MOD( ptsScrollPos.y, (SHORT)lByteAlignY);
break;
case SB_PAGEUP:
ptsScrollPos.y -= ptsScrollPage.y;
break;
case SB_PAGEDOWN:
ptsScrollPos.y += ptsScrollPage.y;
break;
case SB_ENDSCROLL:
break;
default:
break;
}
DoVertScroll();
break;
/************************************************************************/
/* the window is being resized */
/************************************************************************/
case UM_SIZING:
CalcBounds();
CalcTransform( hwndClient);
break;
/************************************************************************/
/* adjust zoom factor */
/************************************************************************/
case UM_ZOOM:
sZoomArg = (SHORT)qmsgAsync.mp1;
while( WinPeekMsg( habAsync
, &qmsgPeek
, NULL
, WM_USER
, UM_LAST
, PM_NOREMOVE))
{
if( qmsgPeek.msg == UM_ZOOM)
WinPeekMsg( habAsync
, &qmsgPeek
, NULL
, WM_USER
, UM_LAST
, PM_REMOVE);
else
break;
sZoomArg += (SHORT)qmsgPeek.mp1;
}
if( WinPeekMsg( habAsync
, &qmsgPeek
, NULL
, UM_SIZING
, UM_LOAD
, PM_NOREMOVE))
DosSemClear( hsemDrawOn);
else
DosSemSet( hsemDrawOn);
Zoom( sZoomArg);
break;
/************************************************************************/
/* Button down will cause a correlate on the picture to test for a hit. */
/* Any selected segment will be highlighted and redrawn as dynamic. */
/************************************************************************/
case UM_LEFTDOWN:
if( !fButtonDownAsync)
{
fButtonDownAsync = TRUE;
LeftDown( qmsgAsync.mp1);
}
break;
/************************************************************************/
/* if a segment is being dragged it will be redrawn in a new posn */
/************************************************************************/
case UM_MOUSEMOVE:
if( !fButtonDownAsync)
break;
for( fDone = FALSE; !fDone ;)
{
if( WinPeekMsg( habAsync /* look through first button-up */
, &qmsgPeek
, NULL
, UM_MOUSEMOVE
, UM_LEFTUP
, PM_NOREMOVE))
if( qmsgPeek.msg == UM_MOUSEMOVE) /* only collapse move msgs */
WinPeekMsg( habAsync
, &qmsgAsync
, NULL
, UM_MOUSEMOVE
, UM_MOUSEMOVE
, PM_REMOVE);
else
fDone = TRUE;
else
fDone = TRUE;
}
MouseMove( qmsgAsync.mp1); /* process last move before button-up */
break;
/************************************************************************/
/* if a segment is being dragged it will be redrawn as normal */
/************************************************************************/
case UM_LEFTUP:
if( fButtonDownAsync)
{
LeftUp();
fButtonDownAsync = FALSE;
}
break;
/************************************************************************/
/* destroy resources and terminate */
/************************************************************************/
case UM_DIE:
WinDestroyMsgQueue( hmqAsync);
WinTerminate( habAsync);
DosExit( EXIT_THREAD, 0);
break;
/************************************************************************/
default:
break;
}
}
}
/******************************************************************************/
/* */
/******************************************************************************/
VOID CalcSize( mp1, mp2)
MPARAM mp1, mp2;
{
ptsScrollMax.y = SHORT2FROMMP( mp2);
ptsHalfScrollMax.y = ptsScrollMax.y >> 1;
ptsScrollPage.x = ptsScrollMax.x >> 3;
ROUND_DOWN_MOD( ptsScrollPage.x, (SHORT)lByteAlignX);
ptsScrollLine.x = ptsScrollMax.x >> 5;
ROUND_DOWN_MOD( ptsScrollLine.x, (SHORT)lByteAlignX);
ptsScrollPos.y = (SHORT)(
( (LONG)ptsScrollPos.y
* (LONG)SHORT2FROMMP(mp2)
)/ (LONG)SHORT2FROMMP(mp1)
);
ptsOldScrollPos.y = (SHORT)(
( (LONG)ptsOldScrollPos.y
* (LONG)SHORT2FROMMP(mp2)
)/ (LONG)SHORT2FROMMP(mp1)
);
WinSendMsg( hwndVertScroll
, SBM_SETSCROLLBAR
, MPFROMSHORT( ptsScrollPos.y)
, MPFROM2SHORT( 1, ptsScrollMax.y) );
ptsScrollMax.x = SHORT1FROMMP( mp2);
ptsHalfScrollMax.x = ptsScrollMax.x >> 1;
ptsScrollPage.y = ptsScrollMax.y >> 3;
ROUND_DOWN_MOD( ptsScrollPage.y, (SHORT)lByteAlignY);
ptsScrollLine.y = ptsScrollMax.y >> 5;
ROUND_DOWN_MOD( ptsScrollLine.y, (SHORT)lByteAlignY);
ptsScrollPos.x = (SHORT)(
( (LONG)ptsScrollPos.x
* (LONG)SHORT1FROMMP(mp2)
)/(LONG)SHORT1FROMMP(mp1)
);
ptsOldScrollPos.x = (SHORT)(
( (LONG)ptsOldScrollPos.x
* (LONG)SHORT1FROMMP(mp2)
)/ (LONG)SHORT1FROMMP(mp1)
);
WinSendMsg( hwndHorzScroll
, SBM_SETSCROLLBAR
, MPFROMSHORT( ptsScrollPos.x)
, MPFROM2SHORT( 1, ptsScrollMax.x) );
}
/******************************************************************************/
/* button down will cause one segment to be indicated and made dynamic */
/******************************************************************************/
VOID
LeftDown( mp)
MPARAM mp;
{
#ifdef fred
LONG alSegTag[HITS][DEPTH][2];
#endif
POINTL aptl[2];
LONG lColor;
POINTL ptl;
HRGN hrgn, hrgnUpdt, hrgnUpdtDrag;
RECTL rcl;
MATRIXLF matlf;
CHAR pszMsg[40];
PSZ psz1, psz2;
BOOL fFirst;
PSEGLIST psl;
ptl.x = (LONG)(SHORT)SHORT1FROMMP( mp);
ptl.y = (LONG)(SHORT)SHORT2FROMMP( mp);
/****************************************************************************/
/****************************************************************************/
aptl[0] = aptl[1] = ptl;
aptl[1].x++;
aptl[1].y++;
GpiBitBlt( hpsBitmapSave, NULL, 2L, aptl, ROP_ONE, BBO_IGNORE);
lColor = GpiQueryPel( hpsBitmapSave, &ptl);
for( pslPicked = pslTail; pslPicked != NULL; pslPicked = pslPicked->pslPrev)
{
rcl = pslPicked->rclCurrent;
GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
rcl.xRight++;
rcl.yTop++;
if( WinPtInRect( habAsync, &rcl, &ptl)) /* is point in bounding box? */
#ifdef fred
if( 0L < GpiCorrelateSegment( hpsClient
, pslPicked->lSegId
, PICKSEL_VISIBLE
, &ptl
, HITS
, DEPTH
, (PLONG)alSegTag ))
#endif
{
DrawPiece( hpsBitmapSave, pslPicked, FALSE);
if( GpiQueryPel( hpsBitmapSave, &ptl) != lColor)
break; /* got a hit */
}
}
if( pslPicked)
lPickedSeg = pslPicked->lSegId;
else
{
fButtonDownAsync = FALSE;
return;
}
if( (lPickedSeg < 1) || (lPickedSeg > lLastSegId) )
{
DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
sprintf( szFmt, "Segment id out of range: %x", lPickedSeg);
for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
;
DosSemClear( hsemSzFmt);
MyMessageBox( hwndClient, pszMsg);
fButtonDownAsync = FALSE;
return;
}
/****************************************************************************/
GpiQuerySegmentTransformMatrix( hpsClient, lPickedSeg, 9L, &matlf );
ptlOffStart.x = matlf.lM31;
ptlOffStart.y = matlf.lM32;
ptlMoveStart = ptl;
GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptlMoveStart);
ptlMoveStart.x = (ptlMoveStart.x / 50) * 50;
ptlMoveStart.y = (ptlMoveStart.y / 50) * 50;
ptlUpdtRef = ptlMoveStart;
GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 1L, &ptlUpdtRef);
/****************************************************************************/
hrgnUpdt = GpiCreateRegion( hpsClient, 0L, NULL);
for( psl = pslPicked, fFirst = TRUE
; (psl != pslPicked) || fFirst
; psl = psl->pslNextIsland, fFirst = FALSE )
{
rcl = psl->rclCurrent; /* get model space bounding box of piece */
GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
rcl.xRight++; /* adjust rectangle for conversion to dev space */
rcl.yTop++;
rcl.xRight += 2; /* should not need */
rcl.yTop += 2; /* should not need */
rcl.xLeft -= 4; /* should not need */
rcl.yBottom -= 4; /* should not need */
hrgn = GpiCreateRegion( hpsClient, 1L, &rcl);
GpiCombineRegion( hpsClient, hrgnUpdt, hrgnUpdt, hrgn, CRGN_OR);
GpiDestroyRegion( hpsClient, hrgn);
GpiSetSegmentAttrs( hpsClient, psl->lSegId, ATTR_VISIBLE, ATTR_OFF);
}
GpiQueryRegionBox( hpsClient, hrgnUpdt, (PRECTL)aptlUpdt);
WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlUpdt, 3);
ROUND_DOWN_MOD( aptlUpdt[0].x, lByteAlignX); /* round down */
ROUND_DOWN_MOD( aptlUpdt[0].y, lByteAlignY); /* round down */
ROUND_UP_MOD( aptlUpdt[1].x, lByteAlignX); /* round up */
ROUND_UP_MOD( aptlUpdt[1].y, lByteAlignY); /* round up */
WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlUpdt, 3);
hrgnUpdtDrag = GpiCreateRegion( hpsBitmapBuff, 1L, (PRECTL)aptlUpdt);
aptlUpdt[2] = aptlUpdt[0];
DoDraw( hpsBitmapBuff, hrgnUpdtDrag, TRUE);
GpiDestroyRegion( hpsClient, hrgnUpdt);
GpiDestroyRegion( hpsBitmapBuff, hrgnUpdtDrag);
GpiBitBlt( hpsBitmapSave
, hpsBitmapBuff
, 3L
, aptlUpdt
, ROP_SRCCOPY
, BBO_IGNORE );
/****************************************************************************/
for( psl = pslPicked, fFirst = TRUE
; (psl != pslPicked) || fFirst
; psl = psl->pslNextIsland, fFirst = FALSE )
{
GpiSetSegmentAttrs( hpsClient, psl->lSegId, ATTR_VISIBLE, ATTR_ON);
DrawPiece( hpsBitmapBuff, psl, TRUE);
}
GpiBitBlt( hpsClient
, hpsBitmapBuff
, 3L
, aptlUpdt
, ROP_SRCCOPY
, BBO_IGNORE );
WinSetCapture( HWND_DESKTOP, hwndClient);
}
/******************************************************************************/
/* */
/* move the segment */
/* */
/******************************************************************************/
VOID
MouseMove( mp)
MPARAM mp;
{
RECTL rcl;
POINTL ptl, ptlModel, ptlDevice;
POINTL aptlUpdtRef[3], aptlUpdtNew[3];
PSEGLIST psl;
BOOL fFirst;
MATRIXLF matlf;
if( !lPickedSeg || !pslPicked)
return;
ptl.x = (LONG)(SHORT)SHORT1FROMMP( mp);
ptl.y = (LONG)(SHORT)SHORT2FROMMP( mp);
/****************************************************************************/
/* clip mouse coords to client window */
/****************************************************************************/
WinQueryWindowRect( hwndClient, &rcl);
if (rcl.xLeft > ptl.x)
ptl.x = rcl.xLeft;
if (rcl.xRight <= ptl.x)
ptl.x = rcl.xRight;
if (rcl.yBottom > ptl.y)
ptl.y = rcl.yBottom;
if (rcl.yTop <= ptl.y)
ptl.y = rcl.yTop;
ptlModel = ptl;
GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptlModel);
ptlModel.x = 50 * (ptlModel.x / 50);
ptlModel.y = 50 * (ptlModel.y / 50);
if( (ptlModel.x == ptlOldMouse.x) && (ptlModel.y == ptlOldMouse.y))
return;
ptlOldMouse.x = ptlModel.x;
ptlOldMouse.y = ptlModel.y;
ptlDevice = ptlModel;
GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 1L, &ptlDevice);
GpiBitBlt( hpsBitmapBuff
, hpsBitmapSave
, 3L
, aptlUpdt
, ROP_SRCCOPY
, BBO_IGNORE );
aptlUpdtRef[0] = aptlUpdt[0];
aptlUpdtRef[1] = aptlUpdt[1];
aptlUpdt[0].x += ptlDevice.x - ptlUpdtRef.x;
aptlUpdt[0].y += ptlDevice.y - ptlUpdtRef.y;
aptlUpdt[1].x += ptlDevice.x - ptlUpdtRef.x;
aptlUpdt[1].y += ptlDevice.y - ptlUpdtRef.y;
WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlUpdt, 3);
ROUND_DOWN_MOD( aptlUpdt[0].x, lByteAlignX); /* round down */
ROUND_DOWN_MOD( aptlUpdt[0].y, lByteAlignY); /* round down */
ROUND_UP_MOD( aptlUpdt[1].x, lByteAlignX); /* round up */
ROUND_UP_MOD( aptlUpdt[1].y, lByteAlignY); /* round up */
WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlUpdt, 3);
aptlUpdt[2] = aptlUpdt[0];
ptlUpdtRef = ptlDevice;
GpiBitBlt( hpsBitmapSave
, hpsBitmapBuff
, 3L
, aptlUpdt
, ROP_SRCCOPY
, BBO_IGNORE );
GpiQuerySegmentTransformMatrix( hpsClient
, lPickedSeg
, 9L
, &matlf);
for( psl = pslPicked, fFirst = TRUE
; (psl != pslPicked) || fFirst
; psl = psl->pslNextIsland, fFirst = FALSE )
{
matlf.lM31 = ptlOffStart.x + ptlModel.x - ptlMoveStart.x;
matlf.lM32 = ptlOffStart.y + ptlModel.y - ptlMoveStart.y;
GpiSetSegmentTransformMatrix( hpsClient
, psl->lSegId
, 9L
, &matlf
, TRANSFORM_REPLACE);
psl->ptlModelXlate.x = matlf.lM31;
psl->ptlModelXlate.y = matlf.lM32;
DrawPiece( hpsBitmapBuff, psl, TRUE);
}
WinUnionRect( habMain
, (PRECTL)aptlUpdtNew
, (PRECTL)aptlUpdt
, (PRECTL)aptlUpdtRef);
WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlUpdtNew, 2);
ROUND_DOWN_MOD( aptlUpdtNew[0].x, lByteAlignX); /* round down */
ROUND_DOWN_MOD( aptlUpdtNew[0].y, lByteAlignY); /* round down */
ROUND_UP_MOD( aptlUpdtNew[1].x, lByteAlignX); /* round up */
ROUND_UP_MOD( aptlUpdtNew[1].y, lByteAlignY); /* round up */
WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlUpdtNew, 2);
aptlUpdtNew[2] = aptlUpdtNew[0];
GpiBitBlt( hpsClient
, hpsBitmapBuff
, 3L
, aptlUpdtNew
, ROP_SRCCOPY
, BBO_IGNORE );
}
/******************************************************************************/
/* */
/* The dragged segment is being unselected. Return it to its normal state. */
/* */
/******************************************************************************/
VOID
LeftUp()
{
PSEGLIST psl, pslTemp;
POINTL ptlShift;
BOOL fFirst;
MATRIXLF matlf;
LONG l;
if( !lPickedSeg || !pslPicked)
return;
for( psl = pslPicked, fFirst = TRUE
; (psl != pslPicked) || fFirst
; psl = psl->pslNextIsland, fFirst = FALSE )
{
SetRect( psl);
SegListUpdate( MAKE_TAIL_SEG, psl); /* at tail => highest priority */
psl->fIslandMark = TRUE; /* mark as island member */
}
GpiQuerySegmentTransformMatrix( hpsClient
, lPickedSeg
, 9L
, &matlf);
ptlShift.x = matlf.lM31;
ptlShift.y = matlf.lM32;
for( psl = pslHead; psl != NULL; psl = psl->pslNext)
if( !psl->fIslandMark)
for( l = 0; l < 8; l++)
if( pslPicked->lAdjacent[l] == psl->lSegId)
{
GpiQuerySegmentTransformMatrix( hpsClient, psl->lSegId, 9L, &matlf);
if( (ptlShift.x == matlf.lM31) && (ptlShift.y == matlf.lM32))
{
DosBeep( 600, 100);
DosBeep( 1200, 50);
MarkIsland( psl, TRUE); /* mark the whole new island */
pslTemp = psl->pslNextIsland; /* swap island ptrs */
psl->pslNextIsland = pslPicked->pslNextIsland;
pslPicked->pslNextIsland = pslTemp;
}
}
MarkIsland( pslPicked, FALSE); /* unmark the island */
pslPicked = NULL;
lPickedSeg = NULL;
WinSetCapture( HWND_DESKTOP, (HWND)NULL);
}
/******************************************************************************/
/* */
/* mark a whole island */
/* */
/******************************************************************************/
VOID
MarkIsland( pslMark, fMark)
PSEGLIST pslMark;
BOOL fMark;
{
PSEGLIST psl;
BOOL fFirst;
for( psl = pslMark, fFirst = TRUE
; (psl != pslMark) || fFirst
; psl = psl->pslNextIsland, fFirst = FALSE )
psl->fIslandMark = fMark; /* mark as island member */
}
/******************************************************************************/
/* */
/* DoHorzScroll will horizontally scroll the current contents of */
/* the client area and redraw the invalidated area */
/* */
/******************************************************************************/
VOID
DoHorzScroll()
{
POINTL aptlClient[3];
HRGN hrgn;
MATRIXLF matlf;
if( ptsScrollPos.x > ptsScrollMax.x) /* clip to range of scroll param */
ptsScrollPos.x = ptsScrollMax.x;
if( ptsScrollPos.x < 0)
ptsScrollPos.x = 0;
if( ptsOldScrollPos.x != ptsScrollPos.x) /* only process change in position */
WinSendMsg( hwndHorzScroll
, SBM_SETPOS
, MPFROM2SHORT( ptsScrollPos.x, 0)
, MPFROMLONG( NULL));
/****************************************************************************/
/* Scroll the window the reqd amount, using bitblt'ing (ScrollWindow) */
/* if any of the screen still in view, and paint into uncovered region; */
/* else repaint the whole client area. */
/****************************************************************************/
hrgn = GpiCreateRegion( hpsClient, 0L, NULL);
WinQueryWindowRect( hwndClient, (PRECTL)aptlClient);
if( abs( ptsScrollPos.x - ptsOldScrollPos.x) <= ptsScrollMax.x)
{
WinScrollWindow( hwndClient
, ptsOldScrollPos.x - ptsScrollPos.x
, 0
, NULL
, NULL
, hrgn
, NULL
, 0);
} else
{
GpiSetRegion( hpsClient, hrgn, 1L, (PRECTL)aptlClient);
}
/****************************************************************************/
/* adjust the default view matrix */
/****************************************************************************/
GpiQueryDefaultViewMatrix( hpsClient, 9L, &matlf );
matlf.lM31 -= ptsScrollPos.x - ptsOldScrollPos.x;
GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, TRANSFORM_REPLACE);
DoDraw( hpsClient, hrgn, TRUE); /* paint into the client area */
ptsOldScrollPos.x = ptsScrollPos.x;
GpiDestroyRegion( hpsClient, hrgn);
aptlClient[2] = aptlClient[0];
GpiBitBlt( hpsBitmapBuff /* update the off-screen client image */
, hpsClient
, 3L
, aptlClient
, ROP_SRCCOPY
, BBO_IGNORE );
}
/******************************************************************************/
/* */
/* DoVertScroll will vertically scroll the current contents of */
/* the client area and redraw the invalidated area */
/* */
/******************************************************************************/
VOID
DoVertScroll()
{
POINTL aptlClient[3];
HRGN hrgn;
MATRIXLF matlf;
if( ptsScrollPos.y > ptsScrollMax.y)
ptsScrollPos.y = ptsScrollMax.y;
if( ptsScrollPos.y < 0)
ptsScrollPos.y = 0;
if( ptsOldScrollPos.y != ptsScrollPos.y)
WinSendMsg( hwndVertScroll
, SBM_SETPOS
, MPFROM2SHORT( ptsScrollPos.y, 0)
, MPFROMLONG( NULL));
/****************************************************************************/
/* Scroll the window the reqd amount, using bitblt'ing (ScrollWindow) */
/* if any of the screen still in view, and paint into uncovered region; */
/* else repaint the whole client area. */
/****************************************************************************/
hrgn = GpiCreateRegion( hpsClient, 0L, NULL);
WinQueryWindowRect( hwndClient, (PRECTL)aptlClient);
if( abs( ptsScrollPos.y - ptsOldScrollPos.y) <= ptsScrollMax.y)
{
WinScrollWindow( hwndClient
, 0
, ptsScrollPos.y - ptsOldScrollPos.y
, NULL
, NULL
, hrgn
, NULL
, 0);
} else
{
GpiSetRegion( hpsClient, hrgn, 1L, (PRECTL)aptlClient);
}
GpiQueryDefaultViewMatrix( hpsClient, 9L, &matlf );
matlf.lM32 += ptsScrollPos.y - ptsOldScrollPos.y;
GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, TRANSFORM_REPLACE);
DoDraw( hpsClient, hrgn, TRUE);
ptsOldScrollPos.y = ptsScrollPos.y;
GpiDestroyRegion( hpsClient, hrgn);
aptlClient[2] = aptlClient[0];
GpiBitBlt( hpsBitmapBuff
, hpsClient
, 3L
, aptlClient
, ROP_SRCCOPY
, BBO_IGNORE );
}
/******************************************************************************/
/* */
/* Redraw the entire client window. */
/* */
/******************************************************************************/
VOID
Redraw()
{
RECTL rclInvalid;
HRGN hrgnUpdt;
POINTL aptlUpdtNew[3];
WinQueryWindowRect( hwndClient, &rclInvalid);
hrgnUpdt = GpiCreateRegion( hpsBitmapBuff, 1L, &rclInvalid);
DoDraw( hpsBitmapBuff, hrgnUpdt, TRUE);
GpiDestroyRegion( hpsBitmapBuff, hrgnUpdt);
aptlUpdtNew[0].x = rclInvalid.xLeft;
aptlUpdtNew[0].y = rclInvalid.yBottom;
aptlUpdtNew[1].x = rclInvalid.xRight;
aptlUpdtNew[1].y = rclInvalid.yTop;
ROUND_DOWN_MOD( aptlUpdtNew[0].x, lByteAlignX); /* round down */
ROUND_DOWN_MOD( aptlUpdtNew[0].y, lByteAlignY); /* round down */
ROUND_UP_MOD( aptlUpdtNew[1].x, lByteAlignX); /* round up */
ROUND_UP_MOD( aptlUpdtNew[1].y, lByteAlignY); /* round up */
aptlUpdtNew[2] = aptlUpdtNew[0];
GpiBitBlt( hpsClient
, hpsBitmapBuff
, 3L
, aptlUpdtNew
, ROP_SRCCOPY
, BBO_IGNORE );
}
/******************************************************************************/
/* */
/* toggle a flag and update the menu check-box */
/* */
/******************************************************************************/
VOID
ToggleMenuItem( usMenuMajor, usMenuMinor, pfFlag)
USHORT usMenuMajor, usMenuMinor;
PBOOL pfFlag;
{
MENUITEM mi;
WinSendMsg( WinWindowFromID( hwndFrame, FID_MENU)
, MM_QUERYITEM
, MPFROM2SHORT( usMenuMajor, FALSE)
, MPFROMP( (PMENUITEM)&mi));
if( *pfFlag)
{
*pfFlag = FALSE;
WinSendMsg( mi.hwndSubMenu
, MM_SETITEMATTR
, MPFROM2SHORT( usMenuMinor, TRUE)
, MPFROM2SHORT( MIA_CHECKED, ~MIA_CHECKED) );
}
else
{
*pfFlag = TRUE;
WinSendMsg( mi.hwndSubMenu
, MM_SETITEMATTR
, MPFROM2SHORT( usMenuMinor, TRUE)
, MPFROM2SHORT( MIA_CHECKED, MIA_CHECKED) );
}
}
/******************************************************************************/
/* */
/* adjust zoom factor and recalc the picture transform, then do a redraw of */
/* whole screen */
/* */
/******************************************************************************/
VOID
Zoom( sInOrOut)
SHORT sInOrOut;
{
LONG lScaleOld;
lScaleOld = lScale;
lScale += sInOrOut;
if( lScale > 0)
lScale = 0;
else
if( lScale < -ZOOM_MAX)
lScale = -ZOOM_MAX;
if( lScale != lScaleOld)
{
ZoomMenuItems();
CalcBounds();
CalcTransform( hwndClient);
Redraw();
}
}
/******************************************************************************/
/* */
/* enable/disable zoom menu items depending on scaling */
/* */
/******************************************************************************/
VOID
ZoomMenuItems()
{
MENUITEM mi;
HWND hwndMenu, hwndOptions;
hwndMenu = WinWindowFromID( hwndFrame, FID_MENU);
WinSendMsg( hwndMenu
, MM_QUERYITEM
, MPFROM2SHORT( SM_OPTIONS, FALSE)
, MPFROMP( (PMENUITEM)&mi));
hwndOptions = mi.hwndSubMenu;
if( lScale >= 0)
{
WinSendMsg( hwndOptions
, MM_SETITEMATTR
, MPFROM2SHORT( MENU_ZOOMIN, TRUE)
, MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED));
WinSendMsg( hwndOptions
, MM_SETITEMATTR
, MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
, MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
} else
{
if( lScale <= - ZOOM_MAX)
{
WinSendMsg( hwndOptions
, MM_SETITEMATTR
, MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
, MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED));
WinSendMsg( hwndOptions
, MM_SETITEMATTR
, MPFROM2SHORT( MENU_ZOOMIN, TRUE)
, MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
} else
{
WinSendMsg( hwndOptions
, MM_SETITEMATTR
, MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
, MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
WinSendMsg( hwndOptions
, MM_SETITEMATTR
, MPFROM2SHORT( MENU_ZOOMIN, TRUE)
, MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
}
}
}
/******************************************************************************/
/* */
/* Determine the bounding rect of a segment. */
/* */
/* Use special knowledge that the only non-identity part of the segment */
/* transform is the translation part. */
/******************************************************************************/
VOID
SetRect( psl)
PSEGLIST psl;
{
MATRIXLF matlf;
GpiQuerySegmentTransformMatrix( hpsClient /* get model xform of segment */
, psl->lSegId
, 9L
, &matlf );
psl->rclCurrent = psl->rclBitBlt; /* world space bounding rect */
psl->rclCurrent.xLeft += matlf.lM31; /* apply translation */
psl->rclCurrent.yBottom += matlf.lM32;
psl->rclCurrent.xRight += matlf.lM31;
psl->rclCurrent.yTop += matlf.lM32;
}
/******************************************************************************/
/* */
/* set the default viewing transform */
/* */
/******************************************************************************/
VOID
SetDVTransform( fx11, fx12, fx21, fx22, l31, l32, lType)
FIXED fx11, fx12, fx21, fx22;
LONG l31, l32, lType;
{
MATRIXLF matlf;
matlf.fxM11 = fx11;
matlf.fxM12 = fx12;
matlf.lM13 = 0L;
matlf.fxM21 = fx21;
matlf.fxM22 = fx22;
matlf.lM23 = 0L;
matlf.lM31 = l31;
matlf.lM32 = l32;
matlf.lM33 = 1L;
GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, lType);
}
/******************************************************************************/
/* */
/* get bounding rect of whole picture in model coordinates */
/* */
/******************************************************************************/
VOID
CalcBounds()
{
PSEGLIST psl;
RECTL rcl;
if( !pslHead)
return;
rclBounds = pslHead->rclCurrent;
for( psl = pslHead->pslNext; psl != NULL; psl = psl->pslNext)
{
rcl = psl->rclCurrent;
if( rcl.xLeft < rclBounds.xLeft)
rclBounds.xLeft = rcl.xLeft;
if( rcl.xRight > rclBounds.xRight)
rclBounds.xRight = rcl.xRight;
if( rcl.yTop > rclBounds.yTop)
rclBounds.yTop = rcl.yTop;
if( rcl.yBottom < rclBounds.yBottom)
rclBounds.yBottom = rcl.yBottom;
}
}
/******************************************************************************/
/* */
/* Calculate and set the default viewing transform based on zoom and scroll */
/* */
/******************************************************************************/
VOID
CalcTransform( hwnd)
HWND hwnd;
{
RECTL rclClient;
POINTL ptlCenter, ptlTrans, ptlScale, aptl[4], ptlOrg, aptlSides[12];
PSEGLIST psl;
LONG l;
/****************************************************************************/
/* from bounding rect of picture get center of picture */
/****************************************************************************/
ptlCenter.x = (rclBounds.xLeft + rclBounds.xRight) / 2;
ptlCenter.y = (rclBounds.yBottom + rclBounds.yTop ) / 2;
/****************************************************************************/
/* translate center of picture to origin */
/****************************************************************************/
SetDVTransform( (FIXED)UNITY
, (FIXED)0
, (FIXED)0
, (FIXED)UNITY
, -ptlCenter.x
, -ptlCenter.y
, TRANSFORM_REPLACE);
/****************************************************************************/
/* scale down to 1:1 of bitmap in file */
/****************************************************************************/
ptlScale.x = UNITY * bmpBitmapFile.cx /(ptlTopRight.x - ptlBotLeft.x);
ptlScale.y = UNITY * bmpBitmapFile.cy /(ptlTopRight.y - ptlBotLeft.y);
/****************************************************************************/
/* add in zoom scale */
/****************************************************************************/
ptlScale.x += ptlScale.x * lScale / (ZOOM_MAX + 1);
ptlScale.y += ptlScale.y * lScale / (ZOOM_MAX + 1);
SetDVTransform( (FIXED)ptlScale.x
, (FIXED)0
, (FIXED)0
, (FIXED)ptlScale.y
, 0L
, 0L
, TRANSFORM_ADD);
/****************************************************************************/
/* translate center of picture to center of client window */
/****************************************************************************/
WinQueryWindowRect( hwnd, &rclClient);
ptlTrans.x = (rclClient.xRight - rclClient.xLeft) / 2;
ptlTrans.y = (rclClient.yTop - rclClient.yBottom) / 2;
/****************************************************************************/
/* add in horizontal and vertical scrolling factors */
/****************************************************************************/
ptlTrans.x += ptsScrollPos.x - ptsHalfScrollMax.x;
ptlTrans.y += ptsScrollPos.y - ptsHalfScrollMax.y;
SetDVTransform( (FIXED)UNITY
, (FIXED)0
, (FIXED)0
, (FIXED)UNITY
, ptlTrans.x
, ptlTrans.y
, TRANSFORM_ADD);
/****************************************************************************/
/* create bitmaps for pieces */
/****************************************************************************/
ptlOffset = ptlBotLeft; /* BottomLeft corner in dev coord */
GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 1L, &ptlOffset);
if( (ptlScale.x != ptlScaleRef.x) || (ptlScale.y != ptlScaleRef.y))
{
ptlScaleRef = ptlScale;
/**************************************************************************/
/* create a shadow bitmap of the original, sized to current output size */
/**************************************************************************/
aptl[0] = ptlBotLeft; /* current output rect, dev coord */
aptl[1] = ptlTopRight;
GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, aptl);
aptl[0].x -= ptlOffset.x; /* put bottom left at (0,0) */
aptl[0].y -= ptlOffset.y;
aptl[1].x -= ptlOffset.x - 1; /* correct for coordinate convert */
aptl[1].y -= ptlOffset.y - 1;
aptl[2].x = 0L;
aptl[2].y = 0L;
aptl[3].x = bmpBitmapFile.cx + 1; /* bitmap dimensions */
aptl[3].y = bmpBitmapFile.cy + 1;
GpiSetBitmap( hpsBitmapSize, hbmBitmapSize);
GpiBitBlt( hpsBitmapSize /* copy the bitmap */
, hpsBitmapFile
, 4L
, aptl
, ROP_SRCCOPY
, BBO_IGNORE);
for( psl = pslHead; psl != NULL; psl = psl->pslNext)
{
if( DosSemWait( hsemTerminate, SEM_IMMEDIATE_RETURN)) /* exit if quit */
break;
aptl[0].x = psl->rclBitBlt.xLeft; /* bounding rect in world space */
aptl[0].y = psl->rclBitBlt.yBottom;
aptl[1].x = psl->rclBitBlt.xRight;
aptl[1].y = psl->rclBitBlt.yTop;
aptl[2] = aptl[0];
aptl[3] = aptl[1];
GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, &aptl[2]);
ptlOrg = aptl[2];
aptl[3].x -= ptlOrg.x - 1; /* bitmap rect of piece */
aptl[3].y -= ptlOrg.y - 1;
aptl[2].x = 0L;
aptl[2].y = 0L;
psl->aptlBitBlt[0] = aptl[0];
psl->aptlBitBlt[1] = aptl[1];
psl->aptlBitBlt[2] = aptl[2];
psl->aptlBitBlt[3] = aptl[3];
/************************************************************************/
/* compute the piece control points */
/************************************************************************/
for( l=0; l<12; l++)
aptlSides[l] = psl->aptlSides[l];
GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 12L, aptlSides);
for( l=0; l<12; l++)
{
aptlSides[l].x -= ptlOrg.x;
aptlSides[l].y -= ptlOrg.y;
}
/************************************************************************/
/* prepare the mask to punch a hole in the output bitmap */
/************************************************************************/
GpiSetBitmap( psl->hpsHole, psl->hbmHole);
GpiSetClipPath( psl->hpsHole, 0L, SCP_RESET); /* no clip path */
GpiBitBlt( psl->hpsHole /* fill with 1's */
, NULL
, 2L
, &psl->aptlBitBlt[2]
, ROP_ONE
, BBO_IGNORE);
GpiBeginPath( psl->hpsHole, 1L); /* define a clip path */
GpiMove( psl->hpsHole, &aptlSides[11]);
GpiPolySpline( psl->hpsHole, 12L, aptlSides);
GpiEndPath( psl->hpsHole);
GpiSetClipPath( psl->hpsHole, 1L, SCP_AND);
GpiBitBlt( psl->hpsHole /* fill with 0's */
, NULL
, 2L
, &psl->aptlBitBlt[2]
, ROP_ZERO
, BBO_IGNORE);
GpiSetClipPath( psl->hpsHole, 0L, SCP_RESET); /* clear the clip path */
/************************************************************************/
/* prepare the mask to fill the hole in the output bitmap */
/************************************************************************/
GpiSetBitmap( psl->hpsFill, psl->hbmFill);
GpiSetClipPath( psl->hpsFill, 0L, SCP_RESET); /* no clip path */
GpiBitBlt( psl->hpsFill /* fill with 1's */
, NULL
, 2L
, &psl->aptlBitBlt[2]
, ROP_ZERO
, BBO_IGNORE);
GpiBeginPath( psl->hpsFill, 1L); /* define a clip path */
GpiMove( psl->hpsFill, &aptlSides[11]);
GpiPolySpline( psl->hpsFill, 12L, aptlSides);
GpiEndPath( psl->hpsFill);
GpiSetClipPath( psl->hpsFill, 1L, SCP_AND);
aptl[0] = psl->aptlBitBlt[2];
aptl[1] = psl->aptlBitBlt[3];
aptl[2].x = ptlOrg.x - ptlOffset.x; /* pick the right part */
aptl[2].y = ptlOrg.y - ptlOffset.y; /* of the sized bitmap */
GpiBitBlt( psl->hpsFill /* fill with data */
, hpsBitmapSize
, 3L
, aptl
, ROP_SRCCOPY
, BBO_IGNORE);
GpiSetClipPath( psl->hpsFill, 0L, SCP_RESET); /* clear the clip path */
GpiSetColor( psl->hpsFill, CLR_RED); /* draw the outline */
GpiMove( psl->hpsFill, &aptlSides[11]);
GpiPolySpline( psl->hpsFill, 12L, aptlSides);
}
}
}
/******************************************************************************/
/* */
/* Draw one piece. */
/* */
/******************************************************************************/
VOID
DrawPiece( hps, psl, fFill)
HPS hps;
PSEGLIST psl;
BOOL fFill;
{
POINTL aptl[4];
if( GpiQuerySegmentAttrs( hpsClient, psl->lSegId, ATTR_VISIBLE) == ATTR_ON)
{
aptl[2].x = aptl[2].y = 0L;
aptl[3] = psl->aptlBitBlt[3];
aptl[0].x = psl->rclBitBlt.xLeft + psl->ptlModelXlate.x;
aptl[0].y = psl->rclBitBlt.yBottom + psl->ptlModelXlate.y;
GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 1L, aptl);
aptl[1].x = aptl[0].x + aptl[3].x;
aptl[1].y = aptl[0].y + aptl[3].y;
GpiBitBlt( hps /* punch a hole */
, psl->hpsHole
, 4L
, aptl
, ROP_SRCAND
, BBO_IGNORE );
if( fFill)
GpiBitBlt( hps /* fill the hole */
, psl->hpsFill
, 4L
, aptl
, ROP_SRCPAINT
, BBO_IGNORE );
}
}
/******************************************************************************/
/* */
/* Draw the picture, using the passed region for clipping. */
/* Intersect the bounding box used for the clip region with the client rect. */
/* Test each segment to see if its bounding box intersects the bounding box */
/* of the clipping region. Draw only if there is an intersection. */
/* */
/******************************************************************************/
BOOL
DoDraw( hps, hrgn, fPaint)
HPS hps;
HRGN hrgn;
BOOL fPaint;
{
HRGN hrgnOld;
RECTL rcl, rclRegion, rclDst, rclClient;
PSEGLIST psl;
if( fPaint)
{
GpiSetColor( hps, CLR_BACKGROUND);
GpiPaintRegion( hps, hrgn); /* erase region */
}
GpiQueryRegionBox( hps, hrgn, &rclRegion);
WinQueryWindowRect( hwndClient, &rclClient);
if( !WinIntersectRect( habAsync, &rclRegion, &rclRegion, &rclClient))
return( FALSE); /* not in client window */
GpiSetClipRegion( hps, hrgn, &hrgnOld); /* make the clip region */
for( psl = pslHead; psl != NULL; psl = psl->pslNext) /* scan all pieces */
{
/**************************************************************************/
/* get the piece bounding box in device coordinates */
/**************************************************************************/
rcl = psl->rclCurrent;
GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
rcl.xRight++;
rcl.yTop++;
/**************************************************************************/
/* if the piece might be visible, and drawing allowed, draw the piece */
/**************************************************************************/
if( WinIntersectRect( habAsync, &rclDst, &rcl, &rclRegion))
if( DosSemWait( hsemDrawOn, SEM_IMMEDIATE_RETURN))
DrawPiece( hps, psl, TRUE);
else
break;
}
GpiSetClipRegion( hps, NULL, &hrgnOld);
if( DosSemWait( hsemDrawOn, SEM_IMMEDIATE_RETURN))
GpiSetRegion( hpsClient, hrgnInvalid, 0L, NULL);
return( TRUE);
}
/******************************************************************************/
/* */
/* Return a pointer to a segment list member, based on segment id. */
/* */
/******************************************************************************/
PSEGLIST
SegListGet( lSeg)
LONG lSeg;
{
PSEGLIST psl;
for( psl = pslHead; psl != NULL; psl = psl->pslNext)
if( psl->lSegId == lSeg)
return( psl);
return( NULL);
}
/******************************************************************************/
/* */
/* Check the segment list for obvious errors. */
/* */
/******************************************************************************/
BOOL
SegListCheck( iLoc)
INT iLoc;
{
PSEGLIST psl;
CHAR pszMsg[50];
PSZ psz1, psz2;
pszMsg[0] = '\0';
for( psl = pslHead; psl != NULL; psl = psl->pslNext)
if( (psl->lSegId < 1) || (psl->lSegId > lLastSegId) )
{
DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
sprintf( szFmt, "Bad head segment list, location %d", iLoc);
for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
;
DosSemClear( hsemSzFmt);
MyMessageBox( hwndClient, pszMsg);
return( FALSE);
}
for( psl = pslTail; psl != NULL; psl = psl->pslPrev)
if( (psl->lSegId < 1) || (psl->lSegId > lLastSegId) )
{
DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
sprintf( szFmt, "Bad head segment list, location %d", iLoc);
for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
;
DosSemClear( hsemSzFmt);
MyMessageBox( hwndClient, pszMsg);
return( FALSE);
}
return( TRUE);
}
/******************************************************************************/
/* */
/* Add (at head or tail) or delete a specified segment list member. */
/* */
/******************************************************************************/
PSEGLIST
SegListUpdate( usOperation, pslUpdate)
USHORT usOperation;
PSEGLIST pslUpdate;
{
PSEGLIST psl;
SEL sel;
switch( usOperation)
{
case ADD_HEAD_SEG:
DosAllocSeg( sizeof( SEGLIST), &sel, 0);
if( pslHead == NULL)
{
pslHead = MAKEP( sel, 0);
if( pslHead == NULL)
return( NULL);
*pslHead = *pslUpdate;
pslHead->pslPrev = NULL;
pslHead->pslNext = NULL;
pslTail = pslHead;
} else
{
psl = MAKEP( sel, 0);
if( psl == NULL)
return( NULL);
*psl = *pslUpdate;
pslHead->pslPrev = psl;
psl->pslNext = pslHead;
psl->pslPrev = NULL;
pslHead = psl;
}
return( pslHead);
break;
case ADD_TAIL_SEG:
DosAllocSeg( sizeof( SEGLIST), &sel, 0);
if( pslTail == NULL)
{
pslHead = MAKEP( sel, 0);
if( pslHead == NULL)
return( NULL);
*pslHead = *pslUpdate;
pslHead->pslPrev = NULL;
pslHead->pslNext = NULL;
pslTail = pslHead;
} else
{
psl = MAKEP( sel, 0);
if( psl == NULL)
return( NULL);
*psl = *pslUpdate;
pslTail->pslNext = psl;
psl->pslPrev = pslTail;
psl->pslNext = NULL;
pslTail = psl;
}
return( pslTail);
break;
case MAKE_TAIL_SEG:
if( pslUpdate == pslTail)
return( pslTail);
if( pslUpdate == pslHead)
{
pslHead = pslHead->pslNext;
pslHead->pslPrev = NULL;
} else
{
pslUpdate->pslPrev->pslNext = pslUpdate->pslNext;
pslUpdate->pslNext->pslPrev = pslUpdate->pslPrev;
}
pslTail->pslNext = pslUpdate;
pslUpdate->pslPrev = pslTail;
pslTail = pslUpdate;
pslTail->pslNext = NULL;
return( pslTail);
break;
case DEL_SEG:
for( psl = pslHead; psl != NULL; psl = psl->pslNext)
{
if( psl->lSegId == pslUpdate->lSegId)
{
if( psl == pslHead)
{
pslHead = psl->pslNext;
if( pslHead == NULL)
pslTail = NULL;
else
pslHead->pslPrev = NULL;
}else if( psl == pslTail)
{
pslTail = psl->pslPrev;
pslTail->pslNext = NULL;
} else
{
(psl->pslPrev)->pslNext = psl->pslNext;
(psl->pslNext)->pslPrev = psl->pslPrev;
}
DosFreeSeg( SELECTOROF(psl));
return( psl);
break;
}
}
return( NULL);
break;
default:
return( NULL);
}
}
/******************************************************************************/
/* */
/* DumpPicture will free the list and segment store for the picture */
/* */
/******************************************************************************/
BOOL
DumpPicture()
{
while( pslHead != NULL )
{
GpiSetBitmap( pslHead->hpsFill, NULL);
GpiDeleteBitmap( pslHead->hbmFill);
GpiDestroyPS( pslHead->hpsFill);
DevCloseDC( pslHead->hdcFill);
GpiSetBitmap( pslHead->hpsHole, NULL);
GpiDeleteBitmap( pslHead->hbmHole);
GpiDestroyPS( pslHead->hpsHole);
DevCloseDC( pslHead->hdcHole);
SegListUpdate( DEL_SEG, pslHead);
}
GpiDeleteSegments( hpsClient, 1L, lLastSegId);
if( hbmBitmapFile)
{
GpiSetBitmap( hpsBitmapFile, NULL);
GpiDeleteBitmap( hbmBitmapFile);
}
if( hbmBitmapSize)
{
GpiSetBitmap( hpsBitmapSize, NULL);
GpiDeleteBitmap( hbmBitmapSize);
}
if( hbmBitmapBuff)
{
GpiSetBitmap( hpsBitmapBuff, NULL);
GpiDeleteBitmap( hbmBitmapBuff);
}
if( hbmBitmapSave)
{
GpiSetBitmap( hpsBitmapSave, NULL);
GpiDeleteBitmap( hbmBitmapSave);
}
return( TRUE);
}
/******************************************************************************/
/* */
/* Draw the picture into segment store. */
/* */
/******************************************************************************/
BOOL
CreatePicture()
{
POINTL ptl, aptlSides[12], aptlControl[12];
SEGLIST sl;
PSEGLIST psl;
LONG l, lMinor, lNeighbor, alFuzz[36][4];
SIZEL sizl;
BITMAPINFOHEADER bmp;
DATETIME date;
/****************************************************************************/
/* compute some fuzz for the control points */
/****************************************************************************/
DosGetDateTime( &date);
srand( (USHORT)date.hundredths);
for( l=0; l<36; l++)
for( lMinor=0; lMinor<4; lMinor++)
alFuzz[l][lMinor] = 50 * (rand() % 10);
/****************************************************************************/
/* reset the default viewing transform to identity */
/****************************************************************************/
SetDVTransform( (FIXED)UNITY
, (FIXED)0
, (FIXED)0
, (FIXED)UNITY
, 0L
, 0L
, TRANSFORM_REPLACE);
/****************************************************************************/
/* set to store mode */
/****************************************************************************/
GpiSetDrawingMode( hpsClient, DM_RETAIN);
/****************************************************************************/
/* chaining and detectability off, fastchaining off */
/****************************************************************************/
GpiSetInitialSegmentAttrs( hpsClient, ATTR_CHAINED, ATTR_OFF);
GpiSetInitialSegmentAttrs( hpsClient, ATTR_FASTCHAIN, ATTR_OFF);
GpiSetInitialSegmentAttrs( hpsClient, ATTR_DETECTABLE, ATTR_OFF);
/****************************************************************************/
/* draw the pieces */
/****************************************************************************/
lLastSegId = 0;
for( ptl.x = ptlBotLeft.x; ptl.x < ptlTopRight.x; ptl.x += 500)
{
if( DosSemWait( hsemTerminate, SEM_IMMEDIATE_RETURN))
break;
for( ptl.y = ptlBotLeft.y; ptl.y < ptlTopRight.y; ptl.y += 500)
{
if( DosSemWait( hsemTerminate, SEM_IMMEDIATE_RETURN))
break;
lLastSegId++;
/************************************************************************/
/* compute the piece outline control points */
/************************************************************************/
aptlControl[0].x = 250L;
aptlControl[0].y = 500L;
aptlControl[1].x = 250;
aptlControl[1].y = -500L;
aptlControl[2].x = 500L;
aptlControl[2].y = 0L;
aptlControl[3].x = 0L;
aptlControl[3].y = 250L;
aptlControl[4].x = 1000L;
aptlControl[4].y = 250L;
aptlControl[5].x = 500L;
aptlControl[5].y = 500L;
aptlControl[6].x = 250L;
aptlControl[6].y = 0L;
aptlControl[7].x = 250L;
aptlControl[7].y = 1000L;
aptlControl[8].x = 0L;
aptlControl[8].y = 500L;
aptlControl[9].x = 500L;
aptlControl[9].y = 250L;
aptlControl[10].x = -500L;
aptlControl[10].y = 250L;
aptlControl[11].x = 0L;
aptlControl[11].y = 0L;
if( ptl.y == ptlBotLeft.y)
{
aptlControl[0].y = 0L;
aptlControl[1].y = 0L;
}
if( (ptl.x + 500) == ptlTopRight.x)
{
aptlControl[3].x = 500L;
aptlControl[4].x = 500L;
}
if( (ptl.y + 500) == ptlTopRight.y)
{
aptlControl[6].y = 500L;
aptlControl[7].y = 500L;
}
if( ptl.x == ptlBotLeft.x)
{
aptlControl[ 9].x = 0L;
aptlControl[10].x = 0L;
}
/************************************************************************/
/* compute the adjacent segments */
/************************************************************************/
sl.lAdjacent[0] = lLastSegId - 7;
sl.lAdjacent[1] = lLastSegId - 6;
sl.lAdjacent[2] = lLastSegId - 5;
sl.lAdjacent[3] = lLastSegId - 1;
sl.lAdjacent[4] = lLastSegId + 1;
sl.lAdjacent[5] = lLastSegId + 5;
sl.lAdjacent[6] = lLastSegId + 6;
sl.lAdjacent[7] = lLastSegId + 7;
if( ptl.x == ptlBotLeft.x)
{
sl.lAdjacent[0] = 0;
sl.lAdjacent[1] = 0;
sl.lAdjacent[2] = 0;
}
if( ptl.y == ptlBotLeft.y)
{
sl.lAdjacent[0] = 0;
sl.lAdjacent[3] = 0;
sl.lAdjacent[5] = 0;
}
if( (ptl.x + 500) == ptlTopRight.x)
{
sl.lAdjacent[5] = 0;
sl.lAdjacent[6] = 0;
sl.lAdjacent[7] = 0;
}
if( (ptl.y + 500) == ptlTopRight.y)
{
sl.lAdjacent[2] = 0;
sl.lAdjacent[4] = 0;
sl.lAdjacent[7] = 0;
}
/************************************************************************/
/* throw in some fuzz */
/************************************************************************/
if( sl.lAdjacent[3])
{
aptlControl[0].y -= alFuzz[lLastSegId - 1][0];
aptlControl[1].y += alFuzz[lLastSegId - 1][1];
}
if( sl.lAdjacent[1])
{
aptlControl[9].x -= alFuzz[lLastSegId - 1][2];
aptlControl[10].x += alFuzz[lLastSegId - 1][3];
}
if( lNeighbor = sl.lAdjacent[4])
{
aptlControl[7].y -= alFuzz[lNeighbor - 1][0];
aptlControl[6].y += alFuzz[lNeighbor - 1][1];
}
if( lNeighbor = sl.lAdjacent[6])
{
aptlControl[4].x -= alFuzz[lNeighbor - 1][2];
aptlControl[3].x += alFuzz[lNeighbor - 1][3];
}
/************************************************************************/
/* compute the piece control points in world coordinates */
/************************************************************************/
for( l=0; l<12; l++)
{
aptlSides[l].x = ptl.x + aptlControl[l].x;
aptlSides[l].y = ptl.y + aptlControl[l].y;
sl.aptlSides[l] = aptlSides[l];
}
/************************************************************************/
/* compute the dimensions of the matching rects for BitBlt */
/************************************************************************/
sl.rclBitBlt.xLeft = ptl.x - 250;
sl.rclBitBlt.yBottom = ptl.y - 250;
sl.rclBitBlt.xRight = ptl.x + 750;
sl.rclBitBlt.yTop = ptl.y + 750;
if( ptl.x == ptlBotLeft.x)
sl.rclBitBlt.xLeft += 250;
if( ptl.y == ptlBotLeft.y)
sl.rclBitBlt.yBottom += 250;
if( (ptl.x + 500) == ptlTopRight.x)
sl.rclBitBlt.xRight -= 250;
if( (ptl.y + 500) == ptlTopRight.y)
sl.rclBitBlt.yTop -= 250;
/************************************************************************/
/* store the piece location */
/************************************************************************/
sl.ptlLocation = ptl;
/************************************************************************/
/* draw one piece */
/************************************************************************/
GpiOpenSegment( hpsClient, lLastSegId);
GpiSetTag( hpsClient, lLastSegId);
GpiBeginPath( hpsClient, 1L);
GpiMove( hpsClient, &ptl);
GpiPolySpline( hpsClient, 12L, aptlSides);
GpiEndPath( hpsClient);
GpiSetColor( hpsClient, CLR_BLACK);
GpiFillPath( hpsClient, 1L, 0L);
GpiCloseSegment( hpsClient);
/************************************************************************/
/* create the masks */
/************************************************************************/
sizl.cx = 2 + ((bmpBitmapFile.cx
* (sl.rclBitBlt.xRight - sl.rclBitBlt.xLeft))
/ (ptlTopRight.x - ptlBotLeft.x));
sizl.cy = 2 + ((bmpBitmapFile.cy
* (sl.rclBitBlt.yTop - sl.rclBitBlt.yBottom))
/ (ptlTopRight.y - ptlBotLeft.y));
bmp = bmpBitmapFile;
bmp.cx = LOUSHORT( sizl.cx);
bmp.cy = LOUSHORT( sizl.cy);
sl.hdcFill = DevOpenDC( habMain
, OD_MEMORY
, "*"
, 3L
, (PDEVOPENDATA)&dop
, NULL);
sl.hpsFill = GpiCreatePS( habMain
, sl.hdcFill
, &sizl
, PU_PELS | GPIA_ASSOC | GPIT_MICRO );
sl.hbmFill = GpiCreateBitmap( sl.hpsFill
, &bmp
, 0L
, NULL
, NULL);
sl.hdcHole = DevOpenDC( habMain
, OD_MEMORY
, "*"
, 3L
, (PDEVOPENDATA)&dop
, NULL);
sl.hpsHole = GpiCreatePS( habMain
, sl.hdcHole
, &sizl
, PU_PELS | GPIA_ASSOC | GPIT_MICRO );
sl.hbmHole = GpiCreateBitmap( sl.hpsHole
, &bmp
, 0L
, NULL
, NULL);
GpiSetSegmentAttrs( hpsClient, lLastSegId, ATTR_DETECTABLE, ATTR_ON);
sl.lSegId = lLastSegId;
sl.pslNext = NULL;
sl.pslPrev = NULL;
sl.fIslandMark = FALSE;
sl.ptlModelXlate.x = sl.ptlModelXlate.y = 0L;
SetRect( &sl);
psl = SegListUpdate( ADD_TAIL_SEG, &sl);
psl->pslNextIsland = psl; /* point to self ==> island of one */
}
}
return( TRUE);
}
/******************************************************************************/
/* */
/* Create the Temp and Drag bitmaps. */
/* */
/******************************************************************************/
BOOL
PrepareBitmap()
{
hbmBitmapSize = GpiCreateBitmap( hpsBitmapSize
, &bmpBitmapFile
, 0L
, NULL
, NULL);
if( !hbmBitmapSize)
return( FALSE);
bmpBitmapSave = bmpBitmapFile;
bmpBitmapSave.cx = LOUSHORT( sizlMaxClient.cx);
bmpBitmapSave.cy = LOUSHORT( sizlMaxClient.cy);
hbmBitmapSave = GpiCreateBitmap( hpsBitmapSave
, &bmpBitmapSave
, 0L
, NULL
, NULL);
if( !hbmBitmapSave)
return( FALSE);
GpiSetBitmap( hpsBitmapSave, hbmBitmapSave);
hbmBitmapBuff = GpiCreateBitmap( hpsBitmapBuff
, &bmpBitmapSave
, 0L
, NULL
, NULL);
if( !hbmBitmapBuff)
return( FALSE);
GpiSetBitmap( hpsBitmapBuff, hbmBitmapBuff);
return( TRUE);
}
/******************************************************************************/
/* */
/* Create a memory DC and an associated PS. */
/* */
/******************************************************************************/
BOOL
CreateBitmapHdcHps( phdc, phps)
PHDC phdc;
PHPS phps;
{
SIZEL sizl;
HDC hdc;
HPS hps;
hdc = DevOpenDC( habMain, OD_MEMORY, "*", 3L, (PDEVOPENDATA)&dop, NULL);
if( !hdc)
return( FALSE);
sizl.cx = sizl.cy = 0L;
hps = GpiCreatePS( habMain
, hdc
, &sizl
, PU_PELS | GPIA_ASSOC | GPIT_MICRO );
if( !hps)
return( FALSE);
*phdc = hdc;
*phps = hps;
return( TRUE);
}
/******************************************************************************/
/* */
/* Get the bitmap from disk. */
/* Note that there are 2 formats for bitmap files, one of which is archaic. */
/* Both formats are supported here. All new bitmaps should follow the format */
/* in BITMAPFILEHEADER. */
/* */
/******************************************************************************/
BOOL
ReadBitmap( hfile)
HFILE hfile;
{
ULONG cScans;
ULONG ulSize; /* Number of bytes occupied by bitmap bits. */
USHORT cSegs; /* Number of 64K segments in ulSize. */
USHORT cbExtra; /* Bytes in last segment of ulSize. */
SEL sel; /* Base selector to file data. */
USHORT hugeshift; /* Segment index shift value. */
USHORT cbRead1; /* Number of bytes to read first call to DosRead */
USHORT cbRead2; /* Number of bytes to read second call to DosRead */
USHORT cbRead; /* Number of bytes read by DosRead. */
BOOL fRet = FALSE; /* Function return code. */
INT i; /* Generic loop index. */
FILESTATUS fsts;
PBITMAPFILEHEADER pbfh;
PRCBITMAP rb;
PBYTE pImage;
/**************************************************************************/
/* Find out how big the file is so we can read the whole thing in. */
/**************************************************************************/
if( DosQFileInfo( hfile, 1, &fsts, sizeof(FILESTATUS)) != 0)
goto ReadBitmap_close_file;
ulSize = fsts.cbFile;
cSegs = (USHORT)(ulSize/0x10000L);
cbExtra = (USHORT)(ulSize%0x10000L);
if (DosAllocHuge(cSegs, cbExtra, (PSEL)&sel, 0, 0))
goto ReadBitmap_close_file;
if (DosGetHugeShift( &hugeshift))
goto ReadBitmap_free_bits;
pImage = (PBYTE)MAKEP(sel, 0);
rb = (PRCBITMAP)pImage;
pbfh = (PBITMAPFILEHEADER)pImage;
/**************************************************************************/
/* Read the bits in from the file. The DosRead function allows a */
/* maximum of 64K-1 bytes read at a time. We get around this */
/* by reading two 32K chunks for each 64K segment, and reading the */
/* last segment in one piece. */
/**************************************************************************/
for (i = 0; i <= cSegs; ++i)
{
if (i < cSegs)
{
/* This segment is 64K bytes long, so split it up. */
cbRead1 = 0x8000;
cbRead2 = 0x8000;
}
else
{
/* This segment is less than 64K bytes long, so read it all. */
cbRead1 = cbExtra;
cbRead2 = 0;
}
/* There's a possibility that cbExtra will be 0, so check
* to avoid an unnecessary system call.
*/
if (cbRead1 > 0)
{
if (DosRead( hfile
, (PVOID)MAKEP(sel+(i<<hugeshift), 0)
, cbRead1
, &cbRead))
goto ReadBitmap_free_bits;
if (cbRead1 != cbRead)
goto ReadBitmap_free_bits;
}
/* This will always be skipped on the last partial segment. */
if (cbRead2 > 0)
{
if (DosRead( hfile
, (PVOID)MAKEP(sel+(i<<hugeshift), cbRead1)
, cbRead2
, &cbRead))
goto ReadBitmap_free_bits;
if (cbRead2 != cbRead)
goto ReadBitmap_free_bits;
}
}
/**************************************************************************/
/* Tell GPI to put the bits into the thread's PS. The function returns */
/* the number of scan lines of the bitmap that were copied. We want */
/* all of them at once. */
/**************************************************************************/
if (pbfh->bmp.cbFix != sizeof(BITMAPINFOHEADER))
{
bmpBitmapFile.cx = rb->bmWidth;
bmpBitmapFile.cy = rb->bmHeight;
bmpBitmapFile.cPlanes = rb->bmPlanes;
bmpBitmapFile.cBitCount = rb->bmBitcount;
hbmBitmapFile = GpiCreateBitmap( hpsBitmapFile
, &bmpBitmapFile
, 0L
, NULL
, NULL);
if( !hbmBitmapFile)
goto ReadBitmap_free_bits;
GpiSetBitmap( hpsBitmapFile, hbmBitmapFile);
pImage += rb->dwBitsOffset;
rb->dwBitsOffset = sizeof(BITMAPINFOHEADER);
cScans = GpiSetBitmapBits( hpsBitmapFile
, 0L
, (LONG)rb->bmHeight
, pImage
, (PBITMAPINFO)&(rb->dwBitsOffset));
if (cScans != (LONG)rb->bmHeight) /* original number of scans ? */
goto ReadBitmap_free_bits;
}
else
{
bmpBitmapFile.cx = pbfh->bmp.cx;
bmpBitmapFile.cy = pbfh->bmp.cy;
bmpBitmapFile.cPlanes = pbfh->bmp.cPlanes;
bmpBitmapFile.cBitCount = pbfh->bmp.cBitCount;
hbmBitmapFile = GpiCreateBitmap( hpsBitmapFile
, &bmpBitmapFile
, 0L
, NULL
, NULL);
if( !hbmBitmapFile)
goto ReadBitmap_free_bits;
GpiSetBitmap( hpsBitmapFile, hbmBitmapFile);
cScans = GpiSetBitmapBits( hpsBitmapFile
, 0L
, (LONG)pbfh->bmp.cy
, pImage + pbfh->offBits
, (PBITMAPINFO)&(pbfh->bmp));
if (cScans != (LONG)pbfh->bmp.cy) /* original number of scans ? */
goto ReadBitmap_free_bits;
}
fRet = TRUE; /* okey-dokey */
/**************************************************************************/
/* Close the file, free the buffer space and leave. This is a */
/* common exit point from the function. Since the same cleanup */
/* operations need to be performed for such a large number of */
/* possible error conditions, this is concise way to do the right */
/* thing. */
/**************************************************************************/
ReadBitmap_free_bits:
DosFreeSeg( sel);
ReadBitmap_close_file:
DosClose( hfile);
return fRet;
}