home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / JIGSAW.ZIP / JIGSAW.C < prev    next >
C/C++ Source or Header  |  1989-05-02  |  107KB  |  3,192 lines

  1. /********************************** Jigsaw ************************************/
  2. /*                                                                            */
  3. /* Created 1988, Microsoft Corporation.                       */
  4. /*                                                                            */
  5. /* Purpose:  To illustrate the use of Gpi.                      */
  6. /*                                                                            */
  7. /* Summary:  This program provides a jigsaw puzzle, based on a decomposition  */
  8. /*   of an arbitrary bitmap loaded from a file.  The user can jumble the      */
  9. /*   pieces, then drag them individually by means of the mouse.  The image    */
  10. /*   can be zoomed in and out and scrolled up/down and left/right.          */
  11. /*                                                                            */
  12. /*   JIGSAW uses BitBlt with clip paths to create a collection of picture     */
  13. /*   fragments which are the puzzle pieces.  Each piece is associated with    */
  14. /*   with a single retained segment and an auxiliary data structure, used     */
  15. /*   for drawing and for selection with the mouse.  To retain responsiveness  */
  16. /*   to user requests, the real work is done in a second thread, with          */
  17. /*   work requests transmitted from the main thread in the form of          */
  18. /*   messages.    This arrangement makes it possible for the user to over-      */
  19. /*   ride lengthy drawing operations with a higher-priority request          */
  20. /*   (eg. program termination, magnification change, etc.).              */
  21. /*                                                                            */
  22. /*   Individual pieces are made to "move" by changing their model transforms. */
  23. /*   Scrolling and zooming of the whole picture is done by changing the       */
  24. /*   default viewing transform.                           */
  25. /*                                                                            */
  26. /*                                                                            */
  27. /******************************************************************************/
  28.  
  29. #define INCL_BITMAPFILEFORMAT
  30.  
  31. #define INCL_DOSINFOSEG
  32. #define INCL_DOSPROCESS
  33. #define INCL_DOSSEMAPHORES
  34. #define INCL_DOSMEMMGR
  35.  
  36. #define INCL_DEV
  37.  
  38. #define INCL_WINWINDOWMGR
  39. #define INCL_WINMESSAGEMGR
  40. #define INCL_WININPUT
  41. #define INCL_WINRECTANGLES
  42. #define INCL_WINPOINTERS
  43. #define INCL_WINMENUS
  44. #define INCL_WINSCROLLBARS
  45. #define INCL_WINFRAMEMGR
  46. #define INCL_WINSWITCHLIST
  47. #define INCL_WINSYS
  48.  
  49. #define INCL_GPIBITMAPS
  50. #define INCL_GPICONTROL
  51. #define INCL_GPITRANSFORMS
  52. #define INCL_GPIPRIMITIVES
  53. #define INCL_GPIPATHS
  54. #define INCL_GPIREGIONS
  55. #define INCL_GPISEGMENTS
  56. #define INCL_GPICORRELATION
  57. #define INCL_GPILCIDS
  58.  
  59. #define INCL_ERRORS
  60.  
  61. #include <os2.h>
  62. #include <stdlib.h>
  63. #include <stdio.h>
  64. #include <string.h>
  65. #include <opendlg.h>
  66. #include "jigsaw.h"
  67.  
  68.  
  69.  
  70. /*----------------------- helper macros --------------------------------------*/
  71.  
  72. #define ROUND_DOWN_MOD( arg, mod)        \
  73.     {                        \
  74.     if( arg < 0)                \
  75.     {                    \
  76.         arg -= mod - 1;            \
  77.         arg += arg % mod;            \
  78.     } else                    \
  79.         arg -= arg % mod;            \
  80.     }
  81.  
  82. #define ROUND_UP_MOD( arg, mod)         \
  83.     {                        \
  84.     if( arg < 0)                \
  85.     {                    \
  86.         arg -= arg % mod;            \
  87.     } else                    \
  88.     {                    \
  89.         arg += mod - 1;            \
  90.         arg -= arg % mod;            \
  91.     }                    \
  92.     }
  93.  
  94.  
  95.  
  96. /*----------------------- inter-thread messages ------------------------------*/
  97.  
  98. #define UM_DIE          WM_USER+1        /* instruct async thread to terminate  */
  99. #define UM_DRAW       WM_USER+2        /* draw the current picture          */
  100. #define UM_VSCROLL    WM_USER+3        /* perform scroll by recalculating the */
  101.                                        /* default viewing transform           */
  102. #define UM_HSCROLL    WM_USER+4        /* perform scroll by recalculating the */
  103.                                        /* default viewing transform           */
  104. #define UM_SIZING     WM_USER+5        /* perform sizing by recalculating the */
  105.                                        /* default viewing transform           */
  106. #define UM_ZOOM       WM_USER+6        /* zoom the picture by recalculating   */
  107.                                        /* the default viewing transform       */
  108. #define UM_REDRAW     WM_USER+8        /* redraw the entire client window     */
  109. #define UM_JUMBLE     WM_USER+9        /* scatter the pieces on the window    */
  110. #define UM_LOAD       WM_USER+10       /* load a bitmap from disk          */
  111. #define UM_LEFTDOWN   WM_USER+11       /* correlate and prepare to drag       */
  112. #define UM_MOUSEMOVE  WM_USER+12       /* remove, reposition, and redraw      */
  113. #define UM_LEFTUP     WM_USER+13       /* stop dragging               */
  114. #define UM_LAST       WM_USER+15
  115.  
  116.  
  117. /*------------------------- correlation parameters ---------------------------*/
  118.  
  119. #define HITS    1L               /* maximum number of hits to return    */
  120. #define DEPTH    2L               /* max depth of seg calls to return    */
  121.  
  122.  
  123. /*-------------------------- general definitions -----------------------------*/
  124.  
  125.  
  126. HAB    habMain=NULL;               /* main thread anchor block handle     */
  127. HMQ    hmqMain=NULL;               /* main thread queue handle          */
  128. HWND    hwndFrame=NULL;                /* frame control handle                */
  129. HWND    hwndClient=NULL;           /* client area handle              */
  130. HDC    hdcClient=NULL;            /* window dc handle              */
  131. HPS    hpsClient=NULL;            /* client area Gpi ps handle          */
  132. SIZEL    sizlMaxClient;               /* max client area size              */
  133. HPS     hpsPaint=NULL;                 /* ps for use in Main Thread           */
  134. HRGN    hrgnInvalid = NULL;           /* handle to the invalid region          */
  135.  
  136. HAB    habAsync=NULL;               /* async thread anchor block handle    */
  137. HMQ    hmqAsync=NULL;               /* async thread queue handle          */
  138. TID     tidAsync;                      /* async thread id                     */
  139. SEL    selStack;               /* async thread stack selector          */
  140. #define STACKSIZE  4096            /* async thread stack size          */
  141. SHORT    sPrty = -1;               /* async thread priority           */
  142.  
  143. HWND    hwndHorzScroll=NULL;           /* horizontal scroll bar window          */
  144. HWND    hwndVertScroll=NULL;           /* vertical scroll bar window          */
  145. POINTS    ptsScrollPos, ptsOldScrollPos;
  146. POINTS    ptsScrollMax, ptsHalfScrollMax;
  147. POINTS    ptsScrollLine;
  148. POINTS    ptsScrollPage;
  149.  
  150. #define UNITY           65536L
  151. MATRIXLF matlfIdentity = { UNITY, 0, 0, 0, UNITY, 0, 0, 0, 1 };
  152. LONG    lScale;                /* current zoom level              */
  153. POINTL    ptlScaleRef;               /* scale factor, detects size change   */
  154. #define ZOOM_MAX    8
  155. #define ZOOM_IN     1
  156. #define ZOOM_OUT   -1
  157.  
  158. POINTL    ptlOffset;
  159. POINTL    ptlBotLeft  = { 0, 0};
  160. POINTL    ptlTopRight = { 3000, 3000};
  161. POINTL    ptlMoveStart;               /* model space point at start of move  */
  162. LONG    lLastSegId;               /* last segment id assigned to a piece */
  163. LONG    lPickedSeg = 0L;           /* seg id of piece selected for drag   */
  164. POINTL    ptlOffStart;               /* segment xform xlate at move start   */
  165. RECTL    rclBounds;               /* pict bounding box in model coords.  */
  166. POINTL    ptlOldMouse = {0L, 0L};        /* current mouse posn              */
  167. BOOL    fButtonDownMain = FALSE;       /* only drag if mouse down          */
  168. BOOL    fButtonDownAsync = FALSE;      /* only drag if mouse down          */
  169.  
  170. POINTL    ptlUpdtRef;
  171. POINTL    aptlUpdt[3];
  172. BOOL    fUpdtFirst;
  173. BOOL    fFirstLoad = TRUE;
  174.  
  175. /*-------------------------- segment list ------------------------------------*/
  176.  
  177. typedef struct _SEGLIST {           /* sl                      */
  178.   LONG            lSegId;
  179.   struct _SEGLIST FAR * pslPrev;
  180.   struct _SEGLIST FAR * pslNext;
  181.   POINTL        ptlLocation;   /* piece location, world coordinates   */
  182.   RECTL         rclCurrent;    /* segment bounding box, model coords  */
  183.   RECTL         rclBitBlt;     /* segment bounding box, world coords  */
  184.   POINTL        aptlBitBlt[4]; /* BitBlt parameters              */
  185.   LONG            lAdjacent[8];  /* adjacent segments in original pict  */
  186.   struct _SEGLIST FAR * pslNextIsland; /* next member in island list          */
  187.   BOOL            fIslandMark;   /* used for marking island members     */
  188.   POINTL        aptlSides[12]; /* control points for piece outline    */
  189.   HDC            hdcHole;       /* used to punch hole in target          */
  190.   HPS            hpsHole;
  191.   HBITMAP        hbmHole;
  192.   HDC            hdcFill;       /* used to fill hole              */
  193.   HPS            hpsFill;
  194.   HBITMAP        hbmFill;
  195.   POINTL        ptlModelXlate; /* lM31 and lM31 for piece          */
  196. } SEGLIST ;
  197. typedef SEGLIST FAR *PSEGLIST;           /* psl                      */
  198. typedef PSEGLIST FAR *PPSEGLIST;       /* ppsl                      */
  199. PSEGLIST pslHead = NULL;           /* head of the list              */
  200. PSEGLIST pslTail = NULL;           /* tail of the list              */
  201. PSEGLIST pslPicked = NULL;           /* picked segment's list member        */
  202. #define   ADD_HEAD_SEG     1
  203. #define   ADD_TAIL_SEG     2
  204. #define        DEL_SEG     3
  205. #define  MAKE_TAIL_SEG     4
  206.  
  207. /*-------------------------- bitmap-related data -----------------------------*/
  208.  
  209. typedef struct _LOADINFO {           /* li                      */
  210.     HFILE   hf;
  211.     CHAR    szFileName[MAX_FNAME_LEN];
  212. } LOADINFO ;
  213. typedef LOADINFO FAR *PLOADINFO;       /* pli                      */
  214.  
  215. HPS           hpsBitmapFile=NULL; /* bitmap straight from the file       */
  216. HDC           hdcBitmapFile=NULL;
  217. HBITMAP        hbmBitmapFile=NULL;
  218. BITMAPINFOHEADER   bmpBitmapFile   = {12L, 0, 0, 0, 0};
  219.  
  220. HPS           hpsBitmapSize=NULL; /* bitmap sized to the current size    */
  221. HDC           hdcBitmapSize=NULL;
  222. HBITMAP        hbmBitmapSize=NULL;
  223.  
  224. HPS           hpsBitmapBuff=NULL; /* image composed here, copied to scrn */
  225. HDC           hdcBitmapBuff=NULL;
  226. HBITMAP        hbmBitmapBuff=NULL;
  227.  
  228. HPS           hpsBitmapSave=NULL; /* save part of screen during dragging */
  229. HDC           hdcBitmapSave=NULL;
  230. HBITMAP        hbmBitmapSave=NULL;
  231. BITMAPINFOHEADER   bmpBitmapSave   = {12L, 0, 0, 0, 0};
  232.  
  233. DEVOPENSTRUC dop = { NULL
  234.            , "DISPLAY"
  235.            , NULL
  236.            , NULL
  237.            , NULL
  238.            , NULL
  239.            , NULL
  240.            , NULL
  241.            , NULL };
  242.  
  243.  
  244. /*-------------------------- old-style bitmap header -------------------------*/
  245.  
  246. typedef struct {
  247.     USHORT    wType;
  248.     ULONG     dwSize;
  249.     int       xHotspot;
  250.     int       yHotspot;
  251.     ULONG     dwBitsOffset;
  252.     USHORT    bmWidth;
  253.     USHORT    bmHeight;
  254.     USHORT    bmPlanes;
  255.     USHORT    bmBitcount;
  256. } RCBITMAP;
  257. typedef RCBITMAP FAR *PRCBITMAP;
  258.  
  259.  
  260. /*--------------------------- Miscellaneous ----------------------------------*/
  261.  
  262. ULONG    ulTerminateSem = 0;           /* main thread blocks while async dies */
  263. HSEM    hsemTerminate  = &ulTerminateSem;
  264.  
  265. ULONG    ulSzFmt   = 0;               /* serializes access to sprintf()      */
  266. HSEM    hsemSzFmt = &ulSzFmt;
  267. CHAR    szFmt[50];               /* buffer used by sprintf()          */
  268.  
  269. ULONG    ulDrawOn   = 0;            /* set iff draw enabled              */
  270. HSEM    hsemDrawOn = &ulDrawOn;
  271.  
  272. PSZ    pszLoadMsg  = "Patience, preparing puzzle pieces ...";
  273. ULONG    ulLoadMsg   = 0;           /* set iff load in progress          */
  274. HSEM    hsemLoadMsg = &ulLoadMsg;
  275.  
  276. SWCNTRL swctl = { 0, 0, 0, 0, 0, SWL_VISIBLE, SWL_JUMPABLE, NULL, 0 };
  277. HSWITCH hsw;                   /* handle to a switch list entry       */
  278. char    szTitle[80];               /* Title bar text              */
  279.  
  280. BOOL    fErrMem = FALSE;           /* set if alloc async stack fails      */
  281.  
  282. LONG    lByteAlignX, lByteAlignY;      /* memory alignment constants          */
  283.  
  284. /*------------------------- Function Prototypes ------------------------------*/
  285.  
  286. VOID     CalcBounds( VOID);
  287. VOID     CalcSize( MPARAM, MPARAM);
  288. VOID     CalcTransform( HWND);
  289. MRESULT CALLBACK ClientWndProc( HWND, USHORT, MPARAM, MPARAM);
  290. BOOL     CreateBitmapHdcHps( HDC, HPS);
  291. BOOL     CreateThread( VOID);
  292. BOOL     CreatePicture( VOID);
  293. BOOL     DoDraw( HPS, HRGN, BOOL);
  294. VOID     DoHorzScroll( VOID);
  295. VOID     DoVertScroll( VOID);
  296. VOID     DrawPiece( HPS, PSEGLIST, BOOL);
  297. BOOL     DumpPicture( VOID);
  298. VOID     Finalize( VOID);
  299. BOOL     Initialize( VOID);
  300. VOID     Jumble( VOID);
  301. VOID     LeftDown( MPARAM);
  302. VOID     LeftUp( VOID);
  303. VOID     Load( PLOADINFO);
  304. VOID cdecl main( VOID);
  305. VOID     MarkIsland( PSEGLIST, BOOL);
  306. VOID     MessageInt( HWND, INT, PCH);
  307. VOID     MouseMove( MPARAM);
  308. VOID     MyMessageBox( HWND, PSZ);
  309. VOID FAR NewThread( VOID);
  310. BOOL     PrepareBitmap( VOID);
  311. BOOL     ReadBitmap( HFILE);
  312. VOID     Redraw( VOID);
  313. VOID     ReportError( HAB);
  314. BOOL     SegListCheck( INT);
  315. PSEGLIST SegListGet( LONG);
  316. PSEGLIST SegListUpdate( USHORT, PSEGLIST);
  317. BOOL     SendCommand( USHORT, MPARAM, MPARAM);
  318. VOID     SetDVTransform( FIXED, FIXED, FIXED, FIXED, LONG, LONG, LONG);
  319. VOID     SetRect( PSEGLIST);
  320. VOID     ShowMessage( HPS, PSZ);
  321. VOID     ToggleMenuItem( USHORT, USHORT, PBOOL);
  322. MRESULT  WndProcCommand( HWND, USHORT, MPARAM, MPARAM);
  323. MRESULT  WndProcCreate( HWND);
  324. MRESULT  WndProcPaint( VOID);
  325. MRESULT  WndProcSize( MPARAM, MPARAM);
  326. VOID     Zoom( SHORT);
  327. VOID     ZoomMenuItems( VOID);
  328.  
  329.  
  330. /******************************************************************************/
  331. /*                                          */
  332. /*  MyMessageBox                                  */
  333. /*                                          */
  334. /*  Displays a message box with the given string.  To simplify matters,       */
  335. /*  the box will always have the same title ("Jigsaw"), will always          */
  336. /*  have a single button ("Ok"), will always have an exclamation point          */
  337. /*  icon, and will always be application modal.                   */
  338. /*                                          */
  339. /******************************************************************************/
  340. VOID
  341. MyMessageBox( hWnd, psz)
  342.  
  343. HWND  hWnd;
  344. PSZ   psz;
  345. {
  346.     WinMessageBox( HWND_DESKTOP
  347.          , hWnd
  348.          , psz
  349.          , szTitle
  350.          , NULL
  351.          , MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL );
  352. }
  353.  
  354. /******************************************************************************/
  355. /*                                                                            */
  356. /* Main thread will initialize the process for PM services and process          */
  357. /* the application message queue until a WM_QUIT message is received. It will */
  358. /* then destroy all PM resources and terminate. Any error during          */
  359. /* initialization will be reported and the process terminated.                */
  360. /*                                                                            */
  361. /******************************************************************************/
  362. VOID cdecl
  363. main()
  364. {
  365.   QMSG    qmsg;
  366.  
  367.   if( Initialize())
  368.       while( WinGetMsg( habMain, &qmsg, NULL, NULL, NULL))
  369.       WinDispatchMsg( habMain, &qmsg);
  370.   else
  371.       ReportError( habMain);
  372.   Finalize();
  373. }
  374.  
  375.  
  376. /******************************************************************************/
  377. /*                                                                            */
  378. /* The Initialize function will initialize the PM interface,              */
  379. /* create an application message queue, a standard frame window and a new     */
  380. /* thread to control drawing operations.  It will also initialize static      */
  381. /* strings.                                                                   */
  382. /*                                                                            */
  383. /******************************************************************************/
  384. BOOL
  385. Initialize()
  386. {
  387.   ULONG   flCreate;
  388.   PID      pid;
  389.   TID      tid;
  390.  
  391.   WinShowPointer( HWND_DESKTOP, TRUE);
  392.   habMain = WinInitialize( NULL);
  393.   if( !habMain)
  394.       return( FALSE);
  395.  
  396.   hmqMain = WinCreateMsgQueue( habMain,0);
  397.   if( !hmqMain)
  398.       return( FALSE);
  399.  
  400.   WinLoadString( habMain, NULL, TITLEBAR, sizeof(szTitle), szTitle);
  401.   if( !WinRegisterClass( habMain
  402.                , (PCH)szTitle
  403.                , (PFNWP)ClientWndProc
  404.                , CS_SIZEREDRAW
  405.                , 0 ))
  406.       return( FALSE);
  407.  
  408.   flCreate =   (FCF_STANDARD | FCF_VERTSCROLL | FCF_HORZSCROLL)
  409.          & ~(ULONG)FCF_TASKLIST;
  410.   hwndFrame = WinCreateStdWindow( HWND_DESKTOP
  411.                 , WS_VISIBLE
  412.                 , &flCreate
  413.                 , szTitle
  414.                 , szTitle
  415.                 , WS_VISIBLE
  416.                 , NULL
  417.                 , APPMENU
  418.                 , &hwndClient);
  419.   if( !hwndFrame)
  420.       return( FALSE);
  421.  
  422.   WinQueryWindowProcess( hwndFrame, &pid, &tid);
  423.   swctl.hwnd      = hwndFrame;
  424.   swctl.idProcess = pid;
  425.   lstrcpy( swctl.szSwtitle, szTitle);
  426.   hsw = WinAddSwitchEntry( &swctl);
  427.  
  428.   ZoomMenuItems();
  429.  
  430.   if( !CreateThread())              /* create async thread             */
  431.       return ( FALSE);
  432.   if( !CreateBitmapHdcHps( &hdcBitmapFile, &hpsBitmapFile))
  433.       return( FALSE);
  434.   if( !CreateBitmapHdcHps( &hdcBitmapSize, &hpsBitmapSize))
  435.       return( FALSE);
  436.   if( !CreateBitmapHdcHps( &hdcBitmapBuff, &hpsBitmapBuff))
  437.       return( FALSE);
  438.   if( !CreateBitmapHdcHps( &hdcBitmapSave, &hpsBitmapSave))
  439.       return( FALSE);
  440.  
  441.   return( TRUE);
  442. }
  443.  
  444. /******************************************************************************/
  445. /*                                                                            */
  446. /* Finalize will destroy the asynchronous drawing thread, all Presentation    */
  447. /* Manager resources, and terminate the process.                              */
  448. /*                                                                            */
  449. /******************************************************************************/
  450. VOID
  451. Finalize()
  452. {
  453.   if( tidAsync)
  454.   {
  455.       DosSemClear( hsemDrawOn);
  456.       DosSemSet( hsemTerminate);
  457.   }
  458.  
  459.   while( pslHead != NULL )
  460.   {
  461.     GpiSetBitmap( pslHead->hpsFill, NULL);
  462.     GpiDeleteBitmap( pslHead->hbmFill);
  463.     GpiDestroyPS( pslHead->hpsFill);
  464.     DevCloseDC( pslHead->hdcFill);
  465.  
  466.     GpiSetBitmap( pslHead->hpsHole, NULL);
  467.     GpiDeleteBitmap( pslHead->hbmHole);
  468.     GpiDestroyPS( pslHead->hpsHole);
  469.     DevCloseDC( pslHead->hdcHole);
  470.  
  471.     SegListUpdate( DEL_SEG, pslHead);
  472.   }
  473.   GpiDeleteSegments( hpsClient, 1L, lLastSegId);
  474.  
  475.   if( hrgnInvalid)
  476.       GpiDestroyRegion( hpsClient, hrgnInvalid);
  477.   if( hpsClient)
  478.   {
  479.       GpiAssociate( hpsClient, NULL);
  480.       GpiDestroyPS( hpsClient);
  481.   }
  482.   if( hpsPaint)
  483.       GpiDestroyPS( hpsPaint);
  484.  
  485.   if( hpsBitmapFile)
  486.   {
  487.       GpiSetBitmap( hpsBitmapFile, NULL);
  488.       GpiDeleteBitmap( hbmBitmapFile);
  489.       GpiDestroyPS( hpsBitmapFile);
  490.       DevCloseDC( hdcBitmapFile);
  491.   }
  492.   if( hpsBitmapSize)
  493.   {
  494.       GpiSetBitmap( hpsBitmapSize, NULL);
  495.       GpiDeleteBitmap( hbmBitmapSize);
  496.       GpiDestroyPS( hpsBitmapSize);
  497.       DevCloseDC( hdcBitmapSize);
  498.   }
  499.   if( hpsBitmapBuff)
  500.   {
  501.       GpiSetBitmap( hpsBitmapBuff, NULL);
  502.       GpiDeleteBitmap( hbmBitmapBuff);
  503.       GpiDestroyPS( hpsBitmapBuff);
  504.       DevCloseDC( hdcBitmapBuff);
  505.   }
  506.   if( hpsBitmapSave)
  507.   {
  508.       GpiSetBitmap( hpsBitmapSave, NULL);
  509.       GpiDeleteBitmap( hbmBitmapSave);
  510.       GpiDestroyPS( hpsBitmapSave);
  511.       DevCloseDC( hdcBitmapSave);
  512.   }
  513.  
  514.   if( hwndFrame)
  515.       WinDestroyWindow( hwndFrame);
  516.   if( hmqMain)
  517.       WinDestroyMsgQueue( hmqMain);
  518.   if( habMain)
  519.       WinTerminate( habMain);
  520.  
  521.   DosExit( EXIT_PROCESS, 0);
  522. }
  523.  
  524.  
  525. /******************************************************************************/
  526. /*                                                                            */
  527. /* ReportError    will display the latest error information for the required    */
  528. /* thread. No resources to be loaded if out of memory error.                  */
  529. /*                                                                            */
  530. /******************************************************************************/
  531. VOID
  532. ReportError( hab)
  533.  
  534. HAB hab;
  535. {
  536.   PERRINFO  perriBlk;
  537.   PSZ        pszErrMsg;
  538.   USHORT *  TempPtr;
  539.  
  540.   if( !hwndFrame)
  541.       return;
  542.   if( !fErrMem)
  543.   {
  544.       perriBlk = WinGetErrorInfo(hab);
  545.       if( !perriBlk)
  546.           return;
  547.       SELECTOROF( pszErrMsg) = SELECTOROF(perriBlk);
  548.       SELECTOROF( TempPtr)   = SELECTOROF(perriBlk);
  549.       OFFSETOF( TempPtr)     = perriBlk->offaoffszMsg;
  550.       OFFSETOF( pszErrMsg)   = *TempPtr;
  551.       WinMessageBox( HWND_DESKTOP
  552.            , hwndFrame
  553.            , pszErrMsg
  554.            , szTitle
  555.            , 0
  556.            , MB_CUACRITICAL | MB_ENTER);
  557.       WinFreeErrorInfo( perriBlk);
  558.   } else
  559.       WinMessageBox( HWND_DESKTOP
  560.            , hwndFrame
  561.            , "ERROR - Out Of Memory"
  562.            , szTitle
  563.            , 0
  564.            , MB_CUACRITICAL | MB_ENTER);
  565. }
  566.  
  567.  
  568. /******************************************************************************/
  569. /*                                                                            */
  570. /* CreateThread  creates the asynchronous drawing thread. It will allocate    */
  571. /* stack space and create the thread.                                         */
  572. /*                                                                            */
  573. /******************************************************************************/
  574. BOOL
  575. CreateThread()
  576. {
  577.   PBYTE pbAsyncStack;              /* long pointer to stack for new thread */
  578.  
  579.  
  580.   if( DosAllocSeg( STACKSIZE, (PSEL)&selStack, 0 ))
  581.   {
  582.       fErrMem = TRUE;
  583.       return( FALSE);
  584.   }
  585.   OFFSETOF(pbAsyncStack) = STACKSIZE-2;
  586.   SELECTOROF(pbAsyncStack) = selStack;
  587.   if( DosCreateThread( (PFNTHREAD)NewThread, &tidAsync, pbAsyncStack ))
  588.       return( FALSE);
  589.   return( TRUE);
  590. }
  591.  
  592.  
  593. /******************************************************************************/
  594. /*                                                                            */
  595. /* SendCommand    will attempt to post the required command and parameters to   */
  596. /* the asynchronous drawing thread's message queue. The command will only     */
  597. /* be posted if the queue exists.                          */
  598. /*                                                                            */
  599. /******************************************************************************/
  600. BOOL
  601. SendCommand( usCommand, mp1, mp2)
  602.  
  603. USHORT    usCommand;
  604. MPARAM    mp1, mp2;
  605. {
  606.   if( !hmqAsync)
  607.       return( FALSE);
  608.  
  609.   switch( usCommand)
  610.   {
  611.     case UM_DIE:
  612.     case UM_LEFTDOWN:
  613.     case UM_LEFTUP:
  614.     case UM_MOUSEMOVE:
  615.     case UM_DRAW:
  616.     case UM_HSCROLL:
  617.     case UM_VSCROLL:
  618.     case UM_ZOOM:
  619.     case UM_REDRAW:
  620.     case UM_SIZING:
  621.     case UM_JUMBLE:
  622.     case UM_LOAD:
  623.  
  624.     return( WinPostQueueMsg( hmqAsync
  625.                    , usCommand
  626.                    , mp1
  627.                    , mp2 ) );
  628.     break;
  629.  
  630.     default:
  631.     return( TRUE);
  632.   }
  633. }
  634.  
  635.  
  636. /******************************************************************************/
  637. /*                                                                            */
  638. /* ClientWndProd is the window procedure associated with the client window.   */
  639. /*                                                                            */
  640. /******************************************************************************/
  641. MRESULT CALLBACK
  642. ClientWndProc( hwnd, msg, mp1, mp2)
  643.  
  644. HWND    hwnd;
  645. USHORT  msg;
  646. MPARAM    mp1;
  647. MPARAM    mp2;
  648. {
  649.   CHAR  szTemp[128];
  650.  
  651.   switch( msg)
  652.   {
  653.     case WM_CREATE:
  654.       return( WndProcCreate( hwnd));
  655.       break;
  656.  
  657.     case WM_CLOSE:
  658.       WinLoadString( habMain, NULL, TERMINATE, sizeof(szTemp), (PSZ)szTemp );
  659.       if( WinMessageBox( HWND_DESKTOP
  660.                , hwndFrame
  661.                , szTemp
  662.                , szTitle
  663.                , 0
  664.                , MB_CUAWARNING | MB_YESNO | MB_DEFBUTTON2)
  665.            == MBID_YES)
  666.       WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
  667.       break;
  668.  
  669.     case WM_PAINT:
  670.       return( WndProcPaint());
  671.       break;
  672.  
  673.     /**************************************************************************/
  674.     /*                                          */
  675.     /**************************************************************************/
  676.     case WM_ERASEBACKGROUND:
  677.       return( TRUE);
  678.       break;
  679.  
  680.     /**************************************************************************/
  681.     /*                                          */
  682.     /**************************************************************************/
  683.     case WM_MINMAXFRAME:
  684.       if( (((PSWP)mp1)->fs & SWP_RESTORE)  ||
  685.       (((PSWP)mp1)->fs & SWP_MAXIMIZE) )
  686.     {
  687.     DosSemClear( hsemDrawOn);
  688.     SendCommand( UM_SIZING, 0, 0);
  689.     }
  690.       break;
  691.  
  692.     /**************************************************************************/
  693.     /* Process menu item commands, and commands generated from the keyboard   */
  694.     /* via the accelerator table. Most are handled by the async thread        */
  695.     /**************************************************************************/
  696.     case WM_COMMAND:
  697.       return( WndProcCommand( hwnd, msg, mp1, mp2));
  698.       break;
  699.  
  700.     /**************************************************************************/
  701.     /* Scrolling is handled by the async drawing thread. Simply pass on the   */
  702.     /* command and parameters                                                 */
  703.     /**************************************************************************/
  704.     case WM_HSCROLL:
  705.       SendCommand( UM_HSCROLL, mp2, 0);
  706.       break;
  707.  
  708.     case WM_VSCROLL:
  709.       SendCommand( UM_VSCROLL, mp2, 0);
  710.       break;
  711.  
  712.     /************************************************************************/
  713.     /* The client area is being resized.                                    */
  714.     /************************************************************************/
  715.     case WM_SIZE:
  716.       DosSemClear( hsemDrawOn);
  717.       SendCommand( UM_SIZING, mp1, mp2);
  718.       break;
  719.  
  720.     /**************************************************************************/
  721.     /* Mouse commands are handled by the async thread. Simply send on the     */
  722.     /* command and parameters.                                                */
  723.     /**************************************************************************/
  724.     case WM_BUTTON1DBLCLK:
  725.     case WM_BUTTON1DOWN:
  726.       if( hwnd != WinQueryFocus( HWND_DESKTOP, FALSE))
  727.       WinSetFocus( HWND_DESKTOP, hwnd);
  728.       if( !fButtonDownMain)
  729.       {
  730.       fButtonDownMain = TRUE;
  731.       SendCommand( UM_LEFTDOWN, mp1, 0);
  732.       }
  733.       return( TRUE);
  734.       break;
  735.  
  736.     case WM_BUTTON1UP:
  737.       if( !fButtonDownMain)
  738.       return( TRUE);
  739.       if( SendCommand( UM_LEFTUP, mp1, 0))
  740.       fButtonDownMain = FALSE;
  741.       else
  742.       WinAlarm( HWND_DESKTOP, WA_WARNING);
  743.       return( TRUE);
  744.       break;
  745.  
  746.     case WM_MOUSEMOVE:
  747.       if( fButtonDownMain)
  748.       SendCommand( UM_MOUSEMOVE, mp1, 0);
  749.       return( WinDefWindowProc( hwnd, msg, mp1, mp2));
  750.       break;
  751.  
  752.     /**************************************************************************/
  753.     /* Default for the rest                              */
  754.     /**************************************************************************/
  755.     default:
  756.       return( WinDefWindowProc( hwnd, msg, mp1, mp2));
  757.   }
  758.  
  759.   return( FALSE);
  760. }
  761.  
  762. /******************************************************************************/
  763. /*                                          */
  764. /* Get the maximum client area size.  Create a window DC for the client       */
  765. /* area and a normal GPI Presentation Space and associate the two.  The GPI   */
  766. /* PS will be the maximum client area size and be in pels.              */
  767. /*                                          */
  768. /******************************************************************************/
  769. MRESULT
  770. WndProcCreate( hwnd)
  771.  
  772. HWND  hwnd;
  773. {
  774.   SIZEL sizlPickApp;              /* pick aperture size              */
  775.  
  776.   sizlMaxClient.cx = WinQuerySysValue( HWND_DESKTOP, SV_CXFULLSCREEN);
  777.   sizlMaxClient.cy = WinQuerySysValue( HWND_DESKTOP, SV_CYFULLSCREEN);
  778.  
  779.   lByteAlignX = WinQuerySysValue( HWND_DESKTOP, SV_CXBYTEALIGN);
  780.   lByteAlignY = WinQuerySysValue( HWND_DESKTOP, SV_CYBYTEALIGN);
  781.  
  782.   hdcClient = WinOpenWindowDC( hwnd);
  783.   hpsClient = GpiCreatePS( habMain
  784.              , hdcClient
  785.              , &sizlMaxClient
  786.              , GPIA_ASSOC | PU_PELS );
  787.   if( !hpsClient)
  788.       return( TRUE);
  789.   GpiSetAttrMode( hpsClient, AM_PRESERVE);
  790.  
  791.   hwndHorzScroll = WinWindowFromID( WinQueryWindow( hwnd, QW_PARENT, FALSE)
  792.                   , FID_HORZSCROLL);
  793.  
  794.   hwndVertScroll = WinWindowFromID( WinQueryWindow( hwnd, QW_PARENT, FALSE)
  795.                   , FID_VERTSCROLL);
  796.  
  797.   hpsPaint = GpiCreatePS( habMain, NULL, &sizlMaxClient, PU_PELS);
  798.  
  799.   hrgnInvalid = GpiCreateRegion( hpsClient, 0L, NULL);
  800.  
  801.   sizlPickApp.cx = sizlPickApp.cy = 1;
  802.   GpiSetPickApertureSize( hpsClient, PICKAP_REC, &sizlPickApp);
  803.   return( FALSE);
  804. }
  805.  
  806.  
  807. /******************************************************************************/
  808. /*                                          */
  809. /* WM_PAINT message                                  */
  810. /*                                          */
  811. /******************************************************************************/
  812. MRESULT
  813. WndProcPaint()
  814.  
  815. {
  816.   HRGN     hrgnUpdt;
  817.   SHORT  sRgnType;
  818.  
  819.   hrgnUpdt = GpiCreateRegion( hpsPaint, 0L, NULL);
  820.   sRgnType = WinQueryUpdateRegion( hwndClient, hrgnUpdt);
  821.   SendCommand( UM_DRAW, (MPARAM)hrgnUpdt, 0);
  822.   if( DosSemWait( hsemLoadMsg, SEM_IMMEDIATE_RETURN))
  823.     ShowMessage( hpsClient, pszLoadMsg);
  824.   WinValidateRegion( hwndClient, hrgnUpdt, FALSE);
  825.   return( FALSE);
  826. }
  827.  
  828. /******************************************************************************/
  829. /* Process menu item commands, and commands generated from the keyboard via   */
  830. /* the accelerator table.  Most are handled by the async thread           */
  831. /******************************************************************************/
  832. MRESULT
  833. WndProcCommand( hwnd, msg, mp1, mp2)
  834.  
  835. HWND    hwnd;
  836. USHORT  msg;
  837. MPARAM    mp1, mp2;
  838. {
  839.   CHAR        szTemp[128];
  840.   DLF        dlf;
  841.   SEL        sel;
  842.   PLOADINFO pli;
  843.   PSZ        pszError, psz1, psz2;
  844.  
  845.   switch( SHORT1FROMMP(mp1))
  846.   {
  847.     case MENU_JUMBLE:
  848.     DosSemClear( hsemDrawOn);
  849.     SendCommand( UM_JUMBLE, 0, 0);
  850.     break;
  851.  
  852.     case MENU_LOAD:
  853.     DosAllocSeg( sizeof( LOADINFO), &sel, 0);
  854.     pli = MAKEP( sel, 0);
  855.  
  856.     dlf.rgbAction        = DLG_OPENDLG;
  857.     dlf.rgbFlags        = ATTRDIRLIST;
  858.     dlf.phFile        = &(pli->hf);
  859.     dlf.pszExt        = (PSZ)"\\*.bmp";
  860.     dlf.pszAppName        = szTitle;
  861.     dlf.pszTitle        = "Load Bitmap";
  862.     dlf.pszInstructions = NULL;
  863.     dlf.szFileName[0]   = '\0';
  864.     dlf.szOpenFile[0]   = '\0';
  865.     pszError        = "Error reading file.";
  866.  
  867.     switch( DlgFile( hwnd, &dlf))
  868.     {
  869.       case TDF_ERRMEM:
  870.       case TDF_INVALID:
  871.           MyMessageBox( hwnd, pszError);
  872.           break;
  873.  
  874.       case TDF_NOOPEN:
  875.           break;
  876.  
  877.       default:
  878.           for( psz1 = dlf.szFileName, psz2 = pli->szFileName
  879.          ; *psz2++ = *psz1++
  880.          ; )
  881.           ;
  882.           DosSemClear( hsemDrawOn);
  883.           SendCommand( UM_LOAD, (MPARAM)pli, 0);
  884.           break;
  885.     }
  886.     break;
  887.     /**********************************************************************/
  888.     /* EXIT command, menu item or F3 key pressed. Give the operator a      */
  889.     /* second chance, if confirmed post a WM_QUIT msg to the application  */
  890.     /* msg queue. This will force the MAIN thread to terminate.           */
  891.     /**********************************************************************/
  892.     case MENU_EXIT:
  893.       WinLoadString( habMain, NULL, TERMINATE, sizeof(szTemp), szTemp);
  894.       if( WinMessageBox( HWND_DESKTOP
  895.                , hwndFrame
  896.                , szTemp
  897.                , szTitle
  898.                , 0
  899.                , MB_CUAWARNING | MB_YESNO | MB_DEFBUTTON2)
  900.         == MBID_YES)
  901.     WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
  902.       break;
  903.  
  904.     /**********************************************************************/
  905.     /* Pass on the rest to the async thread.                  */
  906.     /**********************************************************************/
  907.     case MENU_ZOOMIN:
  908.       DosSemClear( hsemDrawOn);
  909.       SendCommand( UM_ZOOM, (MPARAM)ZOOM_IN, 0);
  910.       break;
  911.  
  912.     case MENU_ZOOMOUT:
  913.       DosSemClear( hsemDrawOn);
  914.       SendCommand( UM_ZOOM, (MPARAM)ZOOM_OUT , 0);
  915.       break;
  916.  
  917.     /**********************************************************************/
  918.     /* Unrecognised => default                          */
  919.     /**********************************************************************/
  920.     default:
  921.       return( WinDefWindowProc(hwnd, msg, mp1, mp2));
  922.   }
  923.   return( FALSE);
  924. }
  925.  
  926. /******************************************************************************/
  927. /* Put a message on the screen                              */
  928. /******************************************************************************/
  929. VOID
  930. ShowMessage( hps, psz)
  931.  
  932. HPS  hps;
  933. PSZ  psz;
  934. {
  935.   HRGN     hrgn;
  936.   RECTL  rclClient;
  937.   POINTL ptl;
  938.  
  939.   WinQueryWindowRect( hwndClient, &rclClient);
  940.   hrgn = GpiCreateRegion( hps, 1L, &rclClient);
  941.   GpiSetColor( hps, CLR_BACKGROUND);
  942.   GpiPaintRegion( hps, hrgn);
  943.   GpiDestroyRegion( hps, hrgn);
  944.  
  945.   GpiSetColor( hps, CLR_NEUTRAL);
  946.   ptl.x = 0L;
  947.   ptl.y = 0L;
  948.  
  949.   GpiMove( hps, (PPOINTL)&ptl);
  950.   GpiCharString( hps, (LONG)strlen( psz), psz);
  951. }
  952.  
  953. /******************************************************************************/
  954. /* Load a bitmap                                  */
  955. /******************************************************************************/
  956. VOID
  957. Load( pli)
  958.  
  959. PLOADINFO  pli;
  960. {
  961.     PSZ       pszError;
  962.     RECTL     rclClient;
  963.  
  964.     pszError = (PSZ)"Error reading file.";
  965.  
  966.     ShowMessage( hpsClient, pszLoadMsg);
  967.     DosSemSet( hsemLoadMsg);
  968.  
  969.     if( !fFirstLoad)
  970.       DumpPicture();
  971.  
  972.     if( !ReadBitmap( pli->hf) )
  973.     {
  974.       MyMessageBox( hwndClient, pszError);
  975.       DosSemClear( hsemLoadMsg);
  976.       return;
  977.     }
  978.     if( !PrepareBitmap() )
  979.     {
  980.       MyMessageBox( hwndClient, pszError);
  981.       DosSemClear( hsemLoadMsg);
  982.       return;
  983.     }
  984.  
  985.     lstrcpy( swctl.szSwtitle, szTitle);
  986.     lstrcat( swctl.szSwtitle, ": ");
  987.     lstrcat( swctl.szSwtitle, pli->szFileName);
  988.     WinChangeSwitchEntry( hsw, &swctl);
  989.     WinSetWindowText( hwndFrame, swctl.szSwtitle);
  990.  
  991.     WinQueryWindowRect( hwndClient, &rclClient);
  992.     ptsScrollMax.x = (SHORT)(rclClient.xRight - rclClient.xLeft);
  993.     ptsHalfScrollMax.x = ptsScrollMax.x >> 1;
  994.     ptsScrollPage.x = ptsScrollMax.x >> 3;
  995.     ROUND_DOWN_MOD( ptsScrollPage.x, (SHORT)lByteAlignX);
  996.     ptsScrollLine.x = ptsScrollMax.x >> 5;
  997.     ROUND_DOWN_MOD( ptsScrollLine.x, (SHORT)lByteAlignX);
  998.     ptsScrollPos.x = ptsHalfScrollMax.x;
  999.     ptsOldScrollPos.x = ptsHalfScrollMax.x;
  1000.     WinSendMsg( hwndHorzScroll
  1001.           , SBM_SETSCROLLBAR
  1002.           , MPFROMSHORT( ptsScrollPos.x)
  1003.           , MPFROM2SHORT( 1, ptsScrollMax.x) );
  1004.     ptsScrollMax.y = (SHORT)(rclClient.yTop - rclClient.yBottom);
  1005.     ptsHalfScrollMax.y = ptsScrollMax.y >> 1;
  1006.     ptsScrollPage.y = ptsScrollMax.y >> 3;
  1007.     ROUND_DOWN_MOD( ptsScrollPage.y, (SHORT)lByteAlignY);
  1008.     ptsScrollLine.y = ptsScrollMax.y >> 5;
  1009.     ROUND_DOWN_MOD( ptsScrollLine.y, (SHORT)lByteAlignY);
  1010.     ptsScrollPos.y = ptsHalfScrollMax.y;
  1011.     ptsOldScrollPos.y = ptsHalfScrollMax.y;
  1012.     WinSendMsg( hwndVertScroll
  1013.           , SBM_SETSCROLLBAR
  1014.           , MPFROMSHORT( ptsScrollPos.y)
  1015.           , MPFROM2SHORT( 1, ptsScrollMax.y) );
  1016.  
  1017.     CreatePicture();
  1018.     lScale = 0;
  1019.  
  1020.     CalcBounds();
  1021.     ptlScaleRef.x = ptlScaleRef.y = 0L;
  1022.     CalcTransform( hwndClient);
  1023.     DosFreeSeg( SELECTOROF( pli));
  1024.     fFirstLoad = FALSE;
  1025.     DosSemClear( hsemLoadMsg);
  1026. }
  1027.  
  1028. /******************************************************************************/
  1029. /* Throw the pieces around the screen.                          */
  1030. /******************************************************************************/
  1031. VOID
  1032. Jumble()
  1033. {
  1034.   LONG        lWidth, lHeight;
  1035.   DATETIME  date;
  1036.   POINTL    ptl;
  1037.   RECTL     rclClient;
  1038.   PSEGLIST  psl;
  1039.   MATRIXLF  matlf;
  1040.  
  1041.   if( WinQueryWindowRect( hwndClient, &rclClient) )
  1042.   {
  1043.     lWidth  = rclClient.xRight - rclClient.xLeft;
  1044.     lHeight = rclClient.yTop   - rclClient.yBottom;
  1045.     if( (lWidth > 0) && (lHeight > 0) )
  1046.     {
  1047.       matlf = matlfIdentity;
  1048.       DosGetDateTime( &date);
  1049.       srand( (USHORT)date.hundredths);
  1050.       for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  1051.       {
  1052.     psl->pslNextIsland = psl;    /* reset island pointer              */
  1053.     psl->fIslandMark = FALSE;    /* clear island mark              */
  1054.     ptl.x = rclClient.xLeft   + (rand() % lWidth);
  1055.     ptl.y = rclClient.yBottom + (rand() % lHeight);
  1056.     GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptl);
  1057.     ptl.x = 50 * (ptl.x / 50) - 250;
  1058.     ptl.y = 50 * (ptl.y / 50) - 250;
  1059.     matlf.lM31 = ptl.x - psl->ptlLocation.x;
  1060.     matlf.lM32 = ptl.y - psl->ptlLocation.y;
  1061.     GpiSetSegmentTransformMatrix( hpsClient
  1062.                     , psl->lSegId
  1063.                     , 9L
  1064.                     , &matlf
  1065.                     , TRANSFORM_REPLACE);
  1066.     psl->ptlModelXlate.x = matlf.lM31;
  1067.     psl->ptlModelXlate.y = matlf.lM32;
  1068.     SetRect( psl);
  1069.       }
  1070.     }
  1071.   }
  1072. }
  1073.  
  1074. /******************************************************************************/
  1075. /*                                                                            */
  1076. /* NewThread is the asynchronous drawing thread. It is responsible for all    */
  1077. /* drawing.  It will initialize its PM interface and create an application    */
  1078. /* message queue.  It will then monitor its message queue and process any     */
  1079. /* commands received.                                  */
  1080. /*                                                                            */
  1081. /******************************************************************************/
  1082. VOID FAR
  1083. NewThread()
  1084. {
  1085.   QMSG      qmsgAsync, qmsgPeek;
  1086.   BOOL      fDone;
  1087.   SHORT   sZoomArg;
  1088.   POINTL  aptlDraw[3];
  1089.  
  1090.   /****************************************************************************/
  1091.   /* Initialize the PM interface.  If it fails, terminate both threads.       */
  1092.   /****************************************************************************/
  1093.   habAsync = WinInitialize( NULL);
  1094.   if( !habAsync)
  1095.   {
  1096.       WinPostMsg( hwndClient, WM_QUIT, NULL, NULL);
  1097.       DosExit( EXIT_THREAD, 0);
  1098.   }
  1099.  
  1100.   /****************************************************************************/
  1101.   /* Create a message queue.  If it fails, terminate both threads.          */
  1102.   /****************************************************************************/
  1103.   hmqAsync = WinCreateMsgQueue( habAsync, 150);
  1104.   if( !hmqAsync)
  1105.   {
  1106.       WinPostMsg( hwndClient, WM_QUIT, NULL, NULL);
  1107.       WinTerminate( habAsync);
  1108.       DosExit( EXIT_THREAD, 0);
  1109.   }
  1110.  
  1111.   DosSetPrty( PRTYS_THREAD, PRTYC_NOCHANGE, sPrty, (TID)NULL);
  1112.  
  1113.  
  1114.   while( TRUE)
  1115.   {
  1116.     WinGetMsg( habAsync, &qmsgAsync, NULL, 0, 0);
  1117.  
  1118.     if( WinPeekMsg( habAsync, &qmsgPeek, NULL, UM_DIE, UM_DIE, PM_NOREMOVE))
  1119.     qmsgAsync = qmsgPeek;
  1120.  
  1121.     if( WinPeekMsg( habAsync, &qmsgPeek, NULL, UM_SIZING, UM_LOAD, PM_NOREMOVE))
  1122.     DosSemClear( hsemDrawOn);
  1123.     else
  1124.     DosSemSet( hsemDrawOn);
  1125.  
  1126.     /**************************************************************************/
  1127.     /* process the commands                                                   */
  1128.     /**************************************************************************/
  1129.     switch( qmsgAsync.msg)
  1130.     {
  1131.  
  1132.       /************************************************************************/
  1133.       /************************************************************************/
  1134.       case UM_LOAD:
  1135.     Load( (PLOADINFO)qmsgAsync.mp1);
  1136.     Redraw();
  1137.     break;
  1138.  
  1139.       /************************************************************************/
  1140.       case UM_JUMBLE:
  1141.     Jumble();
  1142.     Redraw();
  1143.     break;
  1144.  
  1145.       /************************************************************************/
  1146.       case UM_REDRAW:
  1147.     Redraw();
  1148.     break;
  1149.  
  1150.       /************************************************************************/
  1151.       /* DRAW will use the passed region containing the invalidated area of   */
  1152.       /* the screen, repaint it and then destroy the region.              */
  1153.       /************************************************************************/
  1154.       case UM_DRAW:
  1155.  
  1156.     if( qmsgAsync.mp1)
  1157.     {
  1158.       DoDraw( hpsBitmapBuff, (HRGN)qmsgAsync.mp1, TRUE);
  1159.       GpiQueryRegionBox( hpsClient, (HRGN)qmsgAsync.mp1, (PRECTL)aptlDraw);
  1160.       GpiDestroyRegion( hpsClient, (HRGN)qmsgAsync.mp1);
  1161.       WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlDraw, 3);
  1162.       ROUND_DOWN_MOD( aptlDraw[0].x, lByteAlignX);      /* round down       */
  1163.       ROUND_DOWN_MOD( aptlDraw[0].y, lByteAlignY);      /* round down       */
  1164.       ROUND_UP_MOD(   aptlDraw[1].x, lByteAlignX);      /* round up          */
  1165.       ROUND_UP_MOD(   aptlDraw[1].y, lByteAlignY);      /* round up          */
  1166.       WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlDraw, 3);
  1167.       aptlDraw[2] = aptlDraw[0];
  1168.       GpiBitBlt( hpsClient
  1169.            , hpsBitmapBuff
  1170.            , 3L
  1171.            , aptlDraw
  1172.            , ROP_SRCCOPY
  1173.            , BBO_IGNORE );
  1174.     }
  1175.         break;
  1176.  
  1177.  
  1178.       /************************************************************************/
  1179.       /* Get new scroll posn from command ( i.e. +/-1 +/-page) or new          */
  1180.       /* absolute position from parameter, update scroll posn, change the     */
  1181.       /* transform and update the thumb posn.  Finally update the window.     */
  1182.       /************************************************************************/
  1183.       case UM_HSCROLL:
  1184.     switch( SHORT2FROMMP( qmsgAsync.mp1) )
  1185.     {
  1186.             case SB_LINEUP:
  1187.         ptsScrollPos.x -= ptsScrollLine.x;
  1188.                 break;
  1189.             case SB_LINEDOWN:
  1190.         ptsScrollPos.x += ptsScrollLine.x;
  1191.                 break;
  1192.         case SB_SLIDERTRACK:
  1193.             case SB_SLIDERPOSITION:
  1194.         for( fDone = FALSE; !fDone ;)
  1195.         {
  1196.           if( WinPeekMsg( habAsync
  1197.                 , &qmsgPeek
  1198.                 , NULL
  1199.                 , UM_HSCROLL
  1200.                 , UM_HSCROLL
  1201.                 , PM_NOREMOVE))
  1202.               if(   (SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERTRACK)
  1203.               ||(SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERPOSITION) )
  1204.               WinPeekMsg( habAsync
  1205.                     , &qmsgAsync
  1206.                     , NULL
  1207.                     , UM_HSCROLL
  1208.                     , UM_HSCROLL
  1209.                     , PM_REMOVE);
  1210.               else
  1211.               fDone = TRUE;
  1212.           else
  1213.               fDone = TRUE;
  1214.         }
  1215.         ptsScrollPos.x = SHORT1FROMMP( qmsgAsync.mp1);
  1216.         ROUND_DOWN_MOD( ptsScrollPos.x, (SHORT)lByteAlignX);
  1217.                 break;
  1218.             case SB_PAGEUP:
  1219.         ptsScrollPos.x -= ptsScrollPage.x;
  1220.                 break;
  1221.             case SB_PAGEDOWN:
  1222.         ptsScrollPos.x += ptsScrollPage.x;
  1223.                 break;
  1224.             case SB_ENDSCROLL:
  1225.                 break;
  1226.             default:
  1227.                 break;
  1228.     }
  1229.     DoHorzScroll();
  1230.         break;
  1231.  
  1232.       case UM_VSCROLL:
  1233.     switch( SHORT2FROMMP( qmsgAsync.mp1) )
  1234.     {
  1235.             case SB_LINEUP:
  1236.         ptsScrollPos.y -= ptsScrollLine.y;
  1237.                 break;
  1238.             case SB_LINEDOWN:
  1239.         ptsScrollPos.y += ptsScrollLine.y;
  1240.                 break;
  1241.         case SB_SLIDERTRACK:
  1242.             case SB_SLIDERPOSITION:
  1243.         for( fDone = FALSE; !fDone ;)
  1244.         {
  1245.           if( WinPeekMsg( habAsync
  1246.                 , &qmsgPeek
  1247.                 , NULL
  1248.                 , UM_VSCROLL
  1249.                 , UM_VSCROLL
  1250.                 , PM_NOREMOVE))
  1251.               if(   (SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERTRACK)
  1252.               ||(SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERPOSITION) )
  1253.               WinPeekMsg( habAsync
  1254.                     , &qmsgAsync
  1255.                     , NULL
  1256.                     , UM_VSCROLL
  1257.                     , UM_VSCROLL
  1258.                     , PM_REMOVE);
  1259.               else
  1260.               fDone = TRUE;
  1261.           else
  1262.               fDone = TRUE;
  1263.         }
  1264.         ptsScrollPos.y = SHORT1FROMMP( qmsgAsync.mp1);
  1265.         ROUND_DOWN_MOD( ptsScrollPos.y, (SHORT)lByteAlignY);
  1266.                 break;
  1267.             case SB_PAGEUP:
  1268.         ptsScrollPos.y -= ptsScrollPage.y;
  1269.                 break;
  1270.             case SB_PAGEDOWN:
  1271.         ptsScrollPos.y += ptsScrollPage.y;
  1272.                 break;
  1273.             case SB_ENDSCROLL:
  1274.                 break;
  1275.             default:
  1276.                 break;
  1277.     }
  1278.     DoVertScroll();
  1279.         break;
  1280.  
  1281.       /************************************************************************/
  1282.       /* the window is being resized                          */
  1283.       /************************************************************************/
  1284.       case UM_SIZING:
  1285.     CalcBounds();
  1286.     CalcTransform( hwndClient);
  1287.         break;
  1288.  
  1289.       /************************************************************************/
  1290.       /* adjust zoom factor                                                   */
  1291.       /************************************************************************/
  1292.       case UM_ZOOM:
  1293.     sZoomArg = (SHORT)qmsgAsync.mp1;
  1294.     while( WinPeekMsg( habAsync
  1295.              , &qmsgPeek
  1296.              , NULL
  1297.              , WM_USER
  1298.              , UM_LAST
  1299.              , PM_NOREMOVE))
  1300.     {
  1301.         if( qmsgPeek.msg == UM_ZOOM)
  1302.         WinPeekMsg( habAsync
  1303.               , &qmsgPeek
  1304.               , NULL
  1305.               , WM_USER
  1306.               , UM_LAST
  1307.               , PM_REMOVE);
  1308.         else
  1309.         break;
  1310.         sZoomArg += (SHORT)qmsgPeek.mp1;
  1311.     }
  1312.     if( WinPeekMsg( habAsync
  1313.               , &qmsgPeek
  1314.               , NULL
  1315.               , UM_SIZING
  1316.               , UM_LOAD
  1317.               , PM_NOREMOVE))
  1318.         DosSemClear( hsemDrawOn);
  1319.     else
  1320.         DosSemSet( hsemDrawOn);
  1321.     Zoom( sZoomArg);
  1322.         break;
  1323.  
  1324.       /************************************************************************/
  1325.       /* Button down will cause a correlate on the picture to test for a hit. */
  1326.       /* Any selected segment will be highlighted and redrawn as dynamic.     */
  1327.       /************************************************************************/
  1328.       case UM_LEFTDOWN:
  1329.     if( !fButtonDownAsync)
  1330.     {
  1331.         fButtonDownAsync = TRUE;
  1332.         LeftDown( qmsgAsync.mp1);
  1333.     }
  1334.         break;
  1335.  
  1336.       /************************************************************************/
  1337.       /* if a segment is being dragged it will be redrawn in a new posn       */
  1338.       /************************************************************************/
  1339.       case UM_MOUSEMOVE:
  1340.     if( !fButtonDownAsync)
  1341.         break;
  1342.     for( fDone = FALSE; !fDone ;)
  1343.     {
  1344.       if( WinPeekMsg( habAsync          /* look through first button-up */
  1345.             , &qmsgPeek
  1346.             , NULL
  1347.             , UM_MOUSEMOVE
  1348.             , UM_LEFTUP
  1349.             , PM_NOREMOVE))
  1350.           if( qmsgPeek.msg == UM_MOUSEMOVE) /* only collapse move msgs    */
  1351.           WinPeekMsg( habAsync
  1352.                 , &qmsgAsync
  1353.                 , NULL
  1354.                 , UM_MOUSEMOVE
  1355.                 , UM_MOUSEMOVE
  1356.                 , PM_REMOVE);
  1357.           else
  1358.           fDone = TRUE;
  1359.       else
  1360.           fDone = TRUE;
  1361.     }
  1362.     MouseMove( qmsgAsync.mp1);    /* process last move before button-up */
  1363.         break;
  1364.  
  1365.       /************************************************************************/
  1366.       /* if a segment is being dragged it will be redrawn as normal          */
  1367.       /************************************************************************/
  1368.       case UM_LEFTUP:
  1369.     if( fButtonDownAsync)
  1370.     {
  1371.         LeftUp();
  1372.         fButtonDownAsync = FALSE;
  1373.     }
  1374.         break;
  1375.  
  1376.       /************************************************************************/
  1377.       /* destroy resources and terminate                     */
  1378.       /************************************************************************/
  1379.       case UM_DIE:
  1380.     WinDestroyMsgQueue( hmqAsync);
  1381.     WinTerminate( habAsync);
  1382.         DosExit( EXIT_THREAD, 0);
  1383.         break;
  1384.  
  1385.       /************************************************************************/
  1386.       default:
  1387.         break;
  1388.     }
  1389.   }
  1390. }
  1391.  
  1392. /******************************************************************************/
  1393. /*                                          */
  1394. /******************************************************************************/
  1395. VOID CalcSize( mp1, mp2)
  1396.  
  1397. MPARAM    mp1, mp2;
  1398. {
  1399.   ptsScrollMax.y = SHORT2FROMMP( mp2);
  1400.   ptsHalfScrollMax.y = ptsScrollMax.y >> 1;
  1401.   ptsScrollPage.x = ptsScrollMax.x >> 3;
  1402.   ROUND_DOWN_MOD( ptsScrollPage.x, (SHORT)lByteAlignX);
  1403.   ptsScrollLine.x = ptsScrollMax.x >> 5;
  1404.   ROUND_DOWN_MOD( ptsScrollLine.x, (SHORT)lByteAlignX);
  1405.   ptsScrollPos.y = (SHORT)(
  1406.                (  (LONG)ptsScrollPos.y
  1407.                 * (LONG)SHORT2FROMMP(mp2)
  1408.                )/ (LONG)SHORT2FROMMP(mp1)
  1409.               );
  1410.   ptsOldScrollPos.y = (SHORT)(
  1411.                   (  (LONG)ptsOldScrollPos.y
  1412.                    * (LONG)SHORT2FROMMP(mp2)
  1413.                   )/ (LONG)SHORT2FROMMP(mp1)
  1414.                  );
  1415.   WinSendMsg( hwndVertScroll
  1416.         , SBM_SETSCROLLBAR
  1417.         , MPFROMSHORT( ptsScrollPos.y)
  1418.         , MPFROM2SHORT( 1, ptsScrollMax.y) );
  1419.  
  1420.   ptsScrollMax.x = SHORT1FROMMP( mp2);
  1421.   ptsHalfScrollMax.x = ptsScrollMax.x >> 1;
  1422.   ptsScrollPage.y = ptsScrollMax.y >> 3;
  1423.   ROUND_DOWN_MOD( ptsScrollPage.y, (SHORT)lByteAlignY);
  1424.   ptsScrollLine.y = ptsScrollMax.y >> 5;
  1425.   ROUND_DOWN_MOD( ptsScrollLine.y, (SHORT)lByteAlignY);
  1426.   ptsScrollPos.x = (SHORT)(
  1427.                (  (LONG)ptsScrollPos.x
  1428.                 * (LONG)SHORT1FROMMP(mp2)
  1429.                )/(LONG)SHORT1FROMMP(mp1)
  1430.               );
  1431.   ptsOldScrollPos.x = (SHORT)(
  1432.                   (  (LONG)ptsOldScrollPos.x
  1433.                    * (LONG)SHORT1FROMMP(mp2)
  1434.                   )/ (LONG)SHORT1FROMMP(mp1)
  1435.                  );
  1436.   WinSendMsg( hwndHorzScroll
  1437.         , SBM_SETSCROLLBAR
  1438.         , MPFROMSHORT( ptsScrollPos.x)
  1439.         , MPFROM2SHORT( 1, ptsScrollMax.x) );
  1440. }
  1441.  
  1442. /******************************************************************************/
  1443. /* button down will cause one segment to be indicated and made dynamic          */
  1444. /******************************************************************************/
  1445. VOID
  1446. LeftDown( mp)
  1447.  
  1448. MPARAM     mp;
  1449. {
  1450. #ifdef fred
  1451.   LONG        alSegTag[HITS][DEPTH][2];
  1452. #endif
  1453.   POINTL  aptl[2];
  1454.   LONG      lColor;
  1455.  
  1456.   POINTL    ptl;
  1457.   HRGN        hrgn, hrgnUpdt, hrgnUpdtDrag;
  1458.   RECTL     rcl;
  1459.   MATRIXLF  matlf;
  1460.   CHAR        pszMsg[40];
  1461.   PSZ        psz1, psz2;
  1462.   BOOL        fFirst;
  1463.   PSEGLIST  psl;
  1464.  
  1465.   ptl.x = (LONG)(SHORT)SHORT1FROMMP( mp);
  1466.   ptl.y = (LONG)(SHORT)SHORT2FROMMP( mp);
  1467.  
  1468.   /****************************************************************************/
  1469.   /****************************************************************************/
  1470.   aptl[0] = aptl[1] = ptl;
  1471.   aptl[1].x++;
  1472.   aptl[1].y++;
  1473.   GpiBitBlt( hpsBitmapSave, NULL, 2L, aptl, ROP_ONE, BBO_IGNORE);
  1474.   lColor = GpiQueryPel( hpsBitmapSave, &ptl);
  1475.   for( pslPicked = pslTail; pslPicked != NULL; pslPicked = pslPicked->pslPrev)
  1476.   {
  1477.     rcl = pslPicked->rclCurrent;
  1478.     GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
  1479.     rcl.xRight++;
  1480.     rcl.yTop++;
  1481.     if( WinPtInRect( habAsync, &rcl, &ptl))    /*  is point in bounding box?  */
  1482. #ifdef fred
  1483.     if( 0L < GpiCorrelateSegment( hpsClient
  1484.                     , pslPicked->lSegId
  1485.                     , PICKSEL_VISIBLE
  1486.                     , &ptl
  1487.                     , HITS
  1488.                     , DEPTH
  1489.                     , (PLONG)alSegTag ))
  1490. #endif
  1491.     {
  1492.     DrawPiece( hpsBitmapSave, pslPicked, FALSE);
  1493.     if( GpiQueryPel( hpsBitmapSave, &ptl) != lColor)
  1494.         break;                   /*  got a hit              */
  1495.     }
  1496.   }
  1497.   if( pslPicked)
  1498.     lPickedSeg     = pslPicked->lSegId;
  1499.   else
  1500.   {
  1501.     fButtonDownAsync = FALSE;
  1502.     return;
  1503.   }
  1504.   if( (lPickedSeg < 1) || (lPickedSeg > lLastSegId) )
  1505.   {
  1506.     DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
  1507.     sprintf( szFmt, "Segment id out of range: %x", lPickedSeg);
  1508.     for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
  1509.     ;
  1510.     DosSemClear( hsemSzFmt);
  1511.     MyMessageBox( hwndClient, pszMsg);
  1512.     fButtonDownAsync = FALSE;
  1513.     return;
  1514.   }
  1515.  
  1516.   /****************************************************************************/
  1517.   GpiQuerySegmentTransformMatrix( hpsClient, lPickedSeg, 9L, &matlf );
  1518.   ptlOffStart.x = matlf.lM31;
  1519.   ptlOffStart.y = matlf.lM32;
  1520.   ptlMoveStart = ptl;
  1521.   GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptlMoveStart);
  1522.   ptlMoveStart.x = (ptlMoveStart.x / 50) * 50;
  1523.   ptlMoveStart.y = (ptlMoveStart.y / 50) * 50;
  1524.   ptlUpdtRef = ptlMoveStart;
  1525.   GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 1L, &ptlUpdtRef);
  1526.  
  1527.   /****************************************************************************/
  1528.   hrgnUpdt = GpiCreateRegion( hpsClient, 0L, NULL);
  1529.   for( psl = pslPicked, fFirst = TRUE
  1530.      ; (psl != pslPicked) || fFirst
  1531.      ; psl = psl->pslNextIsland, fFirst = FALSE )
  1532.   {
  1533.     rcl = psl->rclCurrent;   /* get model space bounding box of piece          */
  1534.     GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
  1535.     rcl.xRight++;         /* adjust rectangle for conversion to dev space  */
  1536.     rcl.yTop++;
  1537.     rcl.xRight    += 2;                 /* should not need          */
  1538.     rcl.yTop    += 2;                 /* should not need          */
  1539.     rcl.xLeft    -= 4;                 /* should not need          */
  1540.     rcl.yBottom -= 4;                 /* should not need          */
  1541.     hrgn = GpiCreateRegion( hpsClient, 1L, &rcl);
  1542.     GpiCombineRegion( hpsClient, hrgnUpdt, hrgnUpdt, hrgn, CRGN_OR);
  1543.     GpiDestroyRegion( hpsClient, hrgn);
  1544.  
  1545.     GpiSetSegmentAttrs( hpsClient, psl->lSegId, ATTR_VISIBLE, ATTR_OFF);
  1546.   }
  1547.  
  1548.   GpiQueryRegionBox( hpsClient, hrgnUpdt, (PRECTL)aptlUpdt);
  1549.   WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlUpdt, 3);
  1550.   ROUND_DOWN_MOD( aptlUpdt[0].x, lByteAlignX);          /* round down       */
  1551.   ROUND_DOWN_MOD( aptlUpdt[0].y, lByteAlignY);          /* round down       */
  1552.   ROUND_UP_MOD(   aptlUpdt[1].x, lByteAlignX);          /* round up          */
  1553.   ROUND_UP_MOD(   aptlUpdt[1].y, lByteAlignY);          /* round up          */
  1554.   WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlUpdt, 3);
  1555.   hrgnUpdtDrag = GpiCreateRegion( hpsBitmapBuff, 1L, (PRECTL)aptlUpdt);
  1556.  
  1557.   aptlUpdt[2] = aptlUpdt[0];
  1558.   DoDraw( hpsBitmapBuff, hrgnUpdtDrag, TRUE);
  1559.   GpiDestroyRegion( hpsClient, hrgnUpdt);
  1560.   GpiDestroyRegion( hpsBitmapBuff, hrgnUpdtDrag);
  1561.   GpiBitBlt( hpsBitmapSave
  1562.        , hpsBitmapBuff
  1563.        , 3L
  1564.        , aptlUpdt
  1565.        , ROP_SRCCOPY
  1566.        , BBO_IGNORE );
  1567.  
  1568.   /****************************************************************************/
  1569.   for( psl = pslPicked, fFirst = TRUE
  1570.      ; (psl != pslPicked) || fFirst
  1571.      ; psl = psl->pslNextIsland, fFirst = FALSE )
  1572.   {
  1573.     GpiSetSegmentAttrs( hpsClient, psl->lSegId, ATTR_VISIBLE, ATTR_ON);
  1574.     DrawPiece( hpsBitmapBuff, psl, TRUE);
  1575.   }
  1576.   GpiBitBlt( hpsClient
  1577.        , hpsBitmapBuff
  1578.        , 3L
  1579.        , aptlUpdt
  1580.        , ROP_SRCCOPY
  1581.        , BBO_IGNORE );
  1582.   WinSetCapture( HWND_DESKTOP, hwndClient);
  1583. }
  1584.  
  1585.  
  1586.  
  1587.  
  1588. /******************************************************************************/
  1589. /*                                                                            */
  1590. /* move the segment                                  */
  1591. /*                                                                            */
  1592. /******************************************************************************/
  1593. VOID
  1594. MouseMove( mp)
  1595.  
  1596. MPARAM     mp;
  1597. {
  1598.   RECTL     rcl;
  1599.   POINTL    ptl, ptlModel, ptlDevice;
  1600.   POINTL    aptlUpdtRef[3], aptlUpdtNew[3];
  1601.   PSEGLIST  psl;
  1602.   BOOL        fFirst;
  1603.   MATRIXLF  matlf;
  1604.  
  1605.   if( !lPickedSeg || !pslPicked)
  1606.     return;
  1607.  
  1608.   ptl.x = (LONG)(SHORT)SHORT1FROMMP( mp);
  1609.   ptl.y = (LONG)(SHORT)SHORT2FROMMP( mp);
  1610.  
  1611.   /****************************************************************************/
  1612.   /* clip mouse coords to client window                       */
  1613.   /****************************************************************************/
  1614.   WinQueryWindowRect( hwndClient, &rcl);
  1615.   if (rcl.xLeft > ptl.x)
  1616.     ptl.x = rcl.xLeft;
  1617.   if (rcl.xRight <= ptl.x)
  1618.     ptl.x = rcl.xRight;
  1619.   if (rcl.yBottom > ptl.y)
  1620.     ptl.y = rcl.yBottom;
  1621.   if (rcl.yTop <= ptl.y)
  1622.     ptl.y = rcl.yTop;
  1623.  
  1624.   ptlModel = ptl;
  1625.   GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptlModel);
  1626.   ptlModel.x = 50 * (ptlModel.x / 50);
  1627.   ptlModel.y = 50 * (ptlModel.y / 50);
  1628.   if( (ptlModel.x == ptlOldMouse.x) && (ptlModel.y == ptlOldMouse.y))
  1629.     return;
  1630.   ptlOldMouse.x = ptlModel.x;
  1631.   ptlOldMouse.y = ptlModel.y;
  1632.   ptlDevice = ptlModel;
  1633.   GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 1L, &ptlDevice);
  1634.  
  1635.   GpiBitBlt( hpsBitmapBuff
  1636.        , hpsBitmapSave
  1637.        , 3L
  1638.        , aptlUpdt
  1639.        , ROP_SRCCOPY
  1640.        , BBO_IGNORE );
  1641.   aptlUpdtRef[0] = aptlUpdt[0];
  1642.   aptlUpdtRef[1] = aptlUpdt[1];
  1643.  
  1644.   aptlUpdt[0].x += ptlDevice.x - ptlUpdtRef.x;
  1645.   aptlUpdt[0].y += ptlDevice.y - ptlUpdtRef.y;
  1646.   aptlUpdt[1].x += ptlDevice.x - ptlUpdtRef.x;
  1647.   aptlUpdt[1].y += ptlDevice.y - ptlUpdtRef.y;
  1648.   WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlUpdt, 3);
  1649.   ROUND_DOWN_MOD( aptlUpdt[0].x, lByteAlignX);          /* round down       */
  1650.   ROUND_DOWN_MOD( aptlUpdt[0].y, lByteAlignY);          /* round down       */
  1651.   ROUND_UP_MOD(   aptlUpdt[1].x, lByteAlignX);          /* round up          */
  1652.   ROUND_UP_MOD(   aptlUpdt[1].y, lByteAlignY);          /* round up          */
  1653.   WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlUpdt, 3);
  1654.   aptlUpdt[2] = aptlUpdt[0];
  1655.   ptlUpdtRef = ptlDevice;
  1656.   GpiBitBlt( hpsBitmapSave
  1657.        , hpsBitmapBuff
  1658.        , 3L
  1659.        , aptlUpdt
  1660.        , ROP_SRCCOPY
  1661.        , BBO_IGNORE );
  1662.  
  1663.  
  1664.   GpiQuerySegmentTransformMatrix( hpsClient
  1665.                 , lPickedSeg
  1666.                 , 9L
  1667.                 , &matlf);
  1668.   for( psl = pslPicked, fFirst = TRUE
  1669.      ; (psl != pslPicked) || fFirst
  1670.      ; psl = psl->pslNextIsland, fFirst = FALSE )
  1671.   {
  1672.     matlf.lM31 = ptlOffStart.x + ptlModel.x - ptlMoveStart.x;
  1673.     matlf.lM32 = ptlOffStart.y + ptlModel.y - ptlMoveStart.y;
  1674.     GpiSetSegmentTransformMatrix( hpsClient
  1675.                 , psl->lSegId
  1676.                 , 9L
  1677.                 , &matlf
  1678.                 , TRANSFORM_REPLACE);
  1679.     psl->ptlModelXlate.x = matlf.lM31;
  1680.     psl->ptlModelXlate.y = matlf.lM32;
  1681.     DrawPiece( hpsBitmapBuff, psl, TRUE);
  1682.   }
  1683.  
  1684.   WinUnionRect( habMain
  1685.           , (PRECTL)aptlUpdtNew
  1686.           , (PRECTL)aptlUpdt
  1687.           , (PRECTL)aptlUpdtRef);
  1688.   WinMapWindowPoints( hwndClient, HWND_DESKTOP, aptlUpdtNew, 2);
  1689.   ROUND_DOWN_MOD( aptlUpdtNew[0].x, lByteAlignX);      /* round down       */
  1690.   ROUND_DOWN_MOD( aptlUpdtNew[0].y, lByteAlignY);      /* round down       */
  1691.   ROUND_UP_MOD(   aptlUpdtNew[1].x, lByteAlignX);      /* round up          */
  1692.   ROUND_UP_MOD(   aptlUpdtNew[1].y, lByteAlignY);      /* round up          */
  1693.   WinMapWindowPoints( HWND_DESKTOP, hwndClient, aptlUpdtNew, 2);
  1694.   aptlUpdtNew[2] = aptlUpdtNew[0];
  1695.   GpiBitBlt( hpsClient
  1696.        , hpsBitmapBuff
  1697.        , 3L
  1698.        , aptlUpdtNew
  1699.        , ROP_SRCCOPY
  1700.        , BBO_IGNORE );
  1701. }
  1702.  
  1703.  
  1704. /******************************************************************************/
  1705. /*                                          */
  1706. /* The dragged segment is being unselected.  Return it to its normal state.   */
  1707. /*                                          */
  1708. /******************************************************************************/
  1709. VOID
  1710. LeftUp()
  1711. {
  1712.   PSEGLIST   psl, pslTemp;
  1713.   POINTL     ptlShift;
  1714.   BOOL         fFirst;
  1715.   MATRIXLF   matlf;
  1716.   LONG         l;
  1717.  
  1718.   if( !lPickedSeg || !pslPicked)
  1719.     return;
  1720.  
  1721.   for( psl = pslPicked, fFirst = TRUE
  1722.      ; (psl != pslPicked) || fFirst
  1723.      ; psl = psl->pslNextIsland, fFirst = FALSE )
  1724.   {
  1725.  
  1726.     SetRect( psl);
  1727.     SegListUpdate( MAKE_TAIL_SEG, psl);       /* at tail => highest priority  */
  1728.     psl->fIslandMark = TRUE;              /* mark as island member          */
  1729.   }
  1730.   GpiQuerySegmentTransformMatrix( hpsClient
  1731.                 , lPickedSeg
  1732.                 , 9L
  1733.                 , &matlf);
  1734.   ptlShift.x = matlf.lM31;
  1735.   ptlShift.y = matlf.lM32;
  1736.  
  1737.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  1738.     if( !psl->fIslandMark)
  1739.       for( l = 0; l < 8; l++)
  1740.     if( pslPicked->lAdjacent[l] == psl->lSegId)
  1741.     {
  1742.       GpiQuerySegmentTransformMatrix( hpsClient, psl->lSegId, 9L, &matlf);
  1743.       if( (ptlShift.x == matlf.lM31) && (ptlShift.y == matlf.lM32))
  1744.       {
  1745.         DosBeep( 600, 100);
  1746.         DosBeep( 1200, 50);
  1747.         MarkIsland( psl, TRUE);          /* mark the whole new island    */
  1748.         pslTemp = psl->pslNextIsland;     /* swap island ptrs          */
  1749.         psl->pslNextIsland = pslPicked->pslNextIsland;
  1750.         pslPicked->pslNextIsland = pslTemp;
  1751.       }
  1752.     }
  1753.   MarkIsland( pslPicked, FALSE);          /* unmark the island          */
  1754.  
  1755.   pslPicked = NULL;
  1756.   lPickedSeg = NULL;
  1757.  
  1758.   WinSetCapture( HWND_DESKTOP, (HWND)NULL);
  1759. }
  1760.  
  1761.  
  1762. /******************************************************************************/
  1763. /*                                                                            */
  1764. /* mark a whole island                                  */
  1765. /*                                                                            */
  1766. /******************************************************************************/
  1767. VOID
  1768. MarkIsland( pslMark, fMark)
  1769.  
  1770. PSEGLIST  pslMark;
  1771. BOOL      fMark;
  1772. {
  1773.   PSEGLIST  psl;
  1774.   BOOL        fFirst;
  1775.  
  1776.   for( psl = pslMark, fFirst = TRUE
  1777.      ; (psl != pslMark) || fFirst
  1778.      ; psl = psl->pslNextIsland, fFirst = FALSE )
  1779.       psl->fIslandMark = fMark;           /* mark as island member          */
  1780. }
  1781. /******************************************************************************/
  1782. /*                                                                            */
  1783. /* DoHorzScroll will horizontally scroll the current contents of          */
  1784. /* the client area and redraw the invalidated area                  */
  1785. /*                                                                            */
  1786. /******************************************************************************/
  1787. VOID
  1788. DoHorzScroll()
  1789. {
  1790.   POINTL    aptlClient[3];
  1791.   HRGN        hrgn;
  1792.   MATRIXLF  matlf;
  1793.  
  1794.   if( ptsScrollPos.x > ptsScrollMax.x)       /* clip to range of scroll param   */
  1795.       ptsScrollPos.x = ptsScrollMax.x;
  1796.   if( ptsScrollPos.x < 0)
  1797.       ptsScrollPos.x = 0;
  1798.  
  1799.   if( ptsOldScrollPos.x != ptsScrollPos.x) /* only process change in position */
  1800.       WinSendMsg( hwndHorzScroll
  1801.         , SBM_SETPOS
  1802.         , MPFROM2SHORT( ptsScrollPos.x, 0)
  1803.         , MPFROMLONG( NULL));
  1804.  
  1805.   /****************************************************************************/
  1806.   /* Scroll the window the reqd amount, using bitblt'ing (ScrollWindow)       */
  1807.   /* if any of the screen still in view, and paint into uncovered region;     */
  1808.   /* else repaint the whole client area.                      */
  1809.   /****************************************************************************/
  1810.   hrgn = GpiCreateRegion( hpsClient, 0L, NULL);
  1811.   WinQueryWindowRect( hwndClient, (PRECTL)aptlClient);
  1812.   if( abs( ptsScrollPos.x - ptsOldScrollPos.x) <= ptsScrollMax.x)
  1813.   {
  1814.       WinScrollWindow( hwndClient
  1815.              , ptsOldScrollPos.x - ptsScrollPos.x
  1816.              , 0
  1817.              , NULL
  1818.              , NULL
  1819.              , hrgn
  1820.              , NULL
  1821.              , 0);
  1822.   } else
  1823.   {
  1824.       GpiSetRegion( hpsClient, hrgn, 1L, (PRECTL)aptlClient);
  1825.   }
  1826.   /****************************************************************************/
  1827.   /* adjust the default view matrix                          */
  1828.   /****************************************************************************/
  1829.   GpiQueryDefaultViewMatrix( hpsClient, 9L, &matlf );
  1830.   matlf.lM31 -= ptsScrollPos.x - ptsOldScrollPos.x;
  1831.   GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, TRANSFORM_REPLACE);
  1832.  
  1833.   DoDraw( hpsClient, hrgn, TRUE);     /* paint into the client area          */
  1834.   ptsOldScrollPos.x = ptsScrollPos.x;
  1835.   GpiDestroyRegion( hpsClient, hrgn);
  1836.  
  1837.   aptlClient[2] = aptlClient[0];
  1838.   GpiBitBlt( hpsBitmapBuff          /* update the off-screen client image   */
  1839.        , hpsClient
  1840.        , 3L
  1841.        , aptlClient
  1842.        , ROP_SRCCOPY
  1843.        , BBO_IGNORE );
  1844. }
  1845.  
  1846. /******************************************************************************/
  1847. /*                                                                            */
  1848. /* DoVertScroll will vertically scroll the current contents of              */
  1849. /* the client area and redraw the invalidated area                  */
  1850. /*                                                                            */
  1851. /******************************************************************************/
  1852. VOID
  1853. DoVertScroll()
  1854. {
  1855.   POINTL    aptlClient[3];
  1856.   HRGN        hrgn;
  1857.   MATRIXLF  matlf;
  1858.  
  1859.   if( ptsScrollPos.y > ptsScrollMax.y)
  1860.       ptsScrollPos.y = ptsScrollMax.y;
  1861.   if( ptsScrollPos.y < 0)
  1862.       ptsScrollPos.y = 0;
  1863.  
  1864.   if( ptsOldScrollPos.y != ptsScrollPos.y)
  1865.       WinSendMsg( hwndVertScroll
  1866.         , SBM_SETPOS
  1867.         , MPFROM2SHORT( ptsScrollPos.y, 0)
  1868.         , MPFROMLONG( NULL));
  1869.  
  1870.   /****************************************************************************/
  1871.   /* Scroll the window the reqd amount, using bitblt'ing (ScrollWindow)       */
  1872.   /* if any of the screen still in view, and paint into uncovered region;     */
  1873.   /* else repaint the whole client area.                      */
  1874.   /****************************************************************************/
  1875.   hrgn = GpiCreateRegion( hpsClient, 0L, NULL);
  1876.   WinQueryWindowRect( hwndClient, (PRECTL)aptlClient);
  1877.   if( abs( ptsScrollPos.y - ptsOldScrollPos.y) <= ptsScrollMax.y)
  1878.   {
  1879.       WinScrollWindow( hwndClient
  1880.              , 0
  1881.              , ptsScrollPos.y - ptsOldScrollPos.y
  1882.              , NULL
  1883.              , NULL
  1884.              , hrgn
  1885.              , NULL
  1886.              , 0);
  1887.   } else
  1888.   {
  1889.       GpiSetRegion( hpsClient, hrgn, 1L, (PRECTL)aptlClient);
  1890.   }
  1891.   GpiQueryDefaultViewMatrix( hpsClient, 9L, &matlf );
  1892.   matlf.lM32 += ptsScrollPos.y - ptsOldScrollPos.y;
  1893.   GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, TRANSFORM_REPLACE);
  1894.  
  1895.   DoDraw( hpsClient, hrgn, TRUE);
  1896.   ptsOldScrollPos.y = ptsScrollPos.y;
  1897.   GpiDestroyRegion( hpsClient, hrgn);
  1898.  
  1899.   aptlClient[2] = aptlClient[0];
  1900.   GpiBitBlt( hpsBitmapBuff
  1901.        , hpsClient
  1902.        , 3L
  1903.        , aptlClient
  1904.        , ROP_SRCCOPY
  1905.        , BBO_IGNORE );
  1906. }
  1907.  
  1908. /******************************************************************************/
  1909. /*                                                                            */
  1910. /* Redraw the entire client window.                          */
  1911. /*                                                                            */
  1912. /******************************************************************************/
  1913. VOID
  1914. Redraw()
  1915. {
  1916.   RECTL   rclInvalid;
  1917.   HRGN      hrgnUpdt;
  1918.   POINTL  aptlUpdtNew[3];
  1919.  
  1920.   WinQueryWindowRect( hwndClient, &rclInvalid);
  1921.   hrgnUpdt = GpiCreateRegion( hpsBitmapBuff, 1L, &rclInvalid);
  1922.   DoDraw( hpsBitmapBuff, hrgnUpdt, TRUE);
  1923.   GpiDestroyRegion( hpsBitmapBuff, hrgnUpdt);
  1924.   aptlUpdtNew[0].x = rclInvalid.xLeft;
  1925.   aptlUpdtNew[0].y = rclInvalid.yBottom;
  1926.   aptlUpdtNew[1].x = rclInvalid.xRight;
  1927.   aptlUpdtNew[1].y = rclInvalid.yTop;
  1928.   ROUND_DOWN_MOD( aptlUpdtNew[0].x, lByteAlignX);      /* round down       */
  1929.   ROUND_DOWN_MOD( aptlUpdtNew[0].y, lByteAlignY);      /* round down       */
  1930.   ROUND_UP_MOD(   aptlUpdtNew[1].x, lByteAlignX);      /* round up          */
  1931.   ROUND_UP_MOD(   aptlUpdtNew[1].y, lByteAlignY);      /* round up          */
  1932.   aptlUpdtNew[2] = aptlUpdtNew[0];
  1933.   GpiBitBlt( hpsClient
  1934.        , hpsBitmapBuff
  1935.        , 3L
  1936.        , aptlUpdtNew
  1937.        , ROP_SRCCOPY
  1938.        , BBO_IGNORE );
  1939. }
  1940.  
  1941.  
  1942. /******************************************************************************/
  1943. /*                                                                            */
  1944. /* toggle a flag and update the menu check-box                      */
  1945. /*                                                                            */
  1946. /******************************************************************************/
  1947. VOID
  1948. ToggleMenuItem( usMenuMajor, usMenuMinor, pfFlag)
  1949.  
  1950. USHORT    usMenuMajor, usMenuMinor;
  1951. PBOOL    pfFlag;
  1952. {
  1953.   MENUITEM mi;
  1954.  
  1955.   WinSendMsg( WinWindowFromID( hwndFrame, FID_MENU)
  1956.         , MM_QUERYITEM
  1957.         , MPFROM2SHORT( usMenuMajor, FALSE)
  1958.         , MPFROMP( (PMENUITEM)&mi));
  1959.  
  1960.   if( *pfFlag)
  1961.   {
  1962.     *pfFlag = FALSE;
  1963.     WinSendMsg( mi.hwndSubMenu
  1964.           , MM_SETITEMATTR
  1965.           , MPFROM2SHORT( usMenuMinor, TRUE)
  1966.           , MPFROM2SHORT( MIA_CHECKED, ~MIA_CHECKED) );
  1967.   }
  1968.   else
  1969.   {
  1970.     *pfFlag = TRUE;
  1971.     WinSendMsg( mi.hwndSubMenu
  1972.           , MM_SETITEMATTR
  1973.           , MPFROM2SHORT( usMenuMinor, TRUE)
  1974.           , MPFROM2SHORT( MIA_CHECKED, MIA_CHECKED) );
  1975.   }
  1976. }
  1977.  
  1978. /******************************************************************************/
  1979. /*                                                                            */
  1980. /* adjust zoom factor and recalc the picture transform, then do a redraw of   */
  1981. /* whole screen                                   */
  1982. /*                                                                            */
  1983. /******************************************************************************/
  1984. VOID
  1985. Zoom( sInOrOut)
  1986.  
  1987. SHORT sInOrOut;
  1988. {
  1989.   LONG     lScaleOld;
  1990.  
  1991.   lScaleOld = lScale;
  1992.   lScale += sInOrOut;
  1993.   if( lScale > 0)
  1994.     lScale = 0;
  1995.   else
  1996.     if( lScale < -ZOOM_MAX)
  1997.       lScale = -ZOOM_MAX;
  1998.   if( lScale != lScaleOld)
  1999.   {
  2000.       ZoomMenuItems();
  2001.       CalcBounds();
  2002.       CalcTransform( hwndClient);
  2003.       Redraw();
  2004.   }
  2005. }
  2006.  
  2007. /******************************************************************************/
  2008. /*                                                                            */
  2009. /* enable/disable zoom menu items depending on scaling                        */
  2010. /*                                                                            */
  2011. /******************************************************************************/
  2012. VOID
  2013. ZoomMenuItems()
  2014. {
  2015.   MENUITEM  mi;
  2016.   HWND        hwndMenu, hwndOptions;
  2017.  
  2018.   hwndMenu = WinWindowFromID( hwndFrame, FID_MENU);
  2019.   WinSendMsg( hwndMenu
  2020.         , MM_QUERYITEM
  2021.         , MPFROM2SHORT( SM_OPTIONS, FALSE)
  2022.         , MPFROMP( (PMENUITEM)&mi));
  2023.   hwndOptions = mi.hwndSubMenu;
  2024.  
  2025.   if( lScale >= 0)
  2026.   {
  2027.       WinSendMsg( hwndOptions
  2028.         , MM_SETITEMATTR
  2029.         , MPFROM2SHORT( MENU_ZOOMIN, TRUE)
  2030.         , MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED));
  2031.       WinSendMsg( hwndOptions
  2032.         , MM_SETITEMATTR
  2033.         , MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
  2034.         , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  2035.   } else
  2036.   {
  2037.       if( lScale <= - ZOOM_MAX)
  2038.       {
  2039.       WinSendMsg( hwndOptions
  2040.             , MM_SETITEMATTR
  2041.             , MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
  2042.             , MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED));
  2043.       WinSendMsg( hwndOptions
  2044.             , MM_SETITEMATTR
  2045.             , MPFROM2SHORT( MENU_ZOOMIN, TRUE)
  2046.             , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  2047.       } else
  2048.       {
  2049.       WinSendMsg( hwndOptions
  2050.             , MM_SETITEMATTR
  2051.             , MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
  2052.             , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  2053.       WinSendMsg( hwndOptions
  2054.             , MM_SETITEMATTR
  2055.             , MPFROM2SHORT( MENU_ZOOMIN, TRUE)
  2056.             , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  2057.       }
  2058.   }
  2059. }
  2060.  
  2061. /******************************************************************************/
  2062. /*                                                                            */
  2063. /* Determine the bounding rect of a segment.                      */
  2064. /*                                                                            */
  2065. /* Use special knowledge that the only non-identity part of the segment       */
  2066. /* transform is the translation part.                          */
  2067. /******************************************************************************/
  2068. VOID
  2069. SetRect( psl)
  2070.  
  2071. PSEGLIST  psl;
  2072. {
  2073.   MATRIXLF  matlf;
  2074.  
  2075.   GpiQuerySegmentTransformMatrix( hpsClient    /* get model xform of segment */
  2076.                 , psl->lSegId
  2077.                 , 9L
  2078.                 , &matlf );
  2079.   psl->rclCurrent = psl->rclBitBlt;        /* world space bounding rect  */
  2080.   psl->rclCurrent.xLeft   += matlf.lM31;    /* apply translation          */
  2081.   psl->rclCurrent.yBottom += matlf.lM32;
  2082.   psl->rclCurrent.xRight  += matlf.lM31;
  2083.   psl->rclCurrent.yTop      += matlf.lM32;
  2084. }
  2085.  
  2086. /******************************************************************************/
  2087. /*                                                                            */
  2088. /* set the default viewing transform                          */
  2089. /*                                                                            */
  2090. /******************************************************************************/
  2091. VOID
  2092. SetDVTransform( fx11, fx12, fx21, fx22, l31, l32, lType)
  2093.  
  2094. FIXED    fx11, fx12, fx21, fx22;
  2095. LONG    l31, l32, lType;
  2096. {
  2097.   MATRIXLF  matlf;
  2098.  
  2099.   matlf.fxM11 = fx11;
  2100.   matlf.fxM12 = fx12;
  2101.   matlf.lM13  = 0L;
  2102.   matlf.fxM21 = fx21;
  2103.   matlf.fxM22 = fx22;
  2104.   matlf.lM23  = 0L;
  2105.   matlf.lM31  = l31;
  2106.   matlf.lM32  = l32;
  2107.   matlf.lM33  = 1L;
  2108.   GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, lType);
  2109. }
  2110.  
  2111. /******************************************************************************/
  2112. /*                                                                            */
  2113. /* get bounding rect of whole picture in model coordinates              */
  2114. /*                                                                            */
  2115. /******************************************************************************/
  2116. VOID
  2117. CalcBounds()
  2118. {
  2119.   PSEGLIST  psl;
  2120.   RECTL     rcl;
  2121.  
  2122.   if( !pslHead)
  2123.     return;
  2124.   rclBounds = pslHead->rclCurrent;
  2125.   for( psl = pslHead->pslNext; psl != NULL; psl = psl->pslNext)
  2126.   {
  2127.     rcl = psl->rclCurrent;
  2128.     if( rcl.xLeft < rclBounds.xLeft)
  2129.       rclBounds.xLeft = rcl.xLeft;
  2130.     if( rcl.xRight > rclBounds.xRight)
  2131.       rclBounds.xRight = rcl.xRight;
  2132.     if( rcl.yTop > rclBounds.yTop)
  2133.       rclBounds.yTop = rcl.yTop;
  2134.     if( rcl.yBottom < rclBounds.yBottom)
  2135.       rclBounds.yBottom = rcl.yBottom;
  2136.   }
  2137. }
  2138.  
  2139. /******************************************************************************/
  2140. /*                                                                            */
  2141. /* Calculate and set the default viewing transform based on zoom and scroll   */
  2142. /*                                                                            */
  2143. /******************************************************************************/
  2144. VOID
  2145. CalcTransform( hwnd)
  2146.  
  2147. HWND hwnd;
  2148. {
  2149.   RECTL     rclClient;
  2150.   POINTL    ptlCenter, ptlTrans, ptlScale, aptl[4], ptlOrg, aptlSides[12];
  2151.   PSEGLIST  psl;
  2152.   LONG        l;
  2153.  
  2154.   /****************************************************************************/
  2155.   /* from bounding rect of picture get center of picture              */
  2156.   /****************************************************************************/
  2157.   ptlCenter.x = (rclBounds.xLeft   + rclBounds.xRight) / 2;
  2158.   ptlCenter.y = (rclBounds.yBottom + rclBounds.yTop  ) / 2;
  2159.  
  2160.   /****************************************************************************/
  2161.   /* translate center of picture to origin                      */
  2162.   /****************************************************************************/
  2163.   SetDVTransform( (FIXED)UNITY
  2164.         , (FIXED)0
  2165.         , (FIXED)0
  2166.         , (FIXED)UNITY
  2167.         , -ptlCenter.x
  2168.         , -ptlCenter.y
  2169.         , TRANSFORM_REPLACE);
  2170.  
  2171.   /****************************************************************************/
  2172.   /* scale down to 1:1 of bitmap in file                      */
  2173.   /****************************************************************************/
  2174.   ptlScale.x = UNITY * bmpBitmapFile.cx /(ptlTopRight.x - ptlBotLeft.x);
  2175.   ptlScale.y = UNITY * bmpBitmapFile.cy /(ptlTopRight.y - ptlBotLeft.y);
  2176.  
  2177.   /****************************************************************************/
  2178.   /* add in zoom scale                                  */
  2179.   /****************************************************************************/
  2180.   ptlScale.x += ptlScale.x * lScale / (ZOOM_MAX + 1);
  2181.   ptlScale.y += ptlScale.y * lScale / (ZOOM_MAX + 1);
  2182.  
  2183.   SetDVTransform( (FIXED)ptlScale.x
  2184.         , (FIXED)0
  2185.         , (FIXED)0
  2186.         , (FIXED)ptlScale.y
  2187.         , 0L
  2188.         , 0L
  2189.         , TRANSFORM_ADD);
  2190.  
  2191.   /****************************************************************************/
  2192.   /* translate center of picture to center of client window              */
  2193.   /****************************************************************************/
  2194.   WinQueryWindowRect( hwnd, &rclClient);
  2195.   ptlTrans.x = (rclClient.xRight - rclClient.xLeft)   / 2;
  2196.   ptlTrans.y = (rclClient.yTop     - rclClient.yBottom) / 2;
  2197.  
  2198.   /****************************************************************************/
  2199.   /* add in horizontal and vertical scrolling factors                  */
  2200.   /****************************************************************************/
  2201.   ptlTrans.x += ptsScrollPos.x - ptsHalfScrollMax.x;
  2202.   ptlTrans.y += ptsScrollPos.y - ptsHalfScrollMax.y;
  2203.   SetDVTransform( (FIXED)UNITY
  2204.         , (FIXED)0
  2205.         , (FIXED)0
  2206.         , (FIXED)UNITY
  2207.         , ptlTrans.x
  2208.         , ptlTrans.y
  2209.         , TRANSFORM_ADD);
  2210.  
  2211.   /****************************************************************************/
  2212.   /* create bitmaps for pieces                              */
  2213.   /****************************************************************************/
  2214.  
  2215.   ptlOffset = ptlBotLeft;           /* BottomLeft corner in dev coord  */
  2216.   GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 1L, &ptlOffset);
  2217.   if( (ptlScale.x != ptlScaleRef.x) || (ptlScale.y != ptlScaleRef.y))
  2218.   {
  2219.     ptlScaleRef = ptlScale;
  2220.  
  2221.     /**************************************************************************/
  2222.     /* create a shadow bitmap of the original, sized to current output size   */
  2223.     /**************************************************************************/
  2224.     aptl[0] = ptlBotLeft;           /* current output rect, dev coord  */
  2225.     aptl[1] = ptlTopRight;
  2226.     GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, aptl);
  2227.  
  2228.     aptl[0].x -= ptlOffset.x;           /* put bottom left at (0,0)          */
  2229.     aptl[0].y -= ptlOffset.y;
  2230.     aptl[1].x -= ptlOffset.x - 1;       /* correct for coordinate convert  */
  2231.     aptl[1].y -= ptlOffset.y - 1;
  2232.     aptl[2].x = 0L;
  2233.     aptl[2].y = 0L;
  2234.     aptl[3].x = bmpBitmapFile.cx + 1;       /* bitmap dimensions           */
  2235.     aptl[3].y = bmpBitmapFile.cy + 1;
  2236.     GpiSetBitmap( hpsBitmapSize, hbmBitmapSize);
  2237.     GpiBitBlt( hpsBitmapSize           /* copy the bitmap              */
  2238.          , hpsBitmapFile
  2239.          , 4L
  2240.          , aptl
  2241.          , ROP_SRCCOPY
  2242.          , BBO_IGNORE);
  2243.  
  2244.     for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2245.     {
  2246.       if( DosSemWait( hsemTerminate, SEM_IMMEDIATE_RETURN)) /* exit if quit   */
  2247.     break;
  2248.       aptl[0].x = psl->rclBitBlt.xLeft;     /* bounding rect in world space   */
  2249.       aptl[0].y = psl->rclBitBlt.yBottom;
  2250.       aptl[1].x = psl->rclBitBlt.xRight;
  2251.       aptl[1].y = psl->rclBitBlt.yTop;
  2252.       aptl[2] = aptl[0];
  2253.       aptl[3] = aptl[1];
  2254.       GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, &aptl[2]);
  2255.       ptlOrg = aptl[2];
  2256.       aptl[3].x -= ptlOrg.x - 1;        /* bitmap rect of piece          */
  2257.       aptl[3].y -= ptlOrg.y - 1;
  2258.       aptl[2].x = 0L;
  2259.       aptl[2].y = 0L;
  2260.       psl->aptlBitBlt[0] = aptl[0];
  2261.       psl->aptlBitBlt[1] = aptl[1];
  2262.       psl->aptlBitBlt[2] = aptl[2];
  2263.       psl->aptlBitBlt[3] = aptl[3];
  2264.  
  2265.       /************************************************************************/
  2266.       /* compute the piece control points                      */
  2267.       /************************************************************************/
  2268.       for( l=0; l<12; l++)
  2269.     aptlSides[l] = psl->aptlSides[l];
  2270.       GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 12L, aptlSides);
  2271.       for( l=0; l<12; l++)
  2272.       {
  2273.     aptlSides[l].x -= ptlOrg.x;
  2274.     aptlSides[l].y -= ptlOrg.y;
  2275.       }
  2276.  
  2277.       /************************************************************************/
  2278.       /* prepare the mask to punch a hole in the output bitmap              */
  2279.       /************************************************************************/
  2280.       GpiSetBitmap( psl->hpsHole, psl->hbmHole);
  2281.       GpiSetClipPath( psl->hpsHole, 0L, SCP_RESET);  /* no clip path          */
  2282.       GpiBitBlt( psl->hpsHole                 /* fill with 1's         */
  2283.            , NULL
  2284.            , 2L
  2285.            , &psl->aptlBitBlt[2]
  2286.            , ROP_ONE
  2287.            , BBO_IGNORE);
  2288.  
  2289.       GpiBeginPath( psl->hpsHole, 1L);             /* define a clip path    */
  2290.       GpiMove( psl->hpsHole, &aptlSides[11]);
  2291.       GpiPolySpline( psl->hpsHole, 12L, aptlSides);
  2292.       GpiEndPath( psl->hpsHole);
  2293.       GpiSetClipPath( psl->hpsHole, 1L, SCP_AND);
  2294.       GpiBitBlt( psl->hpsHole                 /* fill with 0's         */
  2295.            , NULL
  2296.            , 2L
  2297.            , &psl->aptlBitBlt[2]
  2298.            , ROP_ZERO
  2299.            , BBO_IGNORE);
  2300.       GpiSetClipPath( psl->hpsHole, 0L, SCP_RESET);  /* clear the clip path   */
  2301.  
  2302.       /************************************************************************/
  2303.       /* prepare the mask to fill the hole in the output bitmap           */
  2304.       /************************************************************************/
  2305.       GpiSetBitmap( psl->hpsFill, psl->hbmFill);
  2306.       GpiSetClipPath( psl->hpsFill, 0L, SCP_RESET);  /* no clip path          */
  2307.       GpiBitBlt( psl->hpsFill                 /* fill with 1's         */
  2308.            , NULL
  2309.            , 2L
  2310.            , &psl->aptlBitBlt[2]
  2311.            , ROP_ZERO
  2312.            , BBO_IGNORE);
  2313.  
  2314.       GpiBeginPath( psl->hpsFill, 1L);             /* define a clip path    */
  2315.       GpiMove( psl->hpsFill, &aptlSides[11]);
  2316.       GpiPolySpline( psl->hpsFill, 12L, aptlSides);
  2317.       GpiEndPath( psl->hpsFill);
  2318.       GpiSetClipPath( psl->hpsFill, 1L, SCP_AND);
  2319.       aptl[0] = psl->aptlBitBlt[2];
  2320.       aptl[1] = psl->aptlBitBlt[3];
  2321.       aptl[2].x = ptlOrg.x - ptlOffset.x;         /* pick the right part   */
  2322.       aptl[2].y = ptlOrg.y - ptlOffset.y;         /* of the sized bitmap   */
  2323.       GpiBitBlt( psl->hpsFill                 /* fill with data          */
  2324.            , hpsBitmapSize
  2325.            , 3L
  2326.            , aptl
  2327.            , ROP_SRCCOPY
  2328.            , BBO_IGNORE);
  2329.       GpiSetClipPath( psl->hpsFill, 0L, SCP_RESET);  /* clear the clip path   */
  2330.  
  2331.       GpiSetColor( psl->hpsFill, CLR_RED);         /* draw the outline      */
  2332.       GpiMove( psl->hpsFill, &aptlSides[11]);
  2333.       GpiPolySpline( psl->hpsFill, 12L, aptlSides);
  2334.     }
  2335.   }
  2336. }
  2337.  
  2338.  
  2339. /******************************************************************************/
  2340. /*                                                                            */
  2341. /* Draw one piece.                                  */
  2342. /*                                                                            */
  2343. /******************************************************************************/
  2344. VOID
  2345. DrawPiece( hps, psl, fFill)
  2346.  
  2347. HPS      hps;
  2348. PSEGLIST  psl;
  2349. BOOL      fFill;
  2350. {
  2351.     POINTL  aptl[4];
  2352.  
  2353.     if( GpiQuerySegmentAttrs( hpsClient, psl->lSegId, ATTR_VISIBLE) == ATTR_ON)
  2354.     {
  2355.       aptl[2].x = aptl[2].y = 0L;
  2356.       aptl[3] = psl->aptlBitBlt[3];
  2357.       aptl[0].x = psl->rclBitBlt.xLeft + psl->ptlModelXlate.x;
  2358.       aptl[0].y = psl->rclBitBlt.yBottom + psl->ptlModelXlate.y;
  2359.       GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 1L, aptl);
  2360.       aptl[1].x = aptl[0].x + aptl[3].x;
  2361.       aptl[1].y = aptl[0].y + aptl[3].y;
  2362.       GpiBitBlt( hps                /* punch a hole              */
  2363.            , psl->hpsHole
  2364.            , 4L
  2365.            , aptl
  2366.            , ROP_SRCAND
  2367.            , BBO_IGNORE );
  2368.       if( fFill)
  2369.     GpiBitBlt( hps                /* fill the hole              */
  2370.          , psl->hpsFill
  2371.          , 4L
  2372.          , aptl
  2373.          , ROP_SRCPAINT
  2374.          , BBO_IGNORE );
  2375.     }
  2376. }
  2377.  
  2378. /******************************************************************************/
  2379. /*                                                                            */
  2380. /* Draw the picture, using the passed region for clipping.              */
  2381. /* Intersect the bounding box used for the clip region with the client rect.  */
  2382. /* Test each segment to see if its bounding box intersects the bounding box   */
  2383. /* of the clipping region.  Draw only if there is an intersection.          */
  2384. /*                                                                            */
  2385. /******************************************************************************/
  2386. BOOL
  2387. DoDraw( hps, hrgn, fPaint)
  2388.  
  2389. HPS    hps;
  2390. HRGN    hrgn;
  2391. BOOL    fPaint;
  2392. {
  2393.   HRGN        hrgnOld;
  2394.   RECTL     rcl, rclRegion, rclDst, rclClient;
  2395.   PSEGLIST  psl;
  2396.  
  2397.   if( fPaint)
  2398.   {
  2399.     GpiSetColor( hps, CLR_BACKGROUND);
  2400.     GpiPaintRegion( hps, hrgn);         /* erase region           */
  2401.   }
  2402.   GpiQueryRegionBox( hps, hrgn, &rclRegion);
  2403.   WinQueryWindowRect( hwndClient, &rclClient);
  2404.   if( !WinIntersectRect( habAsync, &rclRegion, &rclRegion, &rclClient))
  2405.     return( FALSE);                /* not in client window       */
  2406.   GpiSetClipRegion( hps, hrgn, &hrgnOld);    /* make the clip region       */
  2407.   for( psl = pslHead; psl != NULL; psl = psl->pslNext) /* scan all pieces     */
  2408.   {
  2409.     /**************************************************************************/
  2410.     /* get the piece bounding box in device coordinates               */
  2411.     /**************************************************************************/
  2412.     rcl = psl->rclCurrent;
  2413.     GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
  2414.     rcl.xRight++;
  2415.     rcl.yTop++;
  2416.     /**************************************************************************/
  2417.     /* if the piece might be visible, and drawing allowed, draw the piece     */
  2418.     /**************************************************************************/
  2419.     if( WinIntersectRect( habAsync, &rclDst, &rcl, &rclRegion))
  2420.     if( DosSemWait( hsemDrawOn, SEM_IMMEDIATE_RETURN))
  2421.       DrawPiece( hps, psl, TRUE);
  2422.     else
  2423.       break;
  2424.   }
  2425.   GpiSetClipRegion( hps, NULL, &hrgnOld);
  2426.   if( DosSemWait( hsemDrawOn, SEM_IMMEDIATE_RETURN))
  2427.     GpiSetRegion( hpsClient, hrgnInvalid, 0L, NULL);
  2428.  
  2429.   return( TRUE);
  2430. }
  2431.  
  2432. /******************************************************************************/
  2433. /*                                                                            */
  2434. /* Return a pointer to a segment list member, based on segment id.          */
  2435. /*                                                                            */
  2436. /******************************************************************************/
  2437. PSEGLIST
  2438. SegListGet( lSeg)
  2439.  
  2440. LONG  lSeg;
  2441. {
  2442.   PSEGLIST  psl;
  2443.  
  2444.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2445.     if( psl->lSegId == lSeg)
  2446.       return( psl);
  2447.   return( NULL);
  2448. }
  2449.  
  2450. /******************************************************************************/
  2451. /*                                                                            */
  2452. /* Check the segment list for obvious errors.                      */
  2453. /*                                          */
  2454. /******************************************************************************/
  2455. BOOL
  2456. SegListCheck( iLoc)
  2457.  
  2458. INT   iLoc;
  2459. {
  2460.   PSEGLIST   psl;
  2461.   CHAR         pszMsg[50];
  2462.   PSZ         psz1, psz2;
  2463.  
  2464.   pszMsg[0] = '\0';
  2465.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2466.     if( (psl->lSegId < 1) || (psl->lSegId > lLastSegId) )
  2467.     {
  2468.       DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
  2469.       sprintf( szFmt, "Bad head segment list, location %d", iLoc);
  2470.       for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
  2471.       ;
  2472.       DosSemClear( hsemSzFmt);
  2473.       MyMessageBox( hwndClient, pszMsg);
  2474.       return( FALSE);
  2475.     }
  2476.   for( psl = pslTail; psl != NULL; psl = psl->pslPrev)
  2477.     if( (psl->lSegId < 1) || (psl->lSegId > lLastSegId) )
  2478.     {
  2479.       DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
  2480.       sprintf( szFmt, "Bad head segment list, location %d", iLoc);
  2481.       for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
  2482.       ;
  2483.       DosSemClear( hsemSzFmt);
  2484.       MyMessageBox( hwndClient, pszMsg);
  2485.       return( FALSE);
  2486.     }
  2487.   return( TRUE);
  2488. }
  2489. /******************************************************************************/
  2490. /*                                                                            */
  2491. /* Add (at head or tail) or delete a specified segment list member.          */
  2492. /*                                                                            */
  2493. /******************************************************************************/
  2494. PSEGLIST
  2495. SegListUpdate( usOperation, pslUpdate)
  2496.  
  2497. USHORT     usOperation;
  2498. PSEGLIST pslUpdate;
  2499. {
  2500.   PSEGLIST psl;
  2501.   SEL       sel;
  2502.  
  2503.   switch( usOperation)
  2504.   {
  2505.     case ADD_HEAD_SEG:
  2506.       DosAllocSeg( sizeof( SEGLIST), &sel, 0);
  2507.       if( pslHead == NULL)
  2508.       {
  2509.     pslHead = MAKEP( sel, 0);
  2510.     if( pslHead == NULL)
  2511.       return( NULL);
  2512.     *pslHead = *pslUpdate;
  2513.     pslHead->pslPrev = NULL;
  2514.     pslHead->pslNext = NULL;
  2515.     pslTail = pslHead;
  2516.       } else
  2517.       {
  2518.     psl = MAKEP( sel, 0);
  2519.     if( psl == NULL)
  2520.       return( NULL);
  2521.     *psl = *pslUpdate;
  2522.     pslHead->pslPrev = psl;
  2523.     psl->pslNext = pslHead;
  2524.     psl->pslPrev = NULL;
  2525.     pslHead = psl;
  2526.       }
  2527.       return( pslHead);
  2528.       break;
  2529.  
  2530.     case ADD_TAIL_SEG:
  2531.       DosAllocSeg( sizeof( SEGLIST), &sel, 0);
  2532.       if( pslTail == NULL)
  2533.       {
  2534.     pslHead = MAKEP( sel, 0);
  2535.     if( pslHead == NULL)
  2536.       return( NULL);
  2537.     *pslHead = *pslUpdate;
  2538.     pslHead->pslPrev = NULL;
  2539.     pslHead->pslNext = NULL;
  2540.     pslTail = pslHead;
  2541.       } else
  2542.       {
  2543.     psl = MAKEP( sel, 0);
  2544.     if( psl == NULL)
  2545.       return( NULL);
  2546.     *psl = *pslUpdate;
  2547.     pslTail->pslNext = psl;
  2548.     psl->pslPrev = pslTail;
  2549.     psl->pslNext = NULL;
  2550.     pslTail = psl;
  2551.       }
  2552.       return( pslTail);
  2553.       break;
  2554.  
  2555.     case MAKE_TAIL_SEG:
  2556.       if( pslUpdate == pslTail)
  2557.     return( pslTail);
  2558.       if( pslUpdate == pslHead)
  2559.       {
  2560.     pslHead = pslHead->pslNext;
  2561.     pslHead->pslPrev = NULL;
  2562.       } else
  2563.       {
  2564.     pslUpdate->pslPrev->pslNext = pslUpdate->pslNext;
  2565.     pslUpdate->pslNext->pslPrev = pslUpdate->pslPrev;
  2566.       }
  2567.       pslTail->pslNext = pslUpdate;
  2568.       pslUpdate->pslPrev = pslTail;
  2569.       pslTail = pslUpdate;
  2570.       pslTail->pslNext = NULL;
  2571.       return( pslTail);
  2572.       break;
  2573.  
  2574.     case DEL_SEG:
  2575.       for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2576.       {
  2577.     if( psl->lSegId == pslUpdate->lSegId)
  2578.     {
  2579.       if( psl == pslHead)
  2580.       {
  2581.         pslHead = psl->pslNext;
  2582.         if( pslHead == NULL)
  2583.           pslTail = NULL;
  2584.         else
  2585.           pslHead->pslPrev = NULL;
  2586.       }else if( psl == pslTail)
  2587.       {
  2588.         pslTail = psl->pslPrev;
  2589.         pslTail->pslNext = NULL;
  2590.       } else
  2591.       {
  2592.         (psl->pslPrev)->pslNext = psl->pslNext;
  2593.         (psl->pslNext)->pslPrev = psl->pslPrev;
  2594.       }
  2595.       DosFreeSeg( SELECTOROF(psl));
  2596.       return( psl);
  2597.       break;
  2598.     }
  2599.       }
  2600.       return( NULL);
  2601.       break;
  2602.  
  2603.     default:
  2604.       return( NULL);
  2605.   }
  2606. }
  2607.  
  2608.  
  2609.  
  2610. /******************************************************************************/
  2611. /*                                                                            */
  2612. /* DumpPicture will free the list and segment store for the picture          */
  2613. /*                                                                            */
  2614. /******************************************************************************/
  2615. BOOL
  2616. DumpPicture()
  2617. {
  2618.   while( pslHead != NULL )
  2619.   {
  2620.     GpiSetBitmap( pslHead->hpsFill, NULL);
  2621.     GpiDeleteBitmap( pslHead->hbmFill);
  2622.     GpiDestroyPS( pslHead->hpsFill);
  2623.     DevCloseDC( pslHead->hdcFill);
  2624.  
  2625.     GpiSetBitmap( pslHead->hpsHole, NULL);
  2626.     GpiDeleteBitmap( pslHead->hbmHole);
  2627.     GpiDestroyPS( pslHead->hpsHole);
  2628.     DevCloseDC( pslHead->hdcHole);
  2629.  
  2630.     SegListUpdate( DEL_SEG, pslHead);
  2631.   }
  2632.   GpiDeleteSegments( hpsClient, 1L, lLastSegId);
  2633.  
  2634.   if( hbmBitmapFile)
  2635.   {
  2636.       GpiSetBitmap( hpsBitmapFile, NULL);
  2637.       GpiDeleteBitmap( hbmBitmapFile);
  2638.   }
  2639.   if( hbmBitmapSize)
  2640.   {
  2641.       GpiSetBitmap( hpsBitmapSize, NULL);
  2642.       GpiDeleteBitmap( hbmBitmapSize);
  2643.   }
  2644.   if( hbmBitmapBuff)
  2645.   {
  2646.       GpiSetBitmap( hpsBitmapBuff, NULL);
  2647.       GpiDeleteBitmap( hbmBitmapBuff);
  2648.   }
  2649.   if( hbmBitmapSave)
  2650.   {
  2651.       GpiSetBitmap( hpsBitmapSave, NULL);
  2652.       GpiDeleteBitmap( hbmBitmapSave);
  2653.   }
  2654.  
  2655.   return( TRUE);
  2656. }
  2657.  
  2658. /******************************************************************************/
  2659. /*                                                                            */
  2660. /* Draw the picture into segment store.                       */
  2661. /*                                                                            */
  2662. /******************************************************************************/
  2663. BOOL
  2664. CreatePicture()
  2665. {
  2666.  
  2667.   POINTL    ptl, aptlSides[12], aptlControl[12];
  2668.   SEGLIST   sl;
  2669.   PSEGLIST  psl;
  2670.   LONG        l, lMinor, lNeighbor, alFuzz[36][4];
  2671.   SIZEL     sizl;
  2672.   BITMAPINFOHEADER bmp;
  2673.   DATETIME  date;
  2674.  
  2675.   /****************************************************************************/
  2676.   /* compute some fuzz for the control points                      */
  2677.   /****************************************************************************/
  2678.   DosGetDateTime( &date);
  2679.   srand( (USHORT)date.hundredths);
  2680.   for( l=0; l<36; l++)
  2681.     for( lMinor=0; lMinor<4; lMinor++)
  2682.       alFuzz[l][lMinor] = 50 * (rand() % 10);
  2683.  
  2684.   /****************************************************************************/
  2685.   /* reset the default viewing transform to identity                  */
  2686.   /****************************************************************************/
  2687.   SetDVTransform( (FIXED)UNITY
  2688.         , (FIXED)0
  2689.         , (FIXED)0
  2690.         , (FIXED)UNITY
  2691.         , 0L
  2692.         , 0L
  2693.         , TRANSFORM_REPLACE);
  2694.  
  2695.   /****************************************************************************/
  2696.   /* set to store mode                                  */
  2697.   /****************************************************************************/
  2698.   GpiSetDrawingMode( hpsClient, DM_RETAIN);
  2699.  
  2700.   /****************************************************************************/
  2701.   /* chaining and detectability off, fastchaining off                  */
  2702.   /****************************************************************************/
  2703.   GpiSetInitialSegmentAttrs( hpsClient, ATTR_CHAINED, ATTR_OFF);
  2704.   GpiSetInitialSegmentAttrs( hpsClient, ATTR_FASTCHAIN, ATTR_OFF);
  2705.   GpiSetInitialSegmentAttrs( hpsClient, ATTR_DETECTABLE, ATTR_OFF);
  2706.  
  2707.   /****************************************************************************/
  2708.   /* draw the pieces                                  */
  2709.   /****************************************************************************/
  2710.   lLastSegId = 0;
  2711.   for( ptl.x = ptlBotLeft.x; ptl.x < ptlTopRight.x; ptl.x += 500)
  2712.   {
  2713.     if( DosSemWait( hsemTerminate, SEM_IMMEDIATE_RETURN))
  2714.       break;
  2715.     for( ptl.y = ptlBotLeft.y; ptl.y < ptlTopRight.y; ptl.y += 500)
  2716.     {
  2717.       if( DosSemWait( hsemTerminate, SEM_IMMEDIATE_RETURN))
  2718.     break;
  2719.       lLastSegId++;
  2720.  
  2721.       /************************************************************************/
  2722.       /* compute the piece outline control points                  */
  2723.       /************************************************************************/
  2724.       aptlControl[0].x = 250L;
  2725.       aptlControl[0].y = 500L;
  2726.       aptlControl[1].x = 250;
  2727.       aptlControl[1].y = -500L;
  2728.       aptlControl[2].x = 500L;
  2729.       aptlControl[2].y = 0L;
  2730.  
  2731.       aptlControl[3].x = 0L;
  2732.       aptlControl[3].y = 250L;
  2733.       aptlControl[4].x = 1000L;
  2734.       aptlControl[4].y = 250L;
  2735.       aptlControl[5].x = 500L;
  2736.       aptlControl[5].y = 500L;
  2737.  
  2738.       aptlControl[6].x = 250L;
  2739.       aptlControl[6].y = 0L;
  2740.       aptlControl[7].x = 250L;
  2741.       aptlControl[7].y = 1000L;
  2742.       aptlControl[8].x = 0L;
  2743.       aptlControl[8].y = 500L;
  2744.  
  2745.       aptlControl[9].x    = 500L;
  2746.       aptlControl[9].y    = 250L;
  2747.       aptlControl[10].x = -500L;
  2748.       aptlControl[10].y = 250L;
  2749.       aptlControl[11].x = 0L;
  2750.       aptlControl[11].y = 0L;
  2751.  
  2752.       if( ptl.y == ptlBotLeft.y)
  2753.       {
  2754.     aptlControl[0].y = 0L;
  2755.     aptlControl[1].y = 0L;
  2756.       }
  2757.  
  2758.       if( (ptl.x + 500) == ptlTopRight.x)
  2759.       {
  2760.     aptlControl[3].x = 500L;
  2761.     aptlControl[4].x = 500L;
  2762.       }
  2763.  
  2764.       if( (ptl.y + 500) == ptlTopRight.y)
  2765.       {
  2766.     aptlControl[6].y = 500L;
  2767.     aptlControl[7].y = 500L;
  2768.       }
  2769.  
  2770.       if( ptl.x == ptlBotLeft.x)
  2771.       {
  2772.     aptlControl[ 9].x = 0L;
  2773.     aptlControl[10].x = 0L;
  2774.       }
  2775.  
  2776.       /************************************************************************/
  2777.       /* compute the adjacent segments                          */
  2778.       /************************************************************************/
  2779.       sl.lAdjacent[0] = lLastSegId - 7;
  2780.       sl.lAdjacent[1] = lLastSegId - 6;
  2781.       sl.lAdjacent[2] = lLastSegId - 5;
  2782.       sl.lAdjacent[3] = lLastSegId - 1;
  2783.       sl.lAdjacent[4] = lLastSegId + 1;
  2784.       sl.lAdjacent[5] = lLastSegId + 5;
  2785.       sl.lAdjacent[6] = lLastSegId + 6;
  2786.       sl.lAdjacent[7] = lLastSegId + 7;
  2787.       if( ptl.x == ptlBotLeft.x)
  2788.       {
  2789.     sl.lAdjacent[0] = 0;
  2790.     sl.lAdjacent[1] = 0;
  2791.     sl.lAdjacent[2] = 0;
  2792.       }
  2793.       if( ptl.y == ptlBotLeft.y)
  2794.       {
  2795.     sl.lAdjacent[0] = 0;
  2796.     sl.lAdjacent[3] = 0;
  2797.     sl.lAdjacent[5] = 0;
  2798.       }
  2799.       if( (ptl.x + 500) == ptlTopRight.x)
  2800.       {
  2801.     sl.lAdjacent[5] = 0;
  2802.     sl.lAdjacent[6] = 0;
  2803.     sl.lAdjacent[7] = 0;
  2804.       }
  2805.       if( (ptl.y + 500) == ptlTopRight.y)
  2806.       {
  2807.     sl.lAdjacent[2] = 0;
  2808.     sl.lAdjacent[4] = 0;
  2809.     sl.lAdjacent[7] = 0;
  2810.       }
  2811.  
  2812.       /************************************************************************/
  2813.       /* throw in some fuzz                              */
  2814.       /************************************************************************/
  2815.       if( sl.lAdjacent[3])
  2816.       {
  2817.     aptlControl[0].y  -= alFuzz[lLastSegId - 1][0];
  2818.     aptlControl[1].y  += alFuzz[lLastSegId - 1][1];
  2819.       }
  2820.  
  2821.       if( sl.lAdjacent[1])
  2822.       {
  2823.     aptlControl[9].x  -= alFuzz[lLastSegId - 1][2];
  2824.     aptlControl[10].x += alFuzz[lLastSegId - 1][3];
  2825.       }
  2826.  
  2827.       if( lNeighbor = sl.lAdjacent[4])
  2828.       {
  2829.     aptlControl[7].y  -= alFuzz[lNeighbor - 1][0];
  2830.     aptlControl[6].y  += alFuzz[lNeighbor - 1][1];
  2831.       }
  2832.  
  2833.       if( lNeighbor = sl.lAdjacent[6])
  2834.       {
  2835.     aptlControl[4].x  -= alFuzz[lNeighbor - 1][2];
  2836.     aptlControl[3].x  += alFuzz[lNeighbor - 1][3];
  2837.       }
  2838.  
  2839.       /************************************************************************/
  2840.       /* compute the piece control points in world coordinates              */
  2841.       /************************************************************************/
  2842.       for( l=0; l<12; l++)
  2843.       {
  2844.     aptlSides[l].x = ptl.x + aptlControl[l].x;
  2845.     aptlSides[l].y = ptl.y + aptlControl[l].y;
  2846.     sl.aptlSides[l] = aptlSides[l];
  2847.       }
  2848.  
  2849.       /************************************************************************/
  2850.       /* compute the dimensions of the matching rects for BitBlt          */
  2851.       /************************************************************************/
  2852.       sl.rclBitBlt.xLeft   = ptl.x - 250;
  2853.       sl.rclBitBlt.yBottom = ptl.y - 250;
  2854.       sl.rclBitBlt.xRight  = ptl.x + 750;
  2855.       sl.rclBitBlt.yTop    = ptl.y + 750;
  2856.       if( ptl.x == ptlBotLeft.x)
  2857.     sl.rclBitBlt.xLeft += 250;
  2858.       if( ptl.y == ptlBotLeft.y)
  2859.     sl.rclBitBlt.yBottom += 250;
  2860.       if( (ptl.x + 500) == ptlTopRight.x)
  2861.     sl.rclBitBlt.xRight -= 250;
  2862.       if( (ptl.y + 500) == ptlTopRight.y)
  2863.     sl.rclBitBlt.yTop -= 250;
  2864.  
  2865.       /************************************************************************/
  2866.       /* store the piece location                          */
  2867.       /************************************************************************/
  2868.       sl.ptlLocation = ptl;
  2869.  
  2870.       /************************************************************************/
  2871.       /* draw one piece                               */
  2872.       /************************************************************************/
  2873.       GpiOpenSegment( hpsClient, lLastSegId);
  2874.       GpiSetTag( hpsClient, lLastSegId);
  2875.       GpiBeginPath( hpsClient, 1L);
  2876.       GpiMove( hpsClient, &ptl);
  2877.       GpiPolySpline( hpsClient, 12L, aptlSides);
  2878.       GpiEndPath( hpsClient);
  2879.       GpiSetColor( hpsClient, CLR_BLACK);
  2880.       GpiFillPath( hpsClient, 1L, 0L);
  2881.       GpiCloseSegment( hpsClient);
  2882.  
  2883.  
  2884.       /************************************************************************/
  2885.       /* create the masks                              */
  2886.       /************************************************************************/
  2887.       sizl.cx = 2 + ((bmpBitmapFile.cx
  2888.              * (sl.rclBitBlt.xRight - sl.rclBitBlt.xLeft))
  2889.              / (ptlTopRight.x - ptlBotLeft.x));
  2890.       sizl.cy = 2 + ((bmpBitmapFile.cy
  2891.              * (sl.rclBitBlt.yTop - sl.rclBitBlt.yBottom))
  2892.              / (ptlTopRight.y - ptlBotLeft.y));
  2893.  
  2894.       bmp    = bmpBitmapFile;
  2895.       bmp.cx = LOUSHORT( sizl.cx);
  2896.       bmp.cy = LOUSHORT( sizl.cy);
  2897.  
  2898.       sl.hdcFill = DevOpenDC( habMain
  2899.                 , OD_MEMORY
  2900.                 , "*"
  2901.                 , 3L
  2902.                 , (PDEVOPENDATA)&dop
  2903.                 , NULL);
  2904.       sl.hpsFill = GpiCreatePS( habMain
  2905.                   , sl.hdcFill
  2906.                   , &sizl
  2907.                   , PU_PELS | GPIA_ASSOC | GPIT_MICRO );
  2908.       sl.hbmFill = GpiCreateBitmap( sl.hpsFill
  2909.                   , &bmp
  2910.                   , 0L
  2911.                   , NULL
  2912.                   , NULL);
  2913.  
  2914.  
  2915.       sl.hdcHole = DevOpenDC( habMain
  2916.                 , OD_MEMORY
  2917.                 , "*"
  2918.                 , 3L
  2919.                 , (PDEVOPENDATA)&dop
  2920.                 , NULL);
  2921.       sl.hpsHole = GpiCreatePS( habMain
  2922.                   , sl.hdcHole
  2923.                   , &sizl
  2924.                   , PU_PELS | GPIA_ASSOC | GPIT_MICRO );
  2925.       sl.hbmHole = GpiCreateBitmap( sl.hpsHole
  2926.                   , &bmp
  2927.                   , 0L
  2928.                   , NULL
  2929.                   , NULL);
  2930.  
  2931.  
  2932.  
  2933.       GpiSetSegmentAttrs( hpsClient, lLastSegId, ATTR_DETECTABLE, ATTR_ON);
  2934.  
  2935.       sl.lSegId = lLastSegId;
  2936.       sl.pslNext = NULL;
  2937.       sl.pslPrev = NULL;
  2938.       sl.fIslandMark = FALSE;
  2939.       sl.ptlModelXlate.x = sl.ptlModelXlate.y = 0L;
  2940.       SetRect( &sl);
  2941.       psl = SegListUpdate( ADD_TAIL_SEG, &sl);
  2942.       psl->pslNextIsland = psl;     /* point to self ==> island of one    */
  2943.     }
  2944.   }
  2945.   return( TRUE);
  2946. }
  2947.  
  2948. /******************************************************************************/
  2949. /*                                                                            */
  2950. /* Create the Temp and Drag bitmaps.                          */
  2951. /*                                                                            */
  2952. /******************************************************************************/
  2953. BOOL
  2954. PrepareBitmap()
  2955. {
  2956.   hbmBitmapSize    = GpiCreateBitmap( hpsBitmapSize
  2957.                     , &bmpBitmapFile
  2958.                     , 0L
  2959.                     , NULL
  2960.                     , NULL);
  2961.   if( !hbmBitmapSize)
  2962.     return( FALSE);
  2963.  
  2964.   bmpBitmapSave    = bmpBitmapFile;
  2965.   bmpBitmapSave.cx = LOUSHORT( sizlMaxClient.cx);
  2966.   bmpBitmapSave.cy = LOUSHORT( sizlMaxClient.cy);
  2967.   hbmBitmapSave    = GpiCreateBitmap( hpsBitmapSave
  2968.                     , &bmpBitmapSave
  2969.                     , 0L
  2970.                     , NULL
  2971.                     , NULL);
  2972.   if( !hbmBitmapSave)
  2973.     return( FALSE);
  2974.   GpiSetBitmap( hpsBitmapSave, hbmBitmapSave);
  2975.  
  2976.   hbmBitmapBuff    = GpiCreateBitmap( hpsBitmapBuff
  2977.                     , &bmpBitmapSave
  2978.                     , 0L
  2979.                     , NULL
  2980.                     , NULL);
  2981.   if( !hbmBitmapBuff)
  2982.     return( FALSE);
  2983.   GpiSetBitmap( hpsBitmapBuff, hbmBitmapBuff);
  2984.  
  2985.   return( TRUE);
  2986. }
  2987.  
  2988. /******************************************************************************/
  2989. /*                                                                            */
  2990. /* Create a memory DC and an associated PS.                      */
  2991. /*                                                                            */
  2992. /******************************************************************************/
  2993. BOOL
  2994. CreateBitmapHdcHps( phdc, phps)
  2995.  
  2996. PHDC  phdc;
  2997. PHPS  phps;
  2998. {
  2999.   SIZEL    sizl;
  3000.   HDC       hdc;
  3001.   HPS       hps;
  3002.  
  3003.   hdc = DevOpenDC( habMain, OD_MEMORY, "*", 3L, (PDEVOPENDATA)&dop, NULL);
  3004.   if( !hdc)
  3005.     return( FALSE);
  3006.  
  3007.   sizl.cx = sizl.cy = 0L;
  3008.   hps = GpiCreatePS( habMain
  3009.            , hdc
  3010.            , &sizl
  3011.            , PU_PELS | GPIA_ASSOC | GPIT_MICRO );
  3012.   if( !hps)
  3013.     return( FALSE);
  3014.  
  3015.   *phdc = hdc;
  3016.   *phps = hps;
  3017.   return( TRUE);
  3018. }
  3019.  
  3020. /******************************************************************************/
  3021. /*                                          */
  3022. /* Get the bitmap from disk.                              */
  3023. /* Note that there are 2 formats for bitmap files, one of which is archaic.   */
  3024. /* Both formats are supported here.  All new bitmaps should follow the format */
  3025. /* in BITMAPFILEHEADER.                               */
  3026. /*                                          */
  3027. /******************************************************************************/
  3028. BOOL
  3029. ReadBitmap( hfile)
  3030.  
  3031. HFILE  hfile;
  3032. {
  3033.     ULONG cScans;
  3034.     ULONG ulSize;     /* Number of bytes occupied by bitmap bits.          */
  3035.     USHORT cSegs;     /* Number of 64K segments in ulSize.              */
  3036.     USHORT cbExtra;     /* Bytes in last segment of ulSize.              */
  3037.     SEL sel;         /* Base selector to file data.               */
  3038.     USHORT hugeshift;     /* Segment index shift value.                  */
  3039.     USHORT cbRead1;     /* Number of bytes to read first call to DosRead     */
  3040.     USHORT cbRead2;     /* Number of bytes to read second call to DosRead    */
  3041.     USHORT cbRead;     /* Number of bytes read by DosRead.              */
  3042.     BOOL fRet = FALSE;     /* Function return code.                  */
  3043.     INT  i;         /* Generic loop index.                   */
  3044.     FILESTATUS fsts;
  3045.     PBITMAPFILEHEADER pbfh;
  3046.     PRCBITMAP  rb;
  3047.     PBYTE pImage;
  3048.  
  3049.  
  3050.     /**************************************************************************/
  3051.     /* Find out how big the file is so we can read the whole thing in.          */
  3052.     /**************************************************************************/
  3053.  
  3054.     if( DosQFileInfo( hfile, 1, &fsts, sizeof(FILESTATUS)) != 0)
  3055.     goto ReadBitmap_close_file;
  3056.  
  3057.     ulSize  = fsts.cbFile;
  3058.     cSegs   = (USHORT)(ulSize/0x10000L);
  3059.     cbExtra = (USHORT)(ulSize%0x10000L);
  3060.     if (DosAllocHuge(cSegs, cbExtra, (PSEL)&sel, 0, 0))
  3061.     goto ReadBitmap_close_file;
  3062.     if (DosGetHugeShift( &hugeshift))
  3063.     goto ReadBitmap_free_bits;
  3064.  
  3065.     pImage = (PBYTE)MAKEP(sel, 0);
  3066.     rb       = (PRCBITMAP)pImage;
  3067.     pbfh   = (PBITMAPFILEHEADER)pImage;
  3068.  
  3069.  
  3070.     /**************************************************************************/
  3071.     /* Read the bits in from the file. The DosRead function allows a          */
  3072.     /* maximum of 64K-1 bytes read at a time.  We get around this          */
  3073.     /* by reading two 32K chunks for each 64K segment, and reading the          */
  3074.     /* last segment in one piece.                          */
  3075.     /**************************************************************************/
  3076.  
  3077.     for (i = 0; i <= cSegs; ++i)
  3078.     {
  3079.     if (i < cSegs)
  3080.     {
  3081.         /* This segment is 64K bytes long, so split it up. */
  3082.         cbRead1 = 0x8000;
  3083.         cbRead2 = 0x8000;
  3084.     }
  3085.     else
  3086.     {
  3087.         /* This segment is less than 64K bytes long, so read it all. */
  3088.         cbRead1 = cbExtra;
  3089.         cbRead2 = 0;
  3090.     }
  3091.  
  3092.     /* There's a possibility that cbExtra will be 0, so check
  3093.      * to avoid an unnecessary system call.
  3094.      */
  3095.     if (cbRead1 > 0)
  3096.     {
  3097.         if (DosRead( hfile
  3098.                , (PVOID)MAKEP(sel+(i<<hugeshift), 0)
  3099.                , cbRead1
  3100.                , &cbRead))
  3101.         goto ReadBitmap_free_bits;
  3102.         if (cbRead1 != cbRead)
  3103.         goto ReadBitmap_free_bits;
  3104.     }
  3105.  
  3106.     /* This will always be skipped on the last partial segment. */
  3107.     if (cbRead2 > 0)
  3108.     {
  3109.         if (DosRead( hfile
  3110.                , (PVOID)MAKEP(sel+(i<<hugeshift), cbRead1)
  3111.                , cbRead2
  3112.                , &cbRead))
  3113.         goto ReadBitmap_free_bits;
  3114.         if (cbRead2 != cbRead)
  3115.         goto ReadBitmap_free_bits;
  3116.     }
  3117.     }
  3118.  
  3119.  
  3120.     /**************************************************************************/
  3121.     /* Tell GPI to put the bits into the thread's PS. The function returns    */
  3122.     /* the number of scan lines of the bitmap that were copied.  We want      */
  3123.     /* all of them at once.                              */
  3124.     /**************************************************************************/
  3125.  
  3126.     if (pbfh->bmp.cbFix != sizeof(BITMAPINFOHEADER))
  3127.     {
  3128.     bmpBitmapFile.cx    = rb->bmWidth;
  3129.     bmpBitmapFile.cy    = rb->bmHeight;
  3130.     bmpBitmapFile.cPlanes    = rb->bmPlanes;
  3131.     bmpBitmapFile.cBitCount = rb->bmBitcount;
  3132.     hbmBitmapFile = GpiCreateBitmap( hpsBitmapFile
  3133.                        , &bmpBitmapFile
  3134.                        , 0L
  3135.                        , NULL
  3136.                        , NULL);
  3137.     if( !hbmBitmapFile)
  3138.         goto ReadBitmap_free_bits;
  3139.     GpiSetBitmap( hpsBitmapFile, hbmBitmapFile);
  3140.  
  3141.         pImage += rb->dwBitsOffset;
  3142.         rb->dwBitsOffset = sizeof(BITMAPINFOHEADER);
  3143.     cScans = GpiSetBitmapBits( hpsBitmapFile
  3144.                  , 0L
  3145.                  , (LONG)rb->bmHeight
  3146.                  , pImage
  3147.                  , (PBITMAPINFO)&(rb->dwBitsOffset));
  3148.     if (cScans != (LONG)rb->bmHeight)  /* original number of scans ? */
  3149.         goto ReadBitmap_free_bits;
  3150.     }
  3151.     else
  3152.     {
  3153.     bmpBitmapFile.cx    = pbfh->bmp.cx;
  3154.     bmpBitmapFile.cy    = pbfh->bmp.cy;
  3155.     bmpBitmapFile.cPlanes    = pbfh->bmp.cPlanes;
  3156.     bmpBitmapFile.cBitCount = pbfh->bmp.cBitCount;
  3157.     hbmBitmapFile = GpiCreateBitmap( hpsBitmapFile
  3158.                        , &bmpBitmapFile
  3159.                        , 0L
  3160.                        , NULL
  3161.                        , NULL);
  3162.     if( !hbmBitmapFile)
  3163.         goto ReadBitmap_free_bits;
  3164.     GpiSetBitmap( hpsBitmapFile, hbmBitmapFile);
  3165.  
  3166.     cScans = GpiSetBitmapBits( hpsBitmapFile
  3167.                  , 0L
  3168.                  , (LONG)pbfh->bmp.cy
  3169.                  , pImage + pbfh->offBits
  3170.                  , (PBITMAPINFO)&(pbfh->bmp));
  3171.     if (cScans != (LONG)pbfh->bmp.cy)  /* original number of scans ? */
  3172.         goto ReadBitmap_free_bits;
  3173.     }
  3174.  
  3175.     fRet = TRUE;     /* okey-dokey */
  3176.  
  3177.  
  3178.     /**************************************************************************/
  3179.     /* Close the file, free the buffer space and leave.  This is a          */
  3180.     /* common exit point from the function.  Since the same cleanup          */
  3181.     /* operations need to be performed for such a large number of          */
  3182.     /* possible error conditions, this is concise way to do the right          */
  3183.     /* thing.                                      */
  3184.     /**************************************************************************/
  3185.  
  3186. ReadBitmap_free_bits:
  3187.     DosFreeSeg( sel);
  3188. ReadBitmap_close_file:
  3189.     DosClose( hfile);
  3190.     return fRet;
  3191. }
  3192.