home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk12 / jigsaw / jigsaw.c next >
Encoding:
C/C++ Source or Header  |  1990-07-06  |  90.1 KB  |  2,684 lines

  1. /********************************** Jigsaw  ***********************************/
  2. /*                                                                            */
  3. /* Created 1988, Microsoft Corporation.                       */
  4. /*                                                                            */
  5. /* Purpose:  To illustrate the use of Gpi retained segments.              */
  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. /*   Each piece of the puzzle is a retained segment.  When a piece is          */
  13. /*   selected for dragging, it is made dynamic.  A menu option allows the     */
  14. /*   selected piece to be dragged as an outline or as a normal-looking piece. */
  15. /*                                                                            */
  16. /*   Individual pieces are made to "move" by changing their model transforms. */
  17. /*   Scrolling and zooming of the whole picture is done by changing the       */
  18. /*   default viewing transform.                           */
  19. /*                                                                            */
  20. /* Optimizations:  While it is possible to implement this puzzle using a very */
  21. /*   naive approach, this is liable to lead to a rather slowly-operating      */
  22. /*   program.  The following optimizations dramatically improve program       */
  23. /*   performance:                                  */
  24. /*                                                                            */
  25. /*   1> BitBlt only as much of the bitmap through a clip path as necessary.   */
  26. /*   Each piece of the puzzle is drawn by defining a clip path, blitting      */
  27. /*   through the path, and drawing an outline on the same path.  The naive    */
  28. /*   approach is to blit the whole bitmap through the clip path.  A more      */
  29. /*   clever approach is to compute the piece's bounding box and only use      */
  30. /*   the source and destination rectangles which correspond to this box.      */
  31. /*   This leads to an order-of-magnitude speedup in the time to draw one      */
  32. /*   piece.                                      */
  33. /*                                                                            */
  34. /*   2> Make the source and target rectangles for BitBlt the same size          */
  35. /*   in device coordinates.  A BitBlt in a retained segment must be done      */
  36. /*   with GpiWCBitBlt and the target rectangle must be specified in world     */
  37. /*   coordinates, so you must use GpiConvert (taking into account that in     */
  38. /*   world space rectangles are inclusive-inclusive while in device space     */
  39. /*   rectangles are inclusive-exclusive) to compute what target world space   */
  40. /*   rectangle will be converted to the desired device space rectangle.       */
  41. /*   Making the sizes of the source and converted target rectangles differ    */
  42. /*   by even one pel will cause strectching or compression to occur, with     */
  43. /*   a dramatic loss in speed.    Unfortunately, due to rounding effects, it is */
  44. /*   not always possible to guarantee that adding an offset to the          */
  45. /*   transformation applied to a segment will leave the size of the          */
  46. /*   rectangle defined by the orders in the segment unchanged.              */
  47. /*                                                                            */
  48. /*   3> Use auxiliary information to reduce the number of segments which      */
  49. /*   must be checked for correlation.  The naive approach to hit-testing is   */
  50. /*   to test the whole chain, even though generally only a small fraction of  */
  51. /*   the segments in the chain could possibly get a hit.  A more clever       */
  52. /*   approach is to take the bounding box for each segment and only include   */
  53. /*   the segment in the correlation check if the box contains the correlation */
  54. /*   point.  eg.                                  */
  55. /*    a> Edit the chain by adjusting the ATTR_CHAINED attribute of each       */
  56. /*    segment to reflect candidacy for being hit.  Afterwards, fix up by      */
  57. /*    adding back removed segments to the chain.                  */
  58. /*    b> Even faster is to keep an auxiliary data structure which records     */
  59. /*    the priority of the segments (placed in the SEGLIST chain).  Run          */
  60. /*    through the priority list from high-priority to low-priority and do a   */
  61. /*    correlation test on each segment which passes the bounding-box test.    */
  62. /*                                                                            */
  63. /*   4> When repainting through a clip region, only draw those segments which */
  64. /*   overlap the clip region.  The naive approach is to set up the clip       */
  65. /*   region and do a GpiDrawChain on the whole chain.  The drawback to this   */
  66. /*   is that much time will be spent running through the orders in segments   */
  67. /*   which are not visible through the clip region.  Very often, most of the  */
  68. /*   segments in the picture can be eliminated from needing to be drawn by    */
  69. /*   recognizing that there is no overlap between the bounding boxes of the   */
  70. /*   segment and the clip region.                          */
  71. /*                                                                            */
  72. /*   5> Do WinScrollWindow horizontally in multiples of 8 pels when possible. */
  73. /*   For example, horizontal scrolls by 7 or 9 pels are much slower than a    */
  74. /*   a horizontal scroll by 8 pels.                          */
  75. /*                                                                            */
  76. /******************************************************************************/
  77.  
  78. #define INCL_BITMAPFILEFORMAT
  79.  
  80. #define INCL_DOSPROCESS
  81. #define INCL_DOSSEMAPHORES
  82. #define INCL_DOSMEMMGR
  83.  
  84. #define INCL_DEV
  85.  
  86. #define INCL_WINWINDOWMGR
  87. #define INCL_WINMESSAGEMGR
  88. #define INCL_WININPUT
  89. #define INCL_WINRECTANGLES
  90. #define INCL_WINPOINTERS
  91. #define INCL_WINMENUS
  92. #define INCL_WINSCROLLBARS
  93. #define INCL_WINFRAMEMGR
  94. #define INCL_WINSWITCHLIST
  95. #define INCL_WINSYS
  96.  
  97. #define INCL_GPIBITMAPS
  98. #define INCL_GPICONTROL
  99. #define INCL_GPITRANSFORMS
  100. #define INCL_GPIPRIMITIVES
  101. #define INCL_GPIMETAFILES
  102. #define INCL_GPIPATHS
  103. #define INCL_GPIREGIONS
  104. #define INCL_GPISEGMENTS
  105. #define INCL_GPISEGEDITING
  106. #define INCL_GPICORRELATION
  107. #define INCL_GPILCIDS
  108.  
  109. #define INCL_ERRORS
  110.  
  111. #include <os2.h>
  112. #include <stdlib.h>
  113. #include <stdio.h>
  114. #include <string.h>
  115. #include <opendlg.h>
  116. #include "noncomm.h"
  117. #include "jigsaw.h"
  118.  
  119.  
  120.  
  121. /*----------------------- inter-thread messages ------------------------------*/
  122.  
  123. #define UM_DIE          WM_USER+1        /* instruct async thread to terminate  */
  124. #define UM_DRAW       WM_USER+2        /* draw the current picture          */
  125. #define UM_VSCROLL    WM_USER+3        /* perform scroll by recalculating the */
  126.                                        /* default viewing transform           */
  127. #define UM_HSCROLL    WM_USER+4        /* perform scroll by recalculating the */
  128.                                        /* default viewing transform           */
  129. #define UM_SIZING     WM_USER+5        /* perform sizing by recalculating the */
  130.                                        /* default viewing transform           */
  131. #define UM_ZOOM_IN    WM_USER+6        /* zoom the picture by recalculating   */
  132.                                        /* the default viewing transform       */
  133. #define UM_ZOOM_OUT   WM_USER+7        /* zoom the picture by recalculating   */
  134.                                        /* the default viewing transform       */
  135. #define UM_REDRAW     WM_USER+8
  136. #define UM_JUMBLE     WM_USER+9
  137. #define UM_LOAD       WM_USER+10
  138. #define UM_DUMMY      WM_USER+11       /* all commands not forcing redraw     */
  139.                                        /* must come after this one            */
  140.  
  141. #define UM_LEFTDOWN   WM_USER+12       /* mouse button down in the client area*/
  142.                        /* perform a correlation on the current*/
  143.                        /* picture, setting any picked segment */
  144.                        /* to dynamic                  */
  145. #define UM_MOUSEMOVE  WM_USER+13       /* mousemove command, remove, repositon*/
  146.                        /* and redraw any dynamic sements      */
  147. #define UM_LEFTUP     WM_USER+14       /* mouse button up in the client area  */
  148.                                        /* set any dynamic segment to normal   */
  149. #define UM_FASTDRAG   WM_USER+15       /* toggle fast-drag (outline) mode     */
  150. #define UM_DRAWDONE   WM_USER+16       /* Async DrawChain has completed       */
  151. #define UM_FLUSH      WM_USER+17
  152.  
  153.  
  154. /*------------------------ element label values  -----------------------------*/
  155.  
  156. #define FILLPATH       222L
  157. #define BITBLT_TOP     232L
  158. #define BITBLT_BOTTOM  233L
  159.  
  160.  
  161. /*------------------------- correlation parameters ---------------------------*/
  162.  
  163. #define HITS    1L               /* maximum number of hits to return    */
  164. #define DEPTH    2L               /* max depth of seg calls to return    */
  165.  
  166.  
  167. /*-------------------------- general definitions -----------------------------*/
  168.  
  169.  
  170. HAB    habMain=NULL;               /* main thread anchor block handle     */
  171. HMQ    hmqMain=NULL;               /* main thread queue handle          */
  172. HWND    hwndFrame=NULL;                /* frame control handle                */
  173. HWND    hwndClient=NULL;           /* client area handle              */
  174. HDC    hdcClient=NULL;            /* window dc handle              */
  175. HPS    hpsClient=NULL;            /* client area Gpi ps handle          */
  176. SIZEL    sizlMaxClient;               /* max client area size              */
  177. HPS     hpsPaint=NULL;                 /* ps for use in Main Thread           */
  178. HRGN    hrgnInvalid = NULL;           /* handle to the invalid region          */
  179.  
  180. HAB    habAsync=NULL;               /* async thread anchor block handle    */
  181. HMQ    hmqAsync=NULL;               /* async thread queue handle          */
  182. TID     tidAsync;                      /* async thread id                     */
  183. SEL    selStack;               /* async thread stack selector          */
  184. #define STACKSIZE  4096            /* async thread stack size          */
  185. SHORT    sPrty = -1;               /* async thread priority           */
  186.  
  187. HWND    hwndHorzScroll=NULL;           /* horizontal scroll bar window          */
  188. HWND    hwndVertScroll=NULL;           /* vertical scroll bar window          */
  189. POINTS    ptsScrollPos, ptsOldScrollPos;
  190. POINTS    ptsScrollMax, ptsHalfScrollMax;
  191. POINTS    ptsScrollLine = { 8, 8};
  192. POINTS    ptsScrollPage = { 64, 64};
  193.  
  194. #define UNITY           65536L
  195. MATRIXLF matlfIdentity = { UNITY, 0, 0, 0, UNITY, 0, 0, 0, 1 };
  196. LONG    lScale = 0;               /* current zoom level              */
  197. #define ZOOM_MAX       8
  198. #define ZOOM_IN_ARG    1
  199. #define ZOOM_OUT_ARG   -1
  200.  
  201. #define CALLSEG_BASE   1000
  202. POINTL    ptlOffset;
  203. POINTL    ptlBotLeft  = { 0, 0};
  204. POINTL    ptlTopRight = { 300, 300};
  205. LONG    lLastSegId;               /* last segment id assigned to a piece */
  206. LONG    lPickedSeg;               /* seg id of piece selected for drag   */
  207. RECTL    rclBounds;               /* pict bounding box in model coords.  */
  208. POINTL    ptlOldMouse = {0L, 0L};        /* current mouse posn              */
  209. BOOL    fButtonDown = FALSE;           /* only drag if mouse down          */
  210. BOOL    fFastDrag = FALSE;           /* show only outline of dragging piece */
  211.  
  212.  
  213. /*-------------------------- segment list ------------------------------------*/
  214.  
  215. typedef struct _SEGLIST {           /* sl                      */
  216.     LONG          lSegId;
  217.     struct _SEGLIST FAR * pslPrev;
  218.     struct _SEGLIST FAR * pslNext;
  219.     POINTL          ptlLocation; /* piece location, world coordinates   */
  220.     RECTL          rclCurrent;  /* segment bounding box, model coords  */
  221.     RECTL          rclBitBlt;   /* segment bounding box, world coords  */
  222. } SEGLIST ;
  223. typedef SEGLIST FAR *PSEGLIST;           /* psl                      */
  224. typedef PSEGLIST FAR *PPSEGLIST;       /* ppsl                      */
  225. PSEGLIST pslHead = NULL;           /* head of the list              */
  226. PSEGLIST pslTail = NULL;           /* tail of the list              */
  227. PSEGLIST pslPicked = NULL;           /* picked segment's list member        */
  228. #define   ADD_HEAD_SEG     1
  229. #define   ADD_TAIL_SEG     2
  230. #define        DEL_SEG     3
  231.  
  232. /*-------------------------- bitmap-related data -----------------------------*/
  233.  
  234. typedef struct _LOADINFO {           /* li                      */
  235.     HFILE   hf;
  236.     CHAR    szFileName[MAX_FNAME_LEN];
  237. } LOADINFO ;
  238. typedef LOADINFO FAR *PLOADINFO;       /* pli                      */
  239.  
  240. HPS           hpsBitmapFile=NULL, hpsBitmapTemp=NULL, hpsBitmapDrag=NULL;
  241. HDC           hdcBitmapFile=NULL, hdcBitmapTemp=NULL, hdcBitmapDrag=NULL;
  242. HBITMAP        hbmBitmapFile=NULL, hbmBitmapTemp=NULL, hbmBitmapDrag=NULL;
  243. BITMAPINFOHEADER   bmpBitmapFile   = {12L, 0, 0, 0, 0};
  244. BITMAPINFOHEADER   bmpBitmapTemp   = {12L, 0, 0, 0, 0};
  245. BITMAPINFOHEADER   bmpBitmapDrag   = {12L, 0, 0, 0, 0};
  246. BITMAPINFO       bmiBitmap       = {12L, 0, 0, 0, 0, {{0, 0, 0}}};
  247. static DEVOPENSTRUC dop = { NULL
  248.               , "DISPLAY"
  249.               , NULL
  250.               , NULL
  251.               , NULL
  252.               , NULL
  253.               , NULL
  254.               , NULL
  255.               , NULL };
  256.  
  257.  
  258. /*-------------------------- old-style bitmap header -------------------------*/
  259.  
  260. typedef struct {
  261.     USHORT    wType;
  262.     ULONG     dwSize;
  263.     int       xHotspot;
  264.     int       yHotspot;
  265.     ULONG     dwBitsOffset;
  266.     USHORT    bmWidth;
  267.     USHORT    bmHeight;
  268.     USHORT    bmPlanes;
  269.     USHORT    bmBitcount;
  270. } RCBITMAP;
  271. typedef RCBITMAP FAR *PRCBITMAP;
  272.  
  273.  
  274. /*--------------------------- Miscellaneous ----------------------------------*/
  275.  
  276. ULONG    ulTerminateSem = 0;           /* main thread blocks while async dies */
  277. HSEM    hsemTerminate  = &ulTerminateSem;
  278.  
  279. ULONG    ulSzFmt   = 0;               /* serializes access to sprintf()      */
  280. HSEM    hsemSzFmt = &ulSzFmt;
  281. CHAR    szFmt[50];               /* buffer used by sprintf()          */
  282.  
  283. SWCNTRL swctl = { 0, 0, 0, 0, 0, SWL_VISIBLE, SWL_JUMPABLE, 0, 0 };
  284. HSWITCH hsw;                   /* handle to a switch list entry       */
  285. char    szTitle[80];               /* Title bar text              */
  286.  
  287. BOOL    fErrMem = FALSE;           /* set if alloc async stack fails      */
  288.  
  289.  
  290. /*------------------------- Function Prototypes ------------------------------*/
  291.  
  292. VOID     CalcBounds( VOID);
  293. VOID     CalcTransform( HWND);
  294. MRESULT CALLBACK ClientWndProc( HWND, USHORT, MPARAM, MPARAM);
  295. BOOL     CreateBitmapHdcHps( HDC, HPS);
  296. BOOL     CreateThread( VOID);
  297. BOOL     CreatePicture( VOID);
  298. VOID     DestroyThread( VOID);
  299. BOOL     DoDraw( HRGN);
  300. VOID     DoHorzScroll( VOID);
  301. VOID     DoVertScroll( VOID);
  302. BOOL     DumpPicture( VOID);
  303. VOID     Finalize( VOID);
  304. BOOL     Initialize( VOID);
  305. VOID     Jumble( VOID);
  306. VOID     LeftDown( MPARAM);
  307. VOID     LeftUp( VOID);
  308. VOID     Load( PLOADINFO);
  309. VOID cdecl main( VOID);
  310. VOID     MessageInt( HWND, INT, PCH);
  311. VOID     MouseMove( MPARAM);
  312. VOID     MyMessageBox( HWND, PSZ);
  313. VOID FAR NewThread( VOID);
  314. BOOL     PrepareBitmap( VOID);
  315. BOOL     ReadBitmap( HFILE);
  316. VOID     Redraw( VOID);
  317. VOID     ReportError( HAB);
  318. BOOL     SegListCheck( INT);
  319. PSEGLIST SegListGet( LONG);
  320. BOOL     SegListUpdate( USHORT, PSEGLIST);
  321. BOOL     SendCommand( USHORT, ULONG);
  322. VOID     SetDVTransform( FIXED, FIXED, FIXED, FIXED, LONG, LONG, LONG);
  323. VOID     SetRect( PSEGLIST);
  324. VOID     ToggleFastDrag( VOID);
  325. VOID     Translate( PSEGLIST, PPOINTL);
  326. MRESULT  WndProcCommand( HWND, USHORT, MPARAM, MPARAM);
  327. MRESULT  WndProcCreate( HWND);
  328. MRESULT  WndProcPaint( VOID);
  329. MRESULT  WndProcSize( MPARAM, MPARAM);
  330. VOID     Zoom( SHORT);
  331. VOID     ZoomMenuItems( VOID);
  332.  
  333.  
  334. /******************************************************************************/
  335. /*                                          */
  336. /*  MyMessageBox                                  */
  337. /*                                          */
  338. /*  Displays a message box with the given string.  To simplify matters,       */
  339. /*  the box will always have the same title ("Jigsaw"), will always          */
  340. /*  have a single button ("Ok"), will always have an exclamation point          */
  341. /*  icon, and will always be application modal.                   */
  342. /*                                          */
  343. /******************************************************************************/
  344. VOID
  345. MyMessageBox( hWnd, psz)
  346.  
  347. HWND  hWnd;
  348. PSZ   psz;
  349. {
  350.     WinMessageBox( HWND_DESKTOP
  351.          , hWnd
  352.          , psz
  353.          , szTitle
  354.          , 0
  355.          , MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL );
  356. }
  357.  
  358. /******************************************************************************/
  359. /*                                                                            */
  360. /* Main thread will initialize the process for PM services and process          */
  361. /* the application message queue until a WM_QUIT message is received. It will */
  362. /* then destroy all PM resources and terminate. Any error during          */
  363. /* initialization will be reported and the process terminated.                */
  364. /*                                                                            */
  365. /******************************************************************************/
  366. VOID cdecl
  367. main()
  368. {
  369.   QMSG    qmsg;
  370.  
  371.   if( Initialize())
  372.       while( WinGetMsg( habMain, &qmsg, NULL, 0, 0))
  373.       WinDispatchMsg( habMain, &qmsg);
  374.   else
  375.       ReportError( habMain);
  376.   Finalize();
  377. }
  378.  
  379.  
  380. /******************************************************************************/
  381. /*                                                                            */
  382. /* The Initialize function will initialize the PM interface,              */
  383. /* create an application message queue, a standard frame window and a new     */
  384. /* thread to control drawing operations.  It will also initialize static      */
  385. /* strings.                                                                   */
  386. /*                                                                            */
  387. /******************************************************************************/
  388. BOOL
  389. Initialize()
  390. {
  391.   ULONG   flCreate;
  392.   PID      pid;
  393.   TID      tid;
  394.  
  395.   WinShowPointer( HWND_DESKTOP, TRUE);
  396.   habMain = WinInitialize( 0);
  397.   if( !habMain)
  398.       return( FALSE);
  399.  
  400.   hmqMain = WinCreateMsgQueue( habMain,0);
  401.   if( !hmqMain)
  402.       return( FALSE);
  403.  
  404.   WinLoadString( habMain, (HMODULE) NULL, TITLEBAR, sizeof(szTitle), szTitle);
  405.   if( !WinRegisterClass( habMain
  406.                , (PCH)szTitle
  407.                , (PFNWP)ClientWndProc
  408.                , CS_SIZEREDRAW
  409.                , 0 ))
  410.       return( FALSE);
  411.  
  412.   flCreate =   (FCF_STANDARD | FCF_VERTSCROLL | FCF_HORZSCROLL)
  413.          & ~(ULONG)FCF_TASKLIST;
  414.   hwndFrame = WinCreateStdWindow( HWND_DESKTOP
  415.                 , WS_VISIBLE
  416.                 , &flCreate
  417.                 , szTitle
  418.                 , szTitle
  419.                 , WS_VISIBLE
  420.                 , (HMODULE) NULL
  421.                 , APPMENU
  422.                 , &hwndClient);
  423.   if( !hwndFrame)
  424.       return( FALSE);
  425.  
  426.   WinQueryWindowProcess( hwndFrame, &pid, &tid);
  427.   swctl.hwnd      = hwndFrame;
  428.   swctl.idProcess = pid;
  429.   lstrcpy( swctl.szSwtitle, szTitle);
  430.   hsw = WinAddSwitchEntry( &swctl);
  431.  
  432.   if( !CreateThread())              /* create async thread             */
  433.       return ( FALSE);
  434.   if( !CreateBitmapHdcHps( &hdcBitmapFile, &hpsBitmapFile))
  435.       return( FALSE);
  436.   if( !CreateBitmapHdcHps( &hdcBitmapTemp, &hpsBitmapTemp))
  437.       return( FALSE);
  438.   if( !CreateBitmapHdcHps( &hdcBitmapDrag, &hpsBitmapDrag))
  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.   DestroyThread();
  454.  
  455.   while( pslHead != NULL )
  456.       SegListUpdate( DEL_SEG, pslHead);
  457.   if( hrgnInvalid)
  458.       GpiDestroyRegion( hpsClient, hrgnInvalid);
  459.   if( hpsClient)
  460.       GpiAssociate( hpsClient, NULL);
  461.   if( hpsPaint)
  462.       GpiAssociate( hpsPaint, NULL);
  463.   if( hpsBitmapFile)
  464.       GpiAssociate( hpsBitmapFile, NULL);
  465.   if( hpsBitmapTemp)
  466.       GpiAssociate( hpsBitmapTemp, NULL);
  467.   if( hpsBitmapDrag)
  468.       GpiAssociate( hpsBitmapDrag, NULL);
  469.   if( hwndFrame)
  470.       WinDestroyWindow( hwndFrame);
  471.   if( hmqMain)
  472.       WinDestroyMsgQueue( hmqMain);
  473.   if( habMain)
  474.       WinTerminate( habMain);
  475.  
  476.   DosExit( EXIT_PROCESS, 0);
  477. }
  478.  
  479.  
  480. /******************************************************************************/
  481. /*                                                                            */
  482. /* ReportError    will display the latest error information for the required    */
  483. /* thread. No resources to be loaded if out of memory error.                  */
  484. /*                                                                            */
  485. /******************************************************************************/
  486. VOID
  487. ReportError( hab)
  488.  
  489. HAB hab;
  490. {
  491.   PERRINFO  perriBlk;
  492.   PSZ        pszErrMsg;
  493.   USHORT *  TempPtr;
  494.  
  495.   if( !hwndFrame)
  496.       return;
  497.   if( !fErrMem)
  498.   {
  499.       perriBlk = WinGetErrorInfo(hab);
  500.       if( !perriBlk)
  501.           return;
  502.       SELECTOROF( pszErrMsg) = SELECTOROF(perriBlk);
  503.       SELECTOROF( TempPtr)   = SELECTOROF(perriBlk);
  504.       OFFSETOF( TempPtr)     = perriBlk->offaoffszMsg;
  505.       OFFSETOF( pszErrMsg)   = *TempPtr;
  506.       WinMessageBox( HWND_DESKTOP
  507.            , hwndFrame
  508.            , pszErrMsg
  509.            , szTitle
  510.            , 0
  511.            , MB_CUACRITICAL | MB_ENTER);
  512.       WinFreeErrorInfo( perriBlk);
  513.   } else
  514.       WinMessageBox( HWND_DESKTOP
  515.            , hwndFrame
  516.            , "ERROR - Out Of Memory"
  517.            , szTitle
  518.            , 0
  519.            , MB_CUACRITICAL | MB_ENTER);
  520. }
  521.  
  522.  
  523. /******************************************************************************/
  524. /*                                                                            */
  525. /* CreateThread  creates the asynchronous drawing thread. It will allocate    */
  526. /* stack space and create the thread.                                         */
  527. /*                                                                            */
  528. /******************************************************************************/
  529. BOOL
  530. CreateThread()
  531. {
  532.   PBYTE pbAsyncStack;              /* long pointer to stack for new thread */
  533.  
  534.  
  535.   if( DosAllocSeg( STACKSIZE, (PSEL)&selStack, 0 ))
  536.   {
  537.       fErrMem = TRUE;
  538.       return( FALSE);
  539.   }
  540.   OFFSETOF(pbAsyncStack) = STACKSIZE-2;
  541.   SELECTOROF(pbAsyncStack) = selStack;
  542.   if( DosCreateThread( (PFNTHREAD)NewThread, &tidAsync, pbAsyncStack ))
  543.       return( FALSE);
  544.   return( TRUE);
  545. }
  546.  
  547.  
  548. /******************************************************************************/
  549. /*                                                                            */
  550. /* DestroyThread  will send a message  to the asynchronous drawing thread     */
  551. /* commanding it to terminate itself. If the send is successful it will wait  */
  552. /* until the async thread has terminated. It will then release any stack space*/
  553. /* used by that thread.                                                       */
  554. /*                                                                            */
  555. /******************************************************************************/
  556. VOID
  557. DestroyThread()
  558. {
  559.   if( tidAsync)
  560.   {
  561.       DosSemSet( hsemTerminate);
  562.       if( SendCommand( (USHORT)UM_DIE, (ULONG)NULL))
  563.       DosSemWait( hsemTerminate, SEM_INDEFINITE_WAIT);
  564.   }
  565.   if( selStack)
  566.       DosFreeSeg( selStack);
  567. }
  568.  
  569.  
  570. /******************************************************************************/
  571. /*                                                                            */
  572. /* SendCommand    will attempt to post the required command and parameters to   */
  573. /* the asynchronous drawing thread's message queue. The command will only     */
  574. /* be posted if the queue exists.                          */
  575. /*                                                                            */
  576. /******************************************************************************/
  577. BOOL
  578. SendCommand( usCommand, ulInfo)
  579.  
  580. USHORT    usCommand;
  581. ULONG    ulInfo;
  582. {
  583.   if( !hmqAsync)
  584.       return( FALSE);
  585.  
  586.   switch( usCommand)
  587.   {
  588.     case UM_DIE:
  589.     case UM_LEFTDOWN:
  590.     case UM_LEFTUP:
  591.     case UM_MOUSEMOVE:
  592.     case UM_DRAW:
  593.     case UM_HSCROLL:
  594.     case UM_VSCROLL:
  595.     case UM_ZOOM_IN:
  596.     case UM_ZOOM_OUT:
  597.     case UM_REDRAW:
  598.     case UM_SIZING:
  599.     case UM_FASTDRAG:
  600.     case UM_JUMBLE:
  601.     case UM_LOAD:
  602.  
  603.     return( WinPostQueueMsg( hmqAsync
  604.                    , usCommand
  605.                    , MPFROMLONG( ulInfo)
  606.                    , MPFROMLONG( NULL  ) ) );
  607.     break;
  608.  
  609.     default:
  610.     return( TRUE);
  611.   }
  612. }
  613.  
  614.  
  615. /******************************************************************************/
  616. /*                                                                            */
  617. /* ClientWndProd is the window procedure associated with the client window.   */
  618. /*                                                                            */
  619. /******************************************************************************/
  620. MRESULT CALLBACK
  621. ClientWndProc( hwnd, msg, mp1, mp2)
  622.  
  623. HWND    hwnd;
  624. USHORT  msg;
  625. MPARAM    mp1;
  626. MPARAM    mp2;
  627. {
  628.   CHAR  szTemp[128];
  629.  
  630.   switch( msg)
  631.   {
  632.     case WM_CREATE:
  633.       return( WndProcCreate( hwnd));
  634.       break;
  635.  
  636.     case WM_CLOSE:
  637.       WinLoadString( habMain, (HMODULE) NULL, TERMINATE, sizeof(szTemp), (PSZ)szTemp );
  638.       if( WinMessageBox( HWND_DESKTOP
  639.                , hwndFrame
  640.                , szTemp
  641.                , szTitle
  642.                , 0
  643.                , MB_CUAWARNING | MB_YESNO | MB_DEFBUTTON2)
  644.            == MBID_YES)
  645.       WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
  646.       break;
  647.  
  648.     case WM_PAINT:
  649.       return( WndProcPaint());
  650.       break;
  651.  
  652.     /**************************************************************************/
  653.     /*                                          */
  654.     /**************************************************************************/
  655.     case WM_ERASEBACKGROUND:
  656.       WinFillRect( (HPS)mp1, (PRECTL)mp2, CLR_BACKGROUND);
  657.       return( FALSE);
  658.       break;
  659.  
  660.     /**************************************************************************/
  661.     /*                                          */
  662.     /**************************************************************************/
  663.     case WM_MINMAXFRAME:
  664.       if( (((PSWP)mp1)->fs & SWP_RESTORE)  ||
  665.       (((PSWP)mp1)->fs & SWP_MAXIMIZE) )
  666.     SendCommand( (USHORT)UM_SIZING, 0L);
  667.       break;
  668.  
  669.     /**************************************************************************/
  670.     /* Process menu item commands, and commands generated from the keyboard   */
  671.     /* via the accelerator table. Most are handled by the async thread        */
  672.     /**************************************************************************/
  673.     case WM_COMMAND:
  674.       return( WndProcCommand( hwnd, msg, mp1, mp2));
  675.       break;
  676.  
  677.     /**************************************************************************/
  678.     /* Scrolling is handled by the async drawing thread. Simply pass on the   */
  679.     /* command and parameters                                                 */
  680.     /**************************************************************************/
  681.     case WM_HSCROLL:
  682.       SendCommand( (USHORT)UM_HSCROLL, LONGFROMMP(mp2));
  683.       break;
  684.  
  685.     case WM_VSCROLL:
  686.       SendCommand( (USHORT)UM_VSCROLL, LONGFROMMP(mp2));
  687.       break;
  688.  
  689.     /************************************************************************/
  690.     /* The client area is being resized.                                    */
  691.     /************************************************************************/
  692.     case WM_SIZE:
  693.       return( WndProcSize( mp1, mp2));
  694.       break;
  695.  
  696.     /**************************************************************************/
  697.     /* Mouse commands are handled by the async thread. Simply send on the     */
  698.     /* command and parameters.                                                */
  699.     /**************************************************************************/
  700.     case WM_BUTTON1DBLCLK:
  701.     case WM_BUTTON1DOWN:
  702.       if( hwnd != WinQueryFocus( HWND_DESKTOP, FALSE))
  703.       WinSetFocus( HWND_DESKTOP, hwnd);
  704.       if( !fButtonDown)
  705.       {
  706.       fButtonDown = TRUE;
  707.       SendCommand( (USHORT)UM_LEFTDOWN, LONGFROMMP(mp1));
  708.       }
  709.       return((MRESULT) TRUE);
  710.       break;
  711.  
  712.     case WM_BUTTON1UP:
  713.       if( !fButtonDown)
  714.       return((MRESULT) TRUE);
  715.       if( SendCommand( (USHORT)UM_LEFTUP, LONGFROMMP(mp1)))
  716.           fButtonDown = FALSE;
  717.       else
  718.       WinAlarm( HWND_DESKTOP, WA_WARNING);
  719.       return((MRESULT) TRUE);
  720.       break;
  721.  
  722.     case WM_MOUSEMOVE:
  723.       if( fButtonDown && (pslPicked != NULL))
  724.       SendCommand( (USHORT)UM_MOUSEMOVE, LONGFROMMP(mp1));
  725.       return( WinDefWindowProc( hwnd, msg, mp1, mp2));
  726.       break;
  727.  
  728.     /**************************************************************************/
  729.     /* Default for the rest                              */
  730.     /**************************************************************************/
  731.     default:
  732.       return( WinDefWindowProc( hwnd, msg, mp1, mp2));
  733.   }
  734.  
  735.   return( FALSE);
  736. }
  737.  
  738. /******************************************************************************/
  739. /*                                          */
  740. /* Get the maximum client area size.  Create a window DC for the client       */
  741. /* area and a normal GPI Presentation Space and associate the two.  The GPI   */
  742. /* PS will be the maximum client area size and be in pels.              */
  743. /*                                          */
  744. /******************************************************************************/
  745. MRESULT
  746. WndProcCreate( hwnd)
  747.  
  748. HWND  hwnd;
  749. {
  750.   SIZEL sizlPickApp;              /* pick aperture size              */
  751.  
  752.   sizlMaxClient.cx = WinQuerySysValue( HWND_DESKTOP, SV_CXFULLSCREEN);
  753.   sizlMaxClient.cy = WinQuerySysValue( HWND_DESKTOP, SV_CYFULLSCREEN);
  754.  
  755.   hdcClient = WinOpenWindowDC( hwnd);
  756.   hpsClient = GpiCreatePS( habMain
  757.              , hdcClient
  758.              , &sizlMaxClient
  759.              , GPIA_ASSOC | PU_PELS );
  760.   if( !hpsClient)
  761.       return((MRESULT) TRUE);
  762.   GpiSetAttrMode( hpsClient, AM_PRESERVE);
  763.  
  764.   hwndHorzScroll = WinWindowFromID( WinQueryWindow( hwnd, QW_PARENT, FALSE)
  765.                   , FID_HORZSCROLL);
  766.  
  767.   hwndVertScroll = WinWindowFromID( WinQueryWindow( hwnd, QW_PARENT, FALSE)
  768.                   , FID_VERTSCROLL);
  769.  
  770.   hpsPaint = GpiCreatePS( habMain, NULL, &sizlMaxClient, PU_PELS);
  771.  
  772.   hrgnInvalid = GpiCreateRegion( hpsClient, 0L, NULL);
  773.  
  774.   sizlPickApp.cx = sizlPickApp.cy = 1;
  775.   GpiSetPickApertureSize( hpsClient, PICKAP_REC, &sizlPickApp);
  776.   return( FALSE);
  777. }
  778.  
  779.  
  780. /*******************************************************************************/
  781. /*                                           */
  782. /* WM_PAINT message                                   */
  783. /*                                           */
  784. /*******************************************************************************/
  785. MRESULT
  786. WndProcPaint()
  787.  
  788. {
  789.   HRGN     hrgnUpdt;
  790.   SHORT  sRgnType;
  791.  
  792.   hrgnUpdt = GpiCreateRegion( hpsPaint, 0L, NULL);
  793.   sRgnType = WinQueryUpdateRegion( hwndClient, hrgnUpdt);
  794.   WinValidateRegion( hwndClient, hrgnUpdt, FALSE);
  795.   SendCommand( UM_DRAW, (ULONG)hrgnUpdt);
  796.   return( FALSE);
  797. }
  798.  
  799. /******************************************************************************/
  800. /* Process menu item commands, and commands generated from the keyboard via   */
  801. /* the accelerator table.  Most are handled by the async thread           */
  802. /******************************************************************************/
  803. MRESULT
  804. WndProcCommand( hwnd, msg, mp1, mp2)
  805.  
  806. HWND    hwnd;
  807. USHORT  msg;
  808. MPARAM    mp1, mp2;
  809. {
  810.   CHAR        szTemp[128];
  811.   DLF        dlf;
  812.   SEL        sel;
  813.   PLOADINFO pli;
  814.   PSZ        pszError, psz1, psz2;
  815.  
  816.   switch( SHORT1FROMMP(mp1))
  817.   {
  818.     case MENU_JUMBLE:
  819.     SendCommand( UM_JUMBLE, 0L);
  820.     break;
  821.  
  822.     case MENU_LOAD:
  823.     DosAllocSeg( sizeof( LOADINFO), &sel, 0);
  824.     pli = MAKEP( sel, 0);
  825.  
  826.     dlf.rgbAction        = DLG_OPENDLG;
  827.     dlf.rgbFlags        = ATTRDIRLIST;
  828.     dlf.phFile        = &(pli->hf);
  829.     dlf.pszExt        = (PSZ)"\\*.bmp";
  830.     dlf.pszAppName        = szTitle;
  831.     dlf.pszTitle        = "Load Bitmap";
  832.     dlf.pszInstructions = NULL;
  833.     dlf.szFileName[0]   = '\0';
  834.     dlf.szOpenFile[0]   = '\0';
  835.     pszError        = "Error reading file.";
  836.  
  837.     switch( DlgFile( hwnd, &dlf))
  838.     {
  839.       case TDF_ERRMEM:
  840.       case TDF_INVALID:
  841.           MyMessageBox( hwnd, pszError);
  842.           break;
  843.  
  844.       case TDF_NOOPEN:
  845.           break;
  846.  
  847.       default:
  848.           for( psz1 = dlf.szFileName, psz2 = pli->szFileName
  849.          ; *psz2++ = *psz1++
  850.          ; )
  851.           ;
  852.           SendCommand( UM_LOAD, (LONG)pli);
  853.           break;
  854.     }
  855.     break;
  856.     /**********************************************************************/
  857.     /* EXIT command, menu item or F3 key pressed. Give the operator a      */
  858.     /* second chance, if confirmed post a WM_QUIT msg to the application  */
  859.     /* msg queue. This will force the MAIN thread to terminate.           */
  860.     /**********************************************************************/
  861.     case MENU_EXIT:
  862.       WinLoadString( habMain, (HMODULE) NULL, TERMINATE, sizeof(szTemp), szTemp);
  863.       if( WinMessageBox( HWND_DESKTOP
  864.                , hwndFrame
  865.                , szTemp
  866.                , szTitle
  867.                , 0
  868.                , MB_CUAWARNING | MB_YESNO | MB_DEFBUTTON2)
  869.         == MBID_YES)
  870.     WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
  871.       break;
  872.  
  873.     /**********************************************************************/
  874.     /* Pass on the rest to the async thread.                  */
  875.     /**********************************************************************/
  876.     case MENU_ZOOMIN:
  877.       SendCommand( UM_ZOOM_IN, 0L);
  878.       break;
  879.  
  880.     case MENU_ZOOMOUT:
  881.       SendCommand( UM_ZOOM_OUT, 0L);
  882.       break;
  883.  
  884.     case MENU_FASTDRAG:
  885.       SendCommand( UM_FASTDRAG, 0L);
  886.       break;
  887.  
  888.     /**********************************************************************/
  889.     /* Unrecognised => default                          */
  890.     /**********************************************************************/
  891.     default:
  892.       return( WinDefWindowProc(hwnd, msg, mp1, mp2));
  893.   }
  894.   return( FALSE);
  895. }
  896.  
  897. /******************************************************************************/
  898. /* Load a bitmap                                  */
  899. /******************************************************************************/
  900. VOID
  901. Load( pli)
  902.  
  903. PLOADINFO  pli;
  904. {
  905.     PSZ     pszError;
  906.     RECTL   rclClient;
  907.  
  908.     pszError = (PSZ)"Error reading file.";
  909.  
  910.     DumpPicture();
  911.     if( !ReadBitmap( pli->hf) )
  912.     {
  913.       MyMessageBox( hwndClient, pszError);
  914.       return;
  915.     }
  916.     if( !PrepareBitmap() )
  917.     {
  918.       MyMessageBox( hwndClient, pszError);
  919.       return;
  920.     }
  921.  
  922.     lstrcpy( swctl.szSwtitle, szTitle);
  923.     lstrcat( swctl.szSwtitle, ": ");
  924.     lstrcat( swctl.szSwtitle, pli->szFileName);
  925.     WinChangeSwitchEntry( hsw, &swctl);
  926.     WinSetWindowText( hwndFrame, swctl.szSwtitle);
  927.  
  928.     CreatePicture();
  929.     lScale = 0;
  930.  
  931.     WinQueryWindowRect( hwndClient, &rclClient);
  932.     ptsScrollMax.x = (SHORT)(rclClient.xRight - rclClient.xLeft);
  933.     ptsHalfScrollMax.x = ptsScrollMax.x >> 1;
  934.     ptsScrollPos.x = ptsHalfScrollMax.x;
  935.     ptsOldScrollPos.x = ptsHalfScrollMax.x;
  936.     WinSendMsg( hwndHorzScroll
  937.           , SBM_SETSCROLLBAR
  938.           , MPFROMSHORT( ptsScrollPos.x)
  939.           , MPFROM2SHORT( 1, ptsScrollMax.x) );
  940.     ptsScrollMax.y = (SHORT)(rclClient.yTop - rclClient.yBottom);
  941.     ptsHalfScrollMax.y = ptsScrollMax.y >> 1;
  942.     ptsScrollPos.y = ptsHalfScrollMax.y;
  943.     ptsOldScrollPos.y = ptsHalfScrollMax.y;
  944.     WinSendMsg( hwndVertScroll
  945.           , SBM_SETSCROLLBAR
  946.           , MPFROMSHORT( ptsScrollPos.y)
  947.           , MPFROM2SHORT( 1, ptsScrollMax.y) );
  948.  
  949.     CalcBounds();
  950.     CalcTransform( hwndClient);
  951.     DosFreeSeg( SELECTOROF( pli));
  952. }
  953. /******************************************************************************/
  954. /* Throw the pieces around the screen.                          */
  955. /******************************************************************************/
  956. VOID
  957. Jumble()
  958. {
  959.   LONG        lWidth, lHeight;
  960.   DATETIME  date;
  961.   POINTL    ptl;
  962.   RECTL     rclClient;
  963.   PSEGLIST  psl;
  964.  
  965.   if( WinQueryWindowRect( hwndClient, &rclClient) )
  966.   {
  967.     lWidth  = rclClient.xRight - rclClient.xLeft;
  968.     lHeight = rclClient.yTop   - rclClient.yBottom;
  969.     if( (lWidth > 0) && (lHeight > 0) )
  970.     {
  971.       DosGetDateTime( &date);
  972.       srand( (USHORT)date.hundredths);
  973.       for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  974.       {
  975.     ptl.x = rclClient.xLeft   + (rand() % lWidth);
  976.     ptl.y = rclClient.yBottom + (rand() % lHeight);
  977.     Translate( psl, &ptl);
  978.     SetRect( psl);
  979.       }
  980.     }
  981.   }
  982. }
  983.  
  984. /******************************************************************************/
  985. /* The client area is being resized.  The current scroll bar thumb position   */
  986. /* and scroll bar range must be recalculated prior to recalculating the       */
  987. /* default viewing transform for the picture.  Wait for subsequent WM_PAINT   */
  988. /* to do any drawing.                                  */
  989. /******************************************************************************/
  990. MRESULT
  991. WndProcSize( mp1, mp2)
  992.  
  993. MPARAM    mp1, mp2;
  994. {
  995.   HWND    hwndFrameTemp;
  996.  
  997.   if( hwndFrame)
  998.     hwndFrameTemp = hwndFrame;
  999.   else
  1000.     hwndFrameTemp = WinQueryWindow( hwndClient, QW_PARENT, FALSE);
  1001.  
  1002.   ptsScrollMax.y = SHORT2FROMMP( mp2);
  1003.   ptsHalfScrollMax.y = ptsScrollMax.y >> 1;
  1004.   if( mp1)
  1005.   {
  1006.       ptsScrollPos.y = (SHORT)(((LONG)ptsScrollPos.y * (LONG)SHORT2FROMMP(mp2))/(LONG)SHORT2FROMMP(mp1));
  1007.       ptsOldScrollPos.y = (SHORT)(((LONG)ptsOldScrollPos.y * (LONG)SHORT2FROMMP(mp2))/(LONG)SHORT2FROMMP(mp1));
  1008.   } else
  1009.   {
  1010.       ptsScrollPos.y = ptsHalfScrollMax.y;     /* first sizing after window creation  */
  1011.       ptsOldScrollPos.y = ptsHalfScrollMax.y;
  1012.   }
  1013.   WinSendMsg( hwndVertScroll
  1014.         , SBM_SETSCROLLBAR
  1015.         , MPFROMSHORT( ptsScrollPos.y)
  1016.         , MPFROM2SHORT( 1, ptsScrollMax.y) );
  1017.  
  1018.  
  1019.   ptsScrollMax.x = SHORT1FROMMP( mp2);
  1020.   ptsHalfScrollMax.x = ptsScrollMax.x >> 1;
  1021.   if( mp1)
  1022.   {
  1023.       ptsScrollPos.x = (SHORT)(((LONG)ptsScrollPos.x * (LONG)SHORT1FROMMP(mp2))/(LONG)SHORT1FROMMP(mp1));
  1024.       ptsOldScrollPos.x = (SHORT)(((LONG)ptsOldScrollPos.x * (LONG)SHORT1FROMMP(mp2))/(LONG)SHORT1FROMMP(mp1));
  1025.   } else
  1026.   {
  1027.       ptsScrollPos.x = ptsHalfScrollMax.x;     /* first sizing after window creation  */
  1028.       ptsOldScrollPos.x = ptsHalfScrollMax.x;
  1029.   }
  1030.   WinSendMsg( hwndHorzScroll
  1031.         , SBM_SETSCROLLBAR
  1032.         , MPFROMSHORT( ptsScrollPos.x)
  1033.         , MPFROM2SHORT( 1, ptsScrollMax.x) );
  1034.  
  1035.  
  1036.   SendCommand( UM_SIZING, 0L);
  1037.   return( FALSE);
  1038. }
  1039.  
  1040. /******************************************************************************/
  1041. /*                                                                            */
  1042. /* NewThread is the asynchronous drawing thread. It is responsible for all    */
  1043. /* drawing.  It will initialize its PM interface and create an application    */
  1044. /* message queue.  It will then monitor its message queue and process any     */
  1045. /* commands received.                                  */
  1046. /*                                                                            */
  1047. /******************************************************************************/
  1048. VOID FAR
  1049. NewThread()
  1050. {
  1051.   QMSG      qmsgAsync, qmsgPeek;
  1052.   BOOL      fDone;
  1053.  
  1054.   /****************************************************************************/
  1055.   /* Initialize the PM interface.  If it fails, terminate both threads.       */
  1056.   /****************************************************************************/
  1057.   habAsync = WinInitialize( 0);
  1058.   if( !habAsync)
  1059.   {
  1060.       WinPostMsg( hwndClient, WM_QUIT, NULL, NULL);
  1061.       DosExit( EXIT_THREAD, 0);
  1062.   }
  1063.  
  1064.   /****************************************************************************/
  1065.   /* Create a message queue.  If it fails, terminate both threads.          */
  1066.   /****************************************************************************/
  1067.   hmqAsync = WinCreateMsgQueue( habAsync, 80);
  1068.   if( !hmqAsync)
  1069.   {
  1070.       WinPostMsg( hwndClient, WM_QUIT, NULL, NULL);
  1071.       WinTerminate( habAsync);
  1072.       DosExit( EXIT_THREAD, 0);
  1073.   }
  1074.  
  1075.   DosSetPrty( PRTYS_THREAD, PRTYC_NOCHANGE, sPrty, (TID)NULL);
  1076.  
  1077.  
  1078.   while( TRUE)
  1079.   {
  1080.     WinGetMsg( habAsync, &qmsgAsync, NULL, 0, 0);
  1081.  
  1082.     /**************************************************************************/
  1083.     /* process the commands                                                   */
  1084.     /**************************************************************************/
  1085.     switch( qmsgAsync.msg)
  1086.     {
  1087.  
  1088.       /************************************************************************/
  1089.       /************************************************************************/
  1090.       case UM_LOAD:
  1091.     Load( (PLOADINFO)qmsgAsync.mp1);
  1092.     Redraw();
  1093.     break;
  1094.  
  1095.       /************************************************************************/
  1096.       case UM_JUMBLE:
  1097.     Jumble();
  1098.     Redraw();
  1099.     break;
  1100.  
  1101.       /************************************************************************/
  1102.       case UM_REDRAW:
  1103.     Redraw();
  1104.     break;
  1105.  
  1106.       /************************************************************************/
  1107.       /* DRAW will use the passed region containing the invalidated area of   */
  1108.       /* the screen, repaint it and then destroy the region.              */
  1109.       /************************************************************************/
  1110.       case UM_DRAW:
  1111.     DoDraw( (HRGN)qmsgAsync.mp1);
  1112.     if( qmsgAsync.mp1)
  1113.         GpiDestroyRegion( hpsClient, (HRGN)qmsgAsync.mp1);
  1114.         break;
  1115.  
  1116.  
  1117.       /************************************************************************/
  1118.       /* Get new scroll posn from command ( i.e. +/-1 +/-page) or new          */
  1119.       /* absolute position from parameter, update scroll posn, change the     */
  1120.       /* transform and update the thumb posn.  Finally update the window.     */
  1121.       /************************************************************************/
  1122.       case UM_HSCROLL:
  1123.     switch( SHORT2FROMMP( qmsgAsync.mp1) )
  1124.     {
  1125.             case SB_LINEUP:
  1126.         ptsScrollPos.x -= ptsScrollLine.x;
  1127.                 break;
  1128.             case SB_LINEDOWN:
  1129.         ptsScrollPos.x += ptsScrollLine.x;
  1130.                 break;
  1131.         case SB_SLIDERTRACK:
  1132.             case SB_SLIDERPOSITION:
  1133.         for( fDone = FALSE; !fDone ;)
  1134.         {
  1135.           if( WinPeekMsg( habAsync
  1136.                 , &qmsgPeek
  1137.                 , NULL
  1138.                 , UM_HSCROLL
  1139.                 , UM_HSCROLL
  1140.                 , PM_NOREMOVE))
  1141.               if(   (SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERTRACK)
  1142.               ||(SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERPOSITION) )
  1143.               WinPeekMsg( habAsync
  1144.                     , &qmsgAsync
  1145.                     , NULL
  1146.                     , UM_HSCROLL
  1147.                     , UM_HSCROLL
  1148.                     , PM_REMOVE);
  1149.               else
  1150.               fDone = TRUE;
  1151.           else
  1152.               fDone = TRUE;
  1153.         }
  1154.         ptsScrollPos.x = SHORT1FROMMP( qmsgAsync.mp1);
  1155.                 break;
  1156.             case SB_PAGEUP:
  1157.         ptsScrollPos.x -= ptsScrollPage.x;
  1158.                 break;
  1159.             case SB_PAGEDOWN:
  1160.         ptsScrollPos.x += ptsScrollPage.x;
  1161.                 break;
  1162.             case SB_ENDSCROLL:
  1163.                 break;
  1164.             default:
  1165.                 break;
  1166.     }
  1167.     DoHorzScroll();
  1168.         break;
  1169.  
  1170.       case UM_VSCROLL:
  1171.     switch( SHORT2FROMMP( qmsgAsync.mp1) )
  1172.     {
  1173.             case SB_LINEUP:
  1174.         ptsScrollPos.y -= ptsScrollLine.y;
  1175.                 break;
  1176.             case SB_LINEDOWN:
  1177.         ptsScrollPos.y += ptsScrollLine.y;
  1178.                 break;
  1179.         case SB_SLIDERTRACK:
  1180.             case SB_SLIDERPOSITION:
  1181.         for( fDone = FALSE; !fDone ;)
  1182.         {
  1183.           if( WinPeekMsg( habAsync
  1184.                 , &qmsgPeek
  1185.                 , NULL
  1186.                 , UM_VSCROLL
  1187.                 , UM_VSCROLL
  1188.                 , PM_NOREMOVE))
  1189.               if(   (SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERTRACK)
  1190.               ||(SHORT2FROMMP( qmsgPeek.mp1) == SB_SLIDERPOSITION) )
  1191.               WinPeekMsg( habAsync
  1192.                     , &qmsgAsync
  1193.                     , NULL
  1194.                     , UM_VSCROLL
  1195.                     , UM_VSCROLL
  1196.                     , PM_REMOVE);
  1197.               else
  1198.               fDone = TRUE;
  1199.           else
  1200.               fDone = TRUE;
  1201.         }
  1202.         ptsScrollPos.y = SHORT1FROMMP( qmsgAsync.mp1);
  1203.                 break;
  1204.             case SB_PAGEUP:
  1205.         ptsScrollPos.y -= ptsScrollPage.y;
  1206.                 break;
  1207.             case SB_PAGEDOWN:
  1208.         ptsScrollPos.y += ptsScrollPage.y;
  1209.                 break;
  1210.             case SB_ENDSCROLL:
  1211.                 break;
  1212.             default:
  1213.                 break;
  1214.     }
  1215.     DoVertScroll();
  1216.         break;
  1217.  
  1218.       /************************************************************************/
  1219.       /* recalc the picture transform                                         */
  1220.       /************************************************************************/
  1221.       case UM_SIZING:
  1222.     CalcBounds();
  1223.     CalcTransform( hwndClient);
  1224.         break;
  1225.  
  1226.       /************************************************************************/
  1227.       /* adjust zoom factor                                                   */
  1228.       /************************************************************************/
  1229.       case UM_ZOOM_IN:
  1230.     Zoom( ZOOM_IN_ARG);
  1231.         break;
  1232.  
  1233.       case UM_ZOOM_OUT:
  1234.     Zoom( ZOOM_OUT_ARG);
  1235.         break;
  1236.  
  1237.       /************************************************************************/
  1238.       /* toggle fast-drag                              */
  1239.       /************************************************************************/
  1240.       case UM_FASTDRAG:
  1241.     ToggleFastDrag();
  1242.     break;
  1243.  
  1244.       /************************************************************************/
  1245.       /* Button down will cause a correlate on the picture to test for a hit. */
  1246.       /* Any selected segment will be highlighted and redrawn as dynamic.     */
  1247.       /************************************************************************/
  1248.       case UM_LEFTDOWN:
  1249.     LeftDown( qmsgAsync.mp1);
  1250.         break;
  1251.  
  1252.       /************************************************************************/
  1253.       /* if a segment is being dragged it will be redrawn in a new posn       */
  1254.       /************************************************************************/
  1255.       case UM_MOUSEMOVE:
  1256.     for( fDone = FALSE; !fDone ;)
  1257.     {
  1258.       if( WinPeekMsg( habAsync
  1259.             , &qmsgPeek
  1260.             , NULL
  1261.             , UM_MOUSEMOVE
  1262.             , UM_LEFTUP
  1263.             , PM_NOREMOVE))
  1264.           if( qmsgPeek.msg == UM_MOUSEMOVE)
  1265.           WinPeekMsg( habAsync
  1266.                 , &qmsgAsync
  1267.                 , NULL
  1268.                 , UM_MOUSEMOVE
  1269.                 , UM_MOUSEMOVE
  1270.                 , PM_REMOVE);
  1271.           else
  1272.           fDone = TRUE;
  1273.       else
  1274.           fDone = TRUE;
  1275.     }
  1276.     MouseMove( qmsgAsync.mp1);
  1277.         break;
  1278.  
  1279.       /************************************************************************/
  1280.       /* if a segment is being dragged it will be redrawn as normal          */
  1281.       /************************************************************************/
  1282.       case UM_LEFTUP:
  1283.     LeftUp();
  1284.         break;
  1285.  
  1286.       /************************************************************************/
  1287.       /* destroy resources and terminate                     */
  1288.       /************************************************************************/
  1289.       case UM_DIE:
  1290.     WinDestroyMsgQueue( hmqAsync);
  1291.     WinTerminate( habAsync);
  1292.     DosEnterCritSec();
  1293.     DosSemClear( hsemTerminate);
  1294.         DosExit( EXIT_THREAD, 0);
  1295.         break;
  1296.  
  1297.       /************************************************************************/
  1298.       /* finish flush of commands from queue                      */
  1299.       /************************************************************************/
  1300.       case UM_FLUSH:
  1301.         break;
  1302.  
  1303.       default:
  1304.         break;
  1305.     }
  1306.   }
  1307. }
  1308.  
  1309. /******************************************************************************/
  1310. /* button down will cause one segment to be indicated and made dynamic          */
  1311. /******************************************************************************/
  1312. VOID
  1313. LeftDown( mp)
  1314.  
  1315. MPARAM     mp;
  1316. {
  1317.   HRGN        hrgnUpdt;
  1318.   LONG        alSegTag[HITS][DEPTH][2];
  1319.   POINTL    ptl, aptl[4];
  1320.   RECTL     rcl;
  1321.   MATRIXLF  matlf;
  1322.   LONG        lOffset;
  1323.   BYTE        bBuff[128];
  1324.   CHAR        pszMsg[40];
  1325.   PSZ        psz1, psz2;
  1326.  
  1327.   ptl.x = (LONG)(SHORT)SHORT1FROMMP( mp);
  1328.   ptl.y = (LONG)(SHORT)SHORT2FROMMP( mp);
  1329.  
  1330.   /****************************************************************************/
  1331.   /****************************************************************************/
  1332.   for( pslPicked = pslTail; pslPicked != NULL; pslPicked = pslPicked->pslPrev)
  1333.   {
  1334.     rcl = pslPicked->rclCurrent;
  1335.     GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
  1336.     rcl.xRight++;
  1337.     rcl.yTop++;
  1338.     if( WinPtInRect( habAsync, &rcl, &ptl))
  1339.     {
  1340.     LONG lRet;
  1341.  
  1342.     GpiSetEditMode( hpsClient, SEGEM_INSERT);
  1343.     GpiOpenSegment( hpsClient, pslPicked->lSegId);
  1344.     GpiSetElementPointerAtLabel( hpsClient, FILLPATH);
  1345.     GpiFillPath( hpsClient, 1L, 0L);
  1346.     GpiCloseSegment( hpsClient);
  1347.     lRet = GpiCorrelateSegment( hpsClient
  1348.                   , pslPicked->lSegId
  1349.                   , PICKSEL_VISIBLE
  1350.                   , &ptl
  1351.                   , HITS
  1352.                   , DEPTH
  1353.                   , (PLONG)alSegTag );
  1354.     GpiOpenSegment( hpsClient, pslPicked->lSegId);
  1355.     GpiSetElementPointerAtLabel( hpsClient, FILLPATH);
  1356.     GpiOffsetElementPointer( hpsClient, 1L);
  1357.     GpiDeleteElement( hpsClient);
  1358.     GpiCloseSegment( hpsClient);
  1359.  
  1360.     if( lRet > 0)
  1361.         break;
  1362.     }
  1363.   }
  1364.   if( pslPicked)
  1365.     lPickedSeg     = pslPicked->lSegId;
  1366.   else
  1367.   {
  1368.     fButtonDown = FALSE;
  1369.     return;
  1370.   }
  1371.   if( (lPickedSeg < 1) || (lPickedSeg > lLastSegId) )
  1372.   {
  1373.     DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
  1374.     sprintf( szFmt, "Segment id out of range: %x", lPickedSeg);
  1375.     for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
  1376.     ;
  1377.     DosSemClear( hsemSzFmt);
  1378.     MyMessageBox( hwndClient, pszMsg);
  1379.     fButtonDown = FALSE;
  1380.     return;
  1381.   }
  1382.  
  1383.   /****************************************************************************/
  1384.   hrgnUpdt = GpiCreateRegion( hpsClient, 1L, &rcl);
  1385.   GpiSetSegmentAttrs( hpsClient, lPickedSeg, ATTR_VISIBLE, ATTR_OFF);
  1386.  
  1387.   GpiQuerySegmentTransformMatrix( hpsClient
  1388.                 , lPickedSeg
  1389.                 , 9L
  1390.                 , &matlf );
  1391.   GpiBeginPath( hpsClient, 1L);
  1392.   GpiCallSegmentMatrix( hpsClient
  1393.               , lPickedSeg + CALLSEG_BASE
  1394.               , 9L
  1395.               , &matlf
  1396.               , TRANSFORM_REPLACE );
  1397.   GpiEndPath( hpsClient);
  1398.   GpiSetClipPath( hpsClient, 1L, SCP_AND);
  1399.   DoDraw( hrgnUpdt);
  1400.   GpiSetClipPath( hpsClient, 0L, SCP_RESET);
  1401.   GpiDestroyRegion( hpsClient, hrgnUpdt);
  1402.  
  1403.   /****************************************************************************/
  1404.   ptlOffset = ptlBotLeft;
  1405.   GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 1L, &ptlOffset);
  1406.  
  1407.   aptl[0].x = pslPicked->rclBitBlt.xLeft;
  1408.   aptl[0].y = pslPicked->rclBitBlt.yBottom;
  1409.   aptl[1].x = pslPicked->rclBitBlt.xRight;
  1410.   aptl[1].y = pslPicked->rclBitBlt.yTop;
  1411.   aptl[2] = aptl[0];
  1412.   aptl[3] = aptl[1];
  1413.   GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, &aptl[2]);
  1414.   aptl[2].x -= ptlOffset.x;
  1415.   aptl[2].y -= ptlOffset.y;
  1416.   aptl[3].x -= ptlOffset.x - 1;
  1417.   aptl[3].y -= ptlOffset.y - 1;
  1418.   GpiSetEditMode( hpsClient, SEGEM_INSERT);
  1419.  
  1420.   for( lOffset = 0L; GpiGetData( hpsClient
  1421.                    , lPickedSeg
  1422.                    , &lOffset
  1423.                    , DFORM_NOCONV
  1424.                    , (LONG)sizeof( bBuff)
  1425.                    , bBuff) > 0; )
  1426.       ;
  1427.  
  1428.   GpiOpenSegment( hpsClient, lPickedSeg);
  1429.   GpiDeleteElementsBetweenLabels( hpsClient, BITBLT_TOP, BITBLT_BOTTOM);
  1430.   if( !fFastDrag)
  1431.       GpiWCBitBlt( hpsClient
  1432.          , hbmBitmapDrag
  1433.          , 4L
  1434.          , aptl
  1435.          , ROP_SRCCOPY
  1436.          , BBO_IGNORE );
  1437.   GpiCloseSegment( hpsClient);
  1438.  
  1439.   for( lOffset = 0L; GpiGetData( hpsClient
  1440.                    , lPickedSeg
  1441.                    , &lOffset
  1442.                    , DFORM_NOCONV
  1443.                    , (LONG)sizeof( bBuff)
  1444.                    , bBuff) > 0; )
  1445.       ;
  1446.  
  1447.   /****************************************************************************/
  1448.   GpiSetSegmentAttrs( hpsClient, lPickedSeg, ATTR_VISIBLE, ATTR_ON);
  1449.   GpiSetSegmentAttrs( hpsClient, lPickedSeg, ATTR_DYNAMIC, ATTR_ON);
  1450.   GpiSetDrawControl( hpsClient, DCTL_DYNAMIC, DCTL_ON);
  1451.   GpiDrawSegment( hpsClient, lPickedSeg);
  1452.  
  1453.   WinSetCapture( HWND_DESKTOP, hwndClient);
  1454. }
  1455.  
  1456.  
  1457.  
  1458.  
  1459. /******************************************************************************/
  1460. /*                                                                            */
  1461. /* move the segment                                  */
  1462. /*                                                                            */
  1463. /******************************************************************************/
  1464. VOID
  1465. MouseMove( mp)
  1466.  
  1467. MPARAM     mp;
  1468. {
  1469.   RECTL   rcl;
  1470.   POINTL  ptl, ptlModel;
  1471.  
  1472.   ptl.x = (LONG)(SHORT)SHORT1FROMMP( mp);
  1473.   ptl.y = (LONG)(SHORT)SHORT2FROMMP( mp);
  1474.  
  1475.   ptlModel = ptl;
  1476.   GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptlModel);
  1477.   ptlModel.x = 5 * (ptlModel.x / 5);
  1478.   ptlModel.y = 5 * (ptlModel.y / 5);
  1479.   if( (ptlModel.x == ptlOldMouse.x) && (ptlModel.y == ptlOldMouse.y))
  1480.     return;
  1481.   ptlOldMouse.x = ptlModel.x;
  1482.   ptlOldMouse.y = ptlModel.y;
  1483.  
  1484.   /****************************************************************************/
  1485.   /* clip mouse coords to client window                       */
  1486.   /****************************************************************************/
  1487.   WinQueryWindowRect(hwndClient, &rcl);
  1488.   if (rcl.xLeft > ptl.x)
  1489.     ptl.x = rcl.xLeft;
  1490.   if (rcl.xRight <= ptl.x)
  1491.     ptl.x = rcl.xRight;
  1492.   if (rcl.yBottom > ptl.y)
  1493.     ptl.y = rcl.yBottom;
  1494.   if (rcl.yTop <= ptl.y)
  1495.     ptl.y = rcl.yTop;
  1496.  
  1497.   GpiRemoveDynamics( hpsClient, lPickedSeg, lPickedSeg);
  1498.   Translate( pslPicked, &ptl);
  1499.   GpiDrawDynamics( hpsClient);
  1500. }
  1501.  
  1502.  
  1503. /******************************************************************************/
  1504. /*                                          */
  1505. /* The dragged segment is being unselected.  Return it to its normal state.   */
  1506. /*                                          */
  1507. /******************************************************************************/
  1508. VOID
  1509. LeftUp()
  1510. {
  1511.   SEGLIST    sl;
  1512.   POINTL     aptl[4];
  1513.  
  1514.   if( !lPickedSeg)
  1515.     return;
  1516.   GpiRemoveDynamics( hpsClient, lPickedSeg, lPickedSeg);
  1517.   GpiSetSegmentAttrs( hpsClient, lPickedSeg, ATTR_DYNAMIC, ATTR_OFF);
  1518.  
  1519.   /****************************************************************************/
  1520.   ptlOffset = ptlBotLeft;
  1521.   GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 1L, &ptlOffset);
  1522.  
  1523.   aptl[0].x = pslPicked->rclBitBlt.xLeft;
  1524.   aptl[0].y = pslPicked->rclBitBlt.yBottom;
  1525.   aptl[1].x = pslPicked->rclBitBlt.xRight;
  1526.   aptl[1].y = pslPicked->rclBitBlt.yTop;
  1527.   aptl[2] = aptl[0];
  1528.   aptl[3] = aptl[1];
  1529.   GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, &aptl[2]);
  1530.   aptl[2].x -= ptlOffset.x;
  1531.   aptl[2].y -= ptlOffset.y;
  1532.   aptl[3].x -= ptlOffset.x - 1;
  1533.   aptl[3].y -= ptlOffset.y - 1;
  1534.   GpiSetEditMode( hpsClient, SEGEM_INSERT);
  1535.   GpiOpenSegment( hpsClient, lPickedSeg);
  1536.   GpiDeleteElementsBetweenLabels( hpsClient, BITBLT_TOP, BITBLT_BOTTOM);
  1537.   GpiWCBitBlt( hpsClient
  1538.          , hbmBitmapTemp
  1539.          , 4L
  1540.          , aptl
  1541.          , ROP_SRCCOPY
  1542.          , BBO_IGNORE );
  1543.   GpiCloseSegment( hpsClient);
  1544.  
  1545.   /****************************************************************************/
  1546.   GpiDrawSegment( hpsClient, lPickedSeg);
  1547.   GpiSetSegmentPriority( hpsClient, lPickedSeg, 0L, LOWER_PRI); /* highest    */
  1548.   SetRect( pslPicked);
  1549.  
  1550.   sl = *pslPicked;
  1551.   SegListUpdate( DEL_SEG, pslPicked);
  1552.   SegListUpdate( ADD_TAIL_SEG, &sl);        /* at tail => highest priority    */
  1553.   pslPicked = NULL;
  1554.  
  1555.   WinSetCapture( HWND_DESKTOP, (HWND)NULL);
  1556. }
  1557.  
  1558.  
  1559. /******************************************************************************/
  1560. /*                                                                            */
  1561. /* DoHorzScroll will horizontally scroll the current contents of          */
  1562. /* the client area and redraw the invalidated area                  */
  1563. /*                                                                            */
  1564. /******************************************************************************/
  1565. VOID
  1566. DoHorzScroll()
  1567. {
  1568.   RECTL     rcl;
  1569.   HRGN        hrgn;
  1570.   MATRIXLF  matlf;
  1571.  
  1572.   if( ptsScrollPos.x > ptsScrollMax.x)
  1573.       ptsScrollPos.x = ptsScrollMax.x;
  1574.   if( ptsScrollPos.x < 0)
  1575.       ptsScrollPos.x = 0;
  1576.  
  1577.   if( ptsOldScrollPos.x != ptsScrollPos.x)
  1578.       WinSendMsg( hwndHorzScroll
  1579.         , SBM_SETPOS
  1580.         , MPFROM2SHORT( ptsScrollPos.x, 0)
  1581.         , MPFROMLONG( NULL));
  1582.  
  1583.   /****************************************************************************/
  1584.   /* Scroll the window the reqd amount, using bitblt'ing (ScrollWindow)       */
  1585.   /* if any of the screen still in view, and paint into uncovered region;     */
  1586.   /* else repaint the whole client area.                      */
  1587.   /****************************************************************************/
  1588.   hrgn = GpiCreateRegion( hpsClient, 0L, NULL);
  1589.   if( abs( ptsScrollPos.x - ptsOldScrollPos.x) <= ptsScrollMax.x)
  1590.   {
  1591.       WinScrollWindow( hwndClient
  1592.              , ptsOldScrollPos.x - ptsScrollPos.x
  1593.              , 0
  1594.              , NULL
  1595.              , NULL
  1596.              , hrgn
  1597.              , &rcl
  1598.              , 0);
  1599.   } else
  1600.   {
  1601.       WinQueryWindowRect( hwndClient, &rcl);
  1602.       GpiSetRegion( hpsClient, hrgn, 1L, &rcl);
  1603.   }
  1604.   GpiQueryDefaultViewMatrix( hpsClient, 9L, &matlf );
  1605.   matlf.lM31 -= ptsScrollPos.x - ptsOldScrollPos.x;
  1606.   GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, TRANSFORM_REPLACE);
  1607.  
  1608.   DoDraw( hrgn);
  1609.   ptsOldScrollPos.x = ptsScrollPos.x;
  1610.   GpiDestroyRegion( hpsClient, hrgn);
  1611. }
  1612.  
  1613. /******************************************************************************/
  1614. /*                                                                            */
  1615. /* DoVertScroll will vertically scroll the current contents of              */
  1616. /* the client area and redraw the invalidated area                  */
  1617. /*                                                                            */
  1618. /******************************************************************************/
  1619. VOID
  1620. DoVertScroll()
  1621. {
  1622.   RECTL     rcl;
  1623.   HRGN        hrgn;
  1624.   MATRIXLF  matlf;
  1625.  
  1626.   if( ptsScrollPos.y > ptsScrollMax.y)
  1627.       ptsScrollPos.y = ptsScrollMax.y;
  1628.   if( ptsScrollPos.y < 0)
  1629.       ptsScrollPos.y = 0;
  1630.  
  1631.   if( ptsOldScrollPos.y != ptsScrollPos.y)
  1632.       WinSendMsg( hwndVertScroll
  1633.         , SBM_SETPOS
  1634.         , MPFROM2SHORT( ptsScrollPos.y, 0)
  1635.         , MPFROMLONG( NULL));
  1636.  
  1637.   /****************************************************************************/
  1638.   /* Scroll the window the reqd amount, using bitblt'ing (ScrollWindow)       */
  1639.   /* if any of the screen still in view, and paint into uncovered region;     */
  1640.   /* else repaint the whole client area.                      */
  1641.   /****************************************************************************/
  1642.   hrgn = GpiCreateRegion( hpsClient, 0L, NULL);
  1643.   if( abs( ptsScrollPos.y - ptsOldScrollPos.y) <= ptsScrollMax.y)
  1644.   {
  1645.       WinScrollWindow( hwndClient
  1646.              , 0
  1647.              , ptsScrollPos.y - ptsOldScrollPos.y
  1648.              , NULL
  1649.              , NULL
  1650.              , hrgn
  1651.              , &rcl
  1652.              , 0);
  1653.   } else
  1654.   {
  1655.       WinQueryWindowRect( hwndClient, &rcl);
  1656.       GpiSetRegion( hpsClient, hrgn, 1L, &rcl);
  1657.   }
  1658.   GpiQueryDefaultViewMatrix( hpsClient, 9L, &matlf );
  1659.   matlf.lM32 += ptsScrollPos.y - ptsOldScrollPos.y;
  1660.   GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, TRANSFORM_REPLACE);
  1661.  
  1662.   DoDraw( hrgn);
  1663.   ptsOldScrollPos.y = ptsScrollPos.y;
  1664.   GpiDestroyRegion( hpsClient, hrgn);
  1665. }
  1666.  
  1667. /******************************************************************************/
  1668. /*                                                                            */
  1669. /* Redraw the entire client window.                          */
  1670. /*                                                                            */
  1671. /******************************************************************************/
  1672. VOID
  1673. Redraw()
  1674. {
  1675.   RECTL   rclInvalid;
  1676.   HRGN      hrgnUpdt;
  1677.  
  1678.   WinQueryWindowRect( hwndClient, &rclInvalid);
  1679.   hrgnUpdt = GpiCreateRegion( hpsClient, 1L, &rclInvalid);
  1680.   DoDraw( hrgnUpdt);
  1681.   GpiDestroyRegion( hpsClient, hrgnUpdt);
  1682. }
  1683.  
  1684.  
  1685. /******************************************************************************/
  1686. /*                                                                            */
  1687. /* toggle the fast-drag flag and update the menu check-box              */
  1688. /*                                                                            */
  1689. /******************************************************************************/
  1690. VOID
  1691. ToggleFastDrag()
  1692. {
  1693.   MENUITEM mi;
  1694.   HWND       hwndMenu, hwndOptions;
  1695.  
  1696.   hwndMenu = WinWindowFromID( hwndFrame, FID_MENU);
  1697.   WinSendMsg( hwndMenu
  1698.         , MM_QUERYITEM
  1699.         , MPFROM2SHORT( SM_OPTIONS, FALSE)
  1700.         , MPFROMP( (PMENUITEM)&mi));
  1701.   hwndOptions = mi.hwndSubMenu;
  1702.  
  1703.   if( fFastDrag)
  1704.   {
  1705.     fFastDrag = FALSE;
  1706.     WinSendMsg( hwndOptions
  1707.           , MM_SETITEMATTR
  1708.           , MPFROM2SHORT( MENU_FASTDRAG, TRUE)
  1709.           , MPFROM2SHORT( MIA_CHECKED, ~MIA_CHECKED) );
  1710.   }
  1711.   else
  1712.   {
  1713.     fFastDrag = TRUE;
  1714.     WinSendMsg( hwndOptions
  1715.           , MM_SETITEMATTR
  1716.           , MPFROM2SHORT( MENU_FASTDRAG, TRUE)
  1717.           , MPFROM2SHORT( MIA_CHECKED, MIA_CHECKED) );
  1718.   }
  1719. }
  1720.  
  1721. /******************************************************************************/
  1722. /*                                                                            */
  1723. /* adjust zoom factor and recalc the picture transform, then do a redraw of   */
  1724. /* whole screen                                   */
  1725. /*                                                                            */
  1726. /******************************************************************************/
  1727. VOID
  1728. Zoom( sInOrOut)
  1729.  
  1730. SHORT sInOrOut;
  1731. {
  1732.   LONG     lScaleOld;
  1733.  
  1734.   lScaleOld = lScale;
  1735.   lScale += sInOrOut;
  1736.   if( lScale > ZOOM_MAX)
  1737.     lScale = ZOOM_MAX;
  1738.   else
  1739.     if( lScale < -ZOOM_MAX)
  1740.       lScale = -ZOOM_MAX;
  1741.   if( lScale != lScaleOld)
  1742.   {
  1743.       ZoomMenuItems();
  1744.       CalcBounds();
  1745.       CalcTransform( hwndClient);
  1746.       Redraw();
  1747.   }
  1748. }
  1749.  
  1750. /******************************************************************************/
  1751. /*                                                                            */
  1752. /* enable/disable zoom menu items depending on scaling                        */
  1753. /*                                                                            */
  1754. /******************************************************************************/
  1755. VOID
  1756. ZoomMenuItems()
  1757. {
  1758.   MENUITEM  mi;
  1759.   HWND        hwndMenu, hwndOptions;
  1760.  
  1761.   hwndMenu = WinWindowFromID( hwndFrame, FID_MENU);
  1762.   WinSendMsg( hwndMenu
  1763.         , MM_QUERYITEM
  1764.         , MPFROM2SHORT( SM_OPTIONS, FALSE)
  1765.         , MPFROMP( (PMENUITEM)&mi));
  1766.   hwndOptions = mi.hwndSubMenu;
  1767.  
  1768.   if( lScale >= ZOOM_MAX)
  1769.   {
  1770.       WinSendMsg( hwndOptions
  1771.         , MM_SETITEMATTR
  1772.         , MPFROM2SHORT( MENU_ZOOMIN, TRUE)
  1773.         , MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED));
  1774.       WinSendMsg( hwndOptions
  1775.         , MM_SETITEMATTR
  1776.         , MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
  1777.         , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  1778.   } else
  1779.   {
  1780.       if( lScale <= - ZOOM_MAX)
  1781.       {
  1782.       WinSendMsg( hwndOptions
  1783.             , MM_SETITEMATTR
  1784.             , MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
  1785.             , MPFROM2SHORT( MIA_DISABLED, MIA_DISABLED));
  1786.       WinSendMsg( hwndOptions
  1787.             , MM_SETITEMATTR
  1788.             , MPFROM2SHORT( MENU_ZOOMIN, TRUE)
  1789.             , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  1790.       } else
  1791.       {
  1792.       WinSendMsg( hwndOptions
  1793.             , MM_SETITEMATTR
  1794.             , MPFROM2SHORT( MENU_ZOOMOUT, TRUE)
  1795.             , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  1796.       WinSendMsg( hwndOptions
  1797.             , MM_SETITEMATTR
  1798.             , MPFROM2SHORT( MENU_ZOOMIN, TRUE)
  1799.             , MPFROM2SHORT( MIA_DISABLED, ~MIA_DISABLED));
  1800.       }
  1801.   }
  1802. }
  1803.  
  1804. /******************************************************************************/
  1805. /*                                                                            */
  1806. /* Determine the bounding rect of a segment.                      */
  1807. /*                                                                            */
  1808. /******************************************************************************/
  1809. VOID
  1810. SetRect( psl)
  1811.  
  1812. PSEGLIST  psl;
  1813. {
  1814.   GpiResetBoundaryData( hpsClient);
  1815.   GpiSetDrawControl( hpsClient, DCTL_DISPLAY, DCTL_OFF);
  1816.   GpiSetDrawControl( hpsClient, DCTL_BOUNDARY, DCTL_ON);
  1817.   GpiDrawSegment( hpsClient, psl->lSegId);
  1818.   GpiSetDrawControl( hpsClient, DCTL_DISPLAY, DCTL_ON);
  1819.   GpiSetDrawControl( hpsClient, DCTL_BOUNDARY, DCTL_OFF);
  1820.   GpiQueryBoundaryData( hpsClient, &(psl->rclCurrent));
  1821. }
  1822.  
  1823. /******************************************************************************/
  1824. /*                                                                            */
  1825. /* Translate a segment                                  */
  1826. /*                                                                            */
  1827. /******************************************************************************/
  1828. VOID
  1829. Translate( psl, pptlNew)
  1830.  
  1831. PSEGLIST  psl;
  1832. PPOINTL   pptlNew;
  1833. {
  1834.   POINTL    ptl;
  1835.   MATRIXLF  matlf;
  1836.  
  1837.   ptl = *pptlNew;
  1838.   GpiConvert( hpsClient, CVTC_DEVICE, CVTC_MODEL, 1L, &ptl);
  1839.   ptl.x = (ptl.x / 5) * 5;
  1840.   ptl.y = (ptl.y / 5) * 5;
  1841.   ptl.x -= 25;
  1842.   ptl.y -= 25;
  1843.  
  1844.   GpiQuerySegmentTransformMatrix( hpsClient
  1845.                 , psl->lSegId
  1846.                 , 9L
  1847.                 , &matlf);
  1848.   matlf.lM31 = ptl.x - (psl->ptlLocation).x;
  1849.   matlf.lM32 = ptl.y - (psl->ptlLocation).y;
  1850.   GpiSetSegmentTransformMatrix( hpsClient
  1851.                   , psl->lSegId
  1852.                   , 9L
  1853.                   , &matlf
  1854.                   , TRANSFORM_REPLACE);
  1855. }
  1856.  
  1857.  
  1858. /******************************************************************************/
  1859. /*                                                                            */
  1860. /* set the default viewing transform                          */
  1861. /*                                                                            */
  1862. /******************************************************************************/
  1863. VOID
  1864. SetDVTransform( fx11, fx12, fx21, fx22, l31, l32, lType)
  1865.  
  1866. FIXED    fx11, fx12, fx21, fx22;
  1867. LONG    l31, l32, lType;
  1868. {
  1869.   MATRIXLF  matlf;
  1870.  
  1871.   matlf.fxM11 = fx11;
  1872.   matlf.fxM12 = fx12;
  1873.   matlf.lM13  = 0L;
  1874.   matlf.fxM21 = fx21;
  1875.   matlf.fxM22 = fx22;
  1876.   matlf.lM23  = 0L;
  1877.   matlf.lM31  = l31;
  1878.   matlf.lM32  = l32;
  1879.   matlf.lM33  = 1L;
  1880.   GpiSetDefaultViewMatrix( hpsClient, 9L, &matlf, lType);
  1881. }
  1882.  
  1883. /******************************************************************************/
  1884. /*                                                                            */
  1885. /* get bounding rect of whole picture in model coordinates              */
  1886. /*                                                                            */
  1887. /******************************************************************************/
  1888. VOID
  1889. CalcBounds()
  1890. {
  1891.   PSEGLIST  psl;
  1892.   RECTL     rcl;
  1893.  
  1894.   if( !pslHead)
  1895.     return;
  1896.   rclBounds = pslHead->rclCurrent;
  1897.   for( psl = pslHead->pslNext; psl != NULL; psl = psl->pslNext)
  1898.   {
  1899.     rcl = psl->rclCurrent;
  1900.     if( rcl.xLeft < rclBounds.xLeft)
  1901.       rclBounds.xLeft = rcl.xLeft;
  1902.     if( rcl.xRight > rclBounds.xRight)
  1903.       rclBounds.xRight = rcl.xRight;
  1904.     if( rcl.yTop > rclBounds.yTop)
  1905.       rclBounds.yTop = rcl.yTop;
  1906.     if( rcl.yBottom < rclBounds.yBottom)
  1907.       rclBounds.yBottom = rcl.yBottom;
  1908.   }
  1909. }
  1910.  
  1911. /******************************************************************************/
  1912. /*                                                                            */
  1913. /* Calculate and set the default viewing transform based on zoom and scroll   */
  1914. /*                                                                            */
  1915. /******************************************************************************/
  1916. VOID
  1917. CalcTransform( hwnd)
  1918.  
  1919. HWND hwnd;
  1920. {
  1921.   RECTL     rclClient;
  1922.   POINTL    ptlCenter, ptlTrans, ptlScale, aptl[4];
  1923.   HRGN        hrgn;
  1924.   PSEGLIST  psl;
  1925.  
  1926.   /****************************************************************************/
  1927.   /* from bounding rect of picture get center of picture              */
  1928.   /****************************************************************************/
  1929.   ptlCenter.x = (rclBounds.xLeft   + rclBounds.xRight) / 2;
  1930.   ptlCenter.y = (rclBounds.yBottom + rclBounds.yTop  ) / 2;
  1931.  
  1932.   /****************************************************************************/
  1933.   /* translate center of picture to origin                      */
  1934.   /****************************************************************************/
  1935.   SetDVTransform( (FIXED)UNITY
  1936.         , (FIXED)0
  1937.         , (FIXED)0
  1938.         , (FIXED)UNITY
  1939.         , -ptlCenter.x
  1940.         , -ptlCenter.y
  1941.         , TRANSFORM_REPLACE);
  1942.  
  1943.   /****************************************************************************/
  1944.   /* scale down to 60% of max client area                      */
  1945.   /****************************************************************************/
  1946.   ptlScale.x = (6 * UNITY * sizlMaxClient.cx) /
  1947.            (10 * (ptlTopRight.x - ptlBotLeft.x));
  1948.   ptlScale.y = (6 * UNITY * sizlMaxClient.cy) /
  1949.            (10 * (ptlTopRight.y - ptlBotLeft.y));
  1950.  
  1951.   /****************************************************************************/
  1952.   /* add in zoom scale                                  */
  1953.   /****************************************************************************/
  1954.   ptlScale.x += ptlScale.x * lScale / (ZOOM_MAX + 1);
  1955.   ptlScale.y += ptlScale.y * lScale / (ZOOM_MAX + 1);
  1956.  
  1957.   SetDVTransform( (FIXED)ptlScale.x
  1958.         , (FIXED)0
  1959.         , (FIXED)0
  1960.         , (FIXED)ptlScale.y
  1961.         , 0L
  1962.         , 0L
  1963.         , TRANSFORM_ADD);
  1964.  
  1965.   /****************************************************************************/
  1966.   /* translate center of picture to center of client window              */
  1967.   /****************************************************************************/
  1968.   WinQueryWindowRect( hwnd, &rclClient);
  1969.   ptlTrans.x = (rclClient.xRight - rclClient.xLeft)   / 2;
  1970.   ptlTrans.y = (rclClient.yTop     - rclClient.yBottom) / 2;
  1971.  
  1972.   /****************************************************************************/
  1973.   /* add in horizontal and vertical scrolling factors                  */
  1974.   /****************************************************************************/
  1975.   ptlTrans.x += ptsScrollPos.x - ptsHalfScrollMax.x;
  1976.   ptlTrans.y += ptsScrollPos.y - ptsHalfScrollMax.y;
  1977.   SetDVTransform( (FIXED)UNITY
  1978.         , (FIXED)0
  1979.         , (FIXED)0
  1980.         , (FIXED)UNITY
  1981.         , ptlTrans.x
  1982.         , ptlTrans.y
  1983.         , TRANSFORM_ADD);
  1984.  
  1985.   /****************************************************************************/
  1986.   /* create a shadow bitmap of the original, sized to the current output size */
  1987.   /****************************************************************************/
  1988.   aptl[0] = ptlBotLeft;
  1989.   aptl[1] = ptlTopRight;
  1990.   GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, aptl);
  1991.   ptlOffset = aptl[0];
  1992.  
  1993.   aptl[0].x -= ptlOffset.x;
  1994.   aptl[0].y -= ptlOffset.y;
  1995.   aptl[1].x -= ptlOffset.x - 1;
  1996.   aptl[1].y -= ptlOffset.y - 1;
  1997.   aptl[2].x = 0L;
  1998.   aptl[2].y = 0L;
  1999.   aptl[3].x = bmpBitmapFile.cx;
  2000.   aptl[3].y = bmpBitmapFile.cy;
  2001.   GpiSetBitmap( hpsBitmapTemp, hbmBitmapTemp);
  2002.   GpiBitBlt( hpsBitmapTemp
  2003.        , hpsBitmapFile
  2004.        , 4L
  2005.        , aptl
  2006.        , ROP_SRCCOPY
  2007.        , BBO_IGNORE);
  2008.   GpiSetBitmap( hpsBitmapTemp, NULL);
  2009.  
  2010.   /****************************************************************************/
  2011.   /* create a copy of the shadow bitmap, adjusted to appear normal when       */
  2012.   /* bitblt'd in XOR mode onto a CLR_BACKGROUND background (dynamic segment)  */
  2013.   /****************************************************************************/
  2014.   GpiSetBitmap( hpsBitmapDrag, hbmBitmapDrag);
  2015.   GpiSetColor( hpsBitmapDrag, CLR_BACKGROUND);
  2016.   hrgn = GpiCreateRegion( hpsBitmapDrag, 1L, (PRECTL)aptl);
  2017.   GpiPaintRegion( hpsBitmapDrag, hrgn);
  2018.   GpiDestroyRegion( hpsBitmapDrag, hrgn);
  2019.   GpiBitBlt( hpsBitmapDrag
  2020.        , hpsBitmapFile
  2021.        , 4L
  2022.        , aptl
  2023.        , ROP_SRCINVERT
  2024.        , BBO_IGNORE);
  2025.   GpiSetBitmap( hpsBitmapDrag, NULL);
  2026.  
  2027.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2028.   {
  2029.     aptl[0].x = psl->rclBitBlt.xLeft;
  2030.     aptl[0].y = psl->rclBitBlt.yBottom;
  2031.     aptl[1].x = psl->rclBitBlt.xRight;
  2032.     aptl[1].y = psl->rclBitBlt.yTop;
  2033.     aptl[2] = aptl[0];
  2034.     aptl[3] = aptl[1];
  2035.     GpiConvert( hpsClient, CVTC_WORLD, CVTC_DEVICE, 2L, &aptl[2]);
  2036.     aptl[2].x -= ptlOffset.x;
  2037.     aptl[2].y -= ptlOffset.y;
  2038.     aptl[3].x -= ptlOffset.x - 1;
  2039.     aptl[3].y -= ptlOffset.y - 1;
  2040.     GpiSetEditMode( hpsClient, SEGEM_INSERT);
  2041.     GpiOpenSegment( hpsClient, psl->lSegId);
  2042.     GpiDeleteElementsBetweenLabels( hpsClient, BITBLT_TOP, BITBLT_BOTTOM);
  2043.     GpiWCBitBlt( hpsClient
  2044.            , hbmBitmapTemp
  2045.            , 4L
  2046.            , aptl
  2047.            , ROP_SRCCOPY
  2048.            , BBO_IGNORE );
  2049.     GpiCloseSegment( hpsClient);
  2050.   }
  2051. }
  2052.  
  2053.  
  2054. /******************************************************************************/
  2055. /*                                                                            */
  2056. /* Draw the picture, using the passed region for clipping.              */
  2057. /* Test each segment to see if its bounding box intersects the bounding box   */
  2058. /* of the clipping region.  Draw only if there is an intersection.          */
  2059. /*                                                                            */
  2060. /******************************************************************************/
  2061. BOOL
  2062. DoDraw( hrgn)
  2063.  
  2064. HRGN    hrgn;
  2065. {
  2066.   HRGN        hrgnOld;
  2067.   RECTL     rcl, rclRegion, rclDst;
  2068.   PSEGLIST  psl;
  2069.  
  2070.   GpiSetColor( hpsClient, CLR_BACKGROUND);
  2071.   GpiPaintRegion( hpsClient, hrgn);
  2072.  
  2073.   GpiQueryRegionBox( hpsClient, hrgn, &rclRegion);
  2074.   GpiSetClipRegion( hpsClient, hrgn, &hrgnOld);
  2075.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2076.   {
  2077.     rcl = psl->rclCurrent;
  2078.     GpiConvert( hpsClient, CVTC_MODEL, CVTC_DEVICE, 2L, (PPOINTL)&rcl);
  2079.     rcl.xRight++;
  2080.     rcl.yTop++;
  2081.     if( WinIntersectRect( habAsync, &rclDst, &rcl, &rclRegion))
  2082.     GpiDrawSegment( hpsClient, psl->lSegId);
  2083.   }
  2084.   GpiSetClipRegion( hpsClient, NULL, &hrgnOld);
  2085.  
  2086.   return( TRUE);
  2087. }
  2088.  
  2089. /******************************************************************************/
  2090. /*                                                                            */
  2091. /* Return a pointer to a segment list member, based on segment id.          */
  2092. /*                                                                            */
  2093. /******************************************************************************/
  2094. PSEGLIST
  2095. SegListGet( lSeg)
  2096.  
  2097. LONG       lSeg;
  2098. {
  2099.   PSEGLIST  psl;
  2100.  
  2101.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2102.     if( psl->lSegId == lSeg)
  2103.       return( psl);
  2104.   return( NULL);
  2105. }
  2106.  
  2107. /******************************************************************************/
  2108. /*                                                                            */
  2109. /* Check the segment list for obvious errors.                      */
  2110. /*                                          */
  2111. /******************************************************************************/
  2112. BOOL
  2113. SegListCheck( iLoc)
  2114.  
  2115. INT   iLoc;
  2116. {
  2117.   PSEGLIST   psl;
  2118.   CHAR         pszMsg[50];
  2119.   PSZ         psz1, psz2;
  2120.  
  2121.   pszMsg[0] = '\0';
  2122.   for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2123.     if( (psl->lSegId < 1) || (psl->lSegId > lLastSegId) )
  2124.     {
  2125.       DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
  2126.       sprintf( szFmt, "Bad head segment list, location %d", iLoc);
  2127.       for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
  2128.       ;
  2129.       DosSemClear( hsemSzFmt);
  2130.       MyMessageBox( hwndClient, pszMsg);
  2131.       return( FALSE);
  2132.     }
  2133.   for( psl = pslTail; psl != NULL; psl = psl->pslPrev)
  2134.     if( (psl->lSegId < 1) || (psl->lSegId > lLastSegId) )
  2135.     {
  2136.       DosSemRequest( hsemSzFmt, SEM_INDEFINITE_WAIT);
  2137.       sprintf( szFmt, "Bad head segment list, location %d", iLoc);
  2138.       for( psz1 = szFmt, psz2 = pszMsg; *psz2++ = *psz1++; )
  2139.       ;
  2140.       DosSemClear( hsemSzFmt);
  2141.       MyMessageBox( hwndClient, pszMsg);
  2142.       return( FALSE);
  2143.     }
  2144.   return( TRUE);
  2145. }
  2146. /******************************************************************************/
  2147. /*                                                                            */
  2148. /* Add (at head or tail) or delete a specified segment list member.          */
  2149. /*                                                                            */
  2150. /******************************************************************************/
  2151. BOOL
  2152. SegListUpdate( usOperation, pslUpdate)
  2153.  
  2154. USHORT     usOperation;
  2155. PSEGLIST pslUpdate;
  2156. {
  2157.   PSEGLIST psl;
  2158.   SEL       sel;
  2159.  
  2160.   switch( usOperation)
  2161.   {
  2162.     case ADD_HEAD_SEG:
  2163.       DosAllocSeg( sizeof( SEGLIST), &sel, 0);
  2164.       if( pslHead == NULL)
  2165.       {
  2166.     pslHead = MAKEP( sel, 0);
  2167.     if( pslHead == NULL)
  2168.       return( FALSE);
  2169.     *pslHead = *pslUpdate;
  2170.     pslHead->pslPrev = NULL;
  2171.     pslHead->pslNext = NULL;
  2172.     pslTail = pslHead;
  2173.       } else
  2174.       {
  2175.     psl = MAKEP( sel, 0);
  2176.     if( psl == NULL)
  2177.       return( FALSE);
  2178.     *psl = *pslUpdate;
  2179.     pslHead->pslPrev = psl;
  2180.     psl->pslNext = pslHead;
  2181.     psl->pslPrev = NULL;
  2182.     pslHead = psl;
  2183.       }
  2184.       return( TRUE);
  2185.       break;
  2186.  
  2187.     case ADD_TAIL_SEG:
  2188.       DosAllocSeg( sizeof( SEGLIST), &sel, 0);
  2189.       if( pslTail == NULL)
  2190.       {
  2191.     pslHead = MAKEP( sel, 0);
  2192.     if( pslHead == NULL)
  2193.       return( FALSE);
  2194.     *pslHead = *pslUpdate;
  2195.     pslHead->pslPrev = NULL;
  2196.     pslHead->pslNext = NULL;
  2197.     pslTail = pslHead;
  2198.       } else
  2199.       {
  2200.     psl = MAKEP( sel, 0);
  2201.     if( psl == NULL)
  2202.       return( FALSE);
  2203.     *psl = *pslUpdate;
  2204.     pslTail->pslNext = psl;
  2205.     psl->pslPrev = pslTail;
  2206.     psl->pslNext = NULL;
  2207.     pslTail = psl;
  2208.       }
  2209.       return( TRUE);
  2210.       break;
  2211.  
  2212.     case DEL_SEG:
  2213.       for( psl = pslHead; psl != NULL; psl = psl->pslNext)
  2214.       {
  2215.     if( psl->lSegId == pslUpdate->lSegId)
  2216.     {
  2217.       if( psl == pslHead)
  2218.       {
  2219.         pslHead = psl->pslNext;
  2220.         if( pslHead == NULL)
  2221.           pslTail = NULL;
  2222.         else
  2223.           pslHead->pslPrev = NULL;
  2224.       }else if( psl == pslTail)
  2225.       {
  2226.         pslTail = psl->pslPrev;
  2227.         pslTail->pslNext = NULL;
  2228.       } else
  2229.       {
  2230.         (psl->pslPrev)->pslNext = psl->pslNext;
  2231.         (psl->pslNext)->pslPrev = psl->pslPrev;
  2232.       }
  2233.       DosFreeSeg( SELECTOROF(psl));
  2234.       return( TRUE);
  2235.       break;
  2236.     }
  2237.       }
  2238.       return( FALSE);
  2239.       break;
  2240.  
  2241.     default:
  2242.       return( FALSE);
  2243.   }
  2244. }
  2245.  
  2246.  
  2247.  
  2248. /******************************************************************************/
  2249. /*                                                                            */
  2250. /* DumpPicture will free the list and segment store for the picture          */
  2251. /*                                                                            */
  2252. /******************************************************************************/
  2253. BOOL
  2254. DumpPicture()
  2255. {
  2256.   while( pslHead != NULL )
  2257.     SegListUpdate( DEL_SEG, pslHead);
  2258.   GpiDeleteSegments( hpsClient, 1L, CALLSEG_BASE + lLastSegId);
  2259.   GpiSetBitmap( hpsBitmapFile, NULL);
  2260.   if( hbmBitmapFile)
  2261.       GpiDeleteBitmap( hbmBitmapFile);
  2262.   GpiSetBitmap( hpsBitmapTemp, NULL);
  2263.   if( hbmBitmapTemp)
  2264.       GpiDeleteBitmap( hbmBitmapTemp);
  2265.   GpiSetBitmap( hpsBitmapDrag, NULL);
  2266.   if( hbmBitmapDrag)
  2267.       GpiDeleteBitmap( hbmBitmapDrag);
  2268.  
  2269.   return( TRUE);
  2270. }
  2271.  
  2272. /******************************************************************************/
  2273. /*                                                                            */
  2274. /* Draw the picture into segment store.                       */
  2275. /*                                                                            */
  2276. /******************************************************************************/
  2277. BOOL
  2278. CreatePicture()
  2279. {
  2280.  
  2281.   POINTL    ptl, aptlSides[12], aptlControl[12];
  2282.   SEGLIST   sl;
  2283.   LONG        lCallSegId, l;
  2284.  
  2285.   /****************************************************************************/
  2286.   /* reset the default viewing transform to identity                  */
  2287.   /****************************************************************************/
  2288.   SetDVTransform( (FIXED)UNITY
  2289.         , (FIXED)0
  2290.         , (FIXED)0
  2291.         , (FIXED)UNITY
  2292.         , 0L
  2293.         , 0L
  2294.         , TRANSFORM_REPLACE);
  2295.  
  2296.   /****************************************************************************/
  2297.   /* set to store mode                                  */
  2298.   /****************************************************************************/
  2299.   GpiSetDrawingMode( hpsClient, DM_RETAIN);
  2300.  
  2301.   /****************************************************************************/
  2302.   /* chaining and detectability off, fastchaining off                  */
  2303.   /****************************************************************************/
  2304.   GpiSetInitialSegmentAttrs( hpsClient, ATTR_CHAINED, ATTR_OFF);
  2305.   GpiSetInitialSegmentAttrs( hpsClient, ATTR_DETECTABLE, ATTR_OFF);
  2306.   GpiSetInitialSegmentAttrs( hpsClient, ATTR_FASTCHAIN, ATTR_OFF);
  2307.  
  2308.   /****************************************************************************/
  2309.   /* draw the pieces                                  */
  2310.   /****************************************************************************/
  2311.   lLastSegId = 0;
  2312.   lCallSegId = CALLSEG_BASE;
  2313.   for( ptl.x = ptlBotLeft.x; ptl.x < ptlTopRight.x; ptl.x += 50)
  2314.   {
  2315.     for( ptl.y = ptlBotLeft.y; ptl.y < ptlTopRight.y; ptl.y += 50)
  2316.     {
  2317.       /************************************************************************/
  2318.       /* compute the piece outline control points                  */
  2319.       /************************************************************************/
  2320.       aptlControl[0].x = 10L;
  2321.       aptlControl[0].y = 10L;
  2322.       aptlControl[1].x = 40L;
  2323.       aptlControl[1].y = -10L;
  2324.       aptlControl[2].x = 50L;
  2325.       aptlControl[2].y = 0L;
  2326.  
  2327.       aptlControl[3].x = 40L;
  2328.       aptlControl[3].y = 10L;
  2329.       aptlControl[4].x = 60L;
  2330.       aptlControl[4].y = 40L;
  2331.       aptlControl[5].x = 50L;
  2332.       aptlControl[5].y = 50L;
  2333.  
  2334.       aptlControl[6].x = 40L;
  2335.       aptlControl[6].y = 40L;
  2336.       aptlControl[7].x = 10L;
  2337.       aptlControl[7].y = 60L;
  2338.       aptlControl[8].x = 0L;
  2339.       aptlControl[8].y = 50L;
  2340.  
  2341.       aptlControl[9].x    = 10L;
  2342.       aptlControl[9].y    = 40L;
  2343.       aptlControl[10].x = -10L;
  2344.       aptlControl[10].y = 10L;
  2345.       aptlControl[11].x = 0L;
  2346.       aptlControl[11].y = 0L;
  2347.  
  2348.       if( ptl.y == ptlBotLeft.y)
  2349.       {
  2350.     aptlControl[0].y = 0L;
  2351.     aptlControl[1].y = 0L;
  2352.       }
  2353.  
  2354.       if( (ptl.x + 50) == ptlTopRight.x)
  2355.       {
  2356.     aptlControl[3].x = 50L;
  2357.     aptlControl[4].x = 50L;
  2358.       }
  2359.  
  2360.       if( (ptl.y + 50) == ptlTopRight.y)
  2361.       {
  2362.     aptlControl[6].y = 50L;
  2363.     aptlControl[7].y = 50L;
  2364.       }
  2365.  
  2366.       if( ptl.x == ptlBotLeft.x)
  2367.       {
  2368.     aptlControl[ 9].x = 0L;
  2369.     aptlControl[10].x = 0L;
  2370.       }
  2371.  
  2372.       for( l=0; l<12; l++)
  2373.       {
  2374.     aptlSides[l].x = ptl.x + aptlControl[l].x;
  2375.     aptlSides[l].y = ptl.y + aptlControl[l].y;
  2376.       }
  2377.  
  2378.       GpiOpenSegment( hpsClient, ++lCallSegId);
  2379.       GpiMove( hpsClient, &ptl);
  2380.       GpiPolyLine( hpsClient, 12L, aptlSides);
  2381.       GpiCloseSegment( hpsClient);
  2382.  
  2383.       /************************************************************************/
  2384.       /* draw the root segment                              */
  2385.       /************************************************************************/
  2386.       GpiOpenSegment( hpsClient, ++lLastSegId);
  2387.       GpiSetTag( hpsClient, lLastSegId);
  2388.  
  2389.       /************************************************************************/
  2390.       /* store the piece location                          */
  2391.       /************************************************************************/
  2392.       sl.ptlLocation = ptl;
  2393.  
  2394.       /************************************************************************/
  2395.       /* compute the dimensions of the matching rects for BitBlt          */
  2396.       /************************************************************************/
  2397.       sl.rclBitBlt.xLeft   = ptl.x - 10;
  2398.       sl.rclBitBlt.yBottom = ptl.y - 10;
  2399.       sl.rclBitBlt.xRight  = ptl.x + 60;
  2400.       sl.rclBitBlt.yTop    = ptl.y + 60;
  2401.       if( ptl.x == ptlBotLeft.x)
  2402.     sl.rclBitBlt.xLeft += 10;
  2403.       if( ptl.y == ptlBotLeft.y)
  2404.     sl.rclBitBlt.yBottom += 10;
  2405.       if( (ptl.x + 50) == ptlTopRight.x)
  2406.     sl.rclBitBlt.xRight -= 10;
  2407.       if( (ptl.y + 50) == ptlTopRight.y)
  2408.     sl.rclBitBlt.yTop -= 10;
  2409.  
  2410.       /************************************************************************/
  2411.       /* draw one piece                               */
  2412.       /************************************************************************/
  2413.       GpiBeginPath( hpsClient, 1L);
  2414.       GpiMove( hpsClient, &ptl);
  2415.       GpiPolyLine( hpsClient, 12L, aptlSides);
  2416.       GpiEndPath( hpsClient);
  2417.       GpiSetColor( hpsClient, CLR_BLACK);
  2418.       GpiLabel( hpsClient, FILLPATH);
  2419.  
  2420.       GpiSetClipPath( hpsClient, 0L, SCP_RESET);
  2421.       GpiBeginPath( hpsClient, 1L);
  2422.       GpiMove( hpsClient, &ptl);
  2423.       GpiPolyLine( hpsClient, 12L, aptlSides);
  2424.       GpiEndPath( hpsClient);
  2425.       GpiSetClipPath( hpsClient, 1L, SCP_AND);
  2426.       GpiLabel( hpsClient, BITBLT_TOP);
  2427.       GpiLabel( hpsClient, BITBLT_BOTTOM);
  2428.  
  2429.       GpiSetClipPath( hpsClient, 0L, SCP_RESET);
  2430.       GpiSetColor( hpsClient, CLR_RED);
  2431.       GpiMove( hpsClient, &ptl);
  2432.       GpiPolyLine( hpsClient, 12L, aptlSides);
  2433.  
  2434.       GpiCloseSegment( hpsClient);
  2435.       GpiSetSegmentAttrs( hpsClient, lLastSegId, ATTR_CHAINED, ATTR_ON);
  2436.       GpiSetSegmentAttrs( hpsClient, lLastSegId, ATTR_DETECTABLE, ATTR_ON);
  2437.  
  2438.       sl.lSegId = lLastSegId;
  2439.       sl.pslNext = NULL;
  2440.       sl.pslPrev = NULL;
  2441.       SetRect( &sl);
  2442.       SegListUpdate( ADD_TAIL_SEG, &sl);
  2443.     }
  2444.   }
  2445.   return( TRUE);
  2446. }
  2447.  
  2448. /******************************************************************************/
  2449. /*                                                                            */
  2450. /* Create the Temp and Drag bitmaps.                          */
  2451. /*                                                                            */
  2452. /******************************************************************************/
  2453. BOOL
  2454. PrepareBitmap()
  2455. {
  2456.   bmpBitmapTemp    = bmpBitmapFile;
  2457.   bmpBitmapTemp.cx = LOUSHORT( (sizlMaxClient.cx * 6L) / 5L);
  2458.   bmpBitmapTemp.cy = LOUSHORT( (sizlMaxClient.cy * 6L) / 5L);
  2459.   hbmBitmapTemp    = GpiCreateBitmap( hpsBitmapTemp
  2460.                     , &bmpBitmapTemp
  2461.                     , 0L
  2462.                     , NULL
  2463.                     , NULL);
  2464.   if( !hbmBitmapTemp)
  2465.     return( FALSE);
  2466.  
  2467.   bmpBitmapDrag    = bmpBitmapFile;
  2468.   bmpBitmapDrag.cx = LOUSHORT( (sizlMaxClient.cx * 6L) / 5L);
  2469.   bmpBitmapDrag.cy = LOUSHORT( (sizlMaxClient.cy * 6L) / 5L);
  2470.   hbmBitmapDrag    = GpiCreateBitmap( hpsBitmapDrag
  2471.                     , &bmpBitmapDrag
  2472.                     , 0L
  2473.                     , NULL
  2474.                     , NULL);
  2475.   if( !hbmBitmapDrag)
  2476.     return( FALSE);
  2477.   return( TRUE);
  2478. }
  2479.  
  2480. /******************************************************************************/
  2481. /*                                                                            */
  2482. /* Create a memory DC and an associated PS.                      */
  2483. /*                                                                            */
  2484. /******************************************************************************/
  2485. BOOL
  2486. CreateBitmapHdcHps( phdc, phps)
  2487.  
  2488. PHDC  phdc;
  2489. PHPS  phps;
  2490. {
  2491.   SIZEL    sizl;
  2492.   HDC       hdc;
  2493.   HPS       hps;
  2494.  
  2495.   hdc = DevOpenDC( habMain, OD_MEMORY, "*", 3L, (PDEVOPENDATA)&dop, NULL);
  2496.   if( !hdc)
  2497.     return( FALSE);
  2498.  
  2499.   sizl.cx = sizl.cy = 0L;
  2500.   hps = GpiCreatePS( habMain
  2501.            , hdc
  2502.            , &sizl
  2503.            , PU_PELS | GPIA_ASSOC );
  2504.   if( !hps)
  2505.     return( FALSE);
  2506.  
  2507.   *phdc = hdc;
  2508.   *phps = hps;
  2509.   return( TRUE);
  2510. }
  2511.  
  2512. /******************************************************************************/
  2513. /*                                          */
  2514. /* Get the bitmap from disk.                              */
  2515. /* Note that there are 2 formats for bitmap files, one of which is archaic.   */
  2516. /* Both formats are supported here.  All new bitmaps should follow the format */
  2517. /* in BITMAPFILEHEADER.                               */
  2518. /*                                          */
  2519. /******************************************************************************/
  2520. BOOL
  2521. ReadBitmap( hfile)
  2522.  
  2523. HFILE  hfile;
  2524. {
  2525.     ULONG cScans;
  2526.     ULONG ulSize;     /* Number of bytes occupied by bitmap bits.          */
  2527.     USHORT cSegs;     /* Number of 64K segments in ulSize.              */
  2528.     USHORT cbExtra;     /* Bytes in last segment of ulSize.              */
  2529.     SEL sel;         /* Base selector to file data.               */
  2530.     USHORT hugeshift;     /* Segment index shift value.                  */
  2531.     USHORT cbRead1;     /* Number of bytes to read first call to DosRead     */
  2532.     USHORT cbRead2;     /* Number of bytes to read second call to DosRead    */
  2533.     USHORT cbRead;     /* Number of bytes read by DosRead.              */
  2534.     BOOL fRet = FALSE;     /* Function return code.                  */
  2535.     USHORT  i;         /* Generic loop index.                   */
  2536.     FILESTATUS fsts;
  2537.     PBITMAPFILEHEADER pbfh;
  2538.     PRCBITMAP  rb;
  2539.     PBYTE pImage;
  2540.  
  2541.  
  2542.     /**************************************************************************/
  2543.     /* Find out how big the file is so we can read the whole thing in.          */
  2544.     /**************************************************************************/
  2545.  
  2546.     if( DosQFileInfo( hfile, 1, &fsts, sizeof(FILESTATUS)) != 0)
  2547.     goto ReadBitmap_close_file;
  2548.  
  2549.     ulSize  = fsts.cbFile;
  2550.     cSegs   = (USHORT)(ulSize/0x10000L);
  2551.     cbExtra = (USHORT)(ulSize%0x10000L);
  2552.     if (DosAllocHuge(cSegs, cbExtra, (PSEL)&sel, 0, 0))
  2553.     goto ReadBitmap_close_file;
  2554.     if (DosGetHugeShift( &hugeshift))
  2555.     goto ReadBitmap_free_bits;
  2556.  
  2557.     pImage = (PBYTE)MAKEP(sel, 0);
  2558.     rb       = (PRCBITMAP)pImage;
  2559.     pbfh   = (PBITMAPFILEHEADER)pImage;
  2560.  
  2561.  
  2562.     /**************************************************************************/
  2563.     /* Read the bits in from the file. The DosRead function allows a          */
  2564.     /* maximum of 64K-1 bytes read at a time.  We get around this          */
  2565.     /* by reading two 32K chunks for each 64K segment, and reading the          */
  2566.     /* last segment in one piece.                          */
  2567.     /**************************************************************************/
  2568.  
  2569.     for (i = 0; i <= cSegs; ++i)
  2570.     {
  2571.     if (i < cSegs)
  2572.     {
  2573.         /* This segment is 64K bytes long, so split it up. */
  2574.         cbRead1 = 0x8000;
  2575.         cbRead2 = 0x8000;
  2576.     }
  2577.     else
  2578.     {
  2579.         /* This segment is less than 64K bytes long, so read it all. */
  2580.         cbRead1 = cbExtra;
  2581.         cbRead2 = 0;
  2582.     }
  2583.  
  2584.     /* There's a possibility that cbExtra will be 0, so check
  2585.      * to avoid an unnecessary system call.
  2586.      */
  2587.     if (cbRead1 > 0)
  2588.     {
  2589.         if (DosRead( hfile
  2590.                , (PVOID)MAKEP(sel+(i<<hugeshift), 0)
  2591.                , cbRead1
  2592.                , &cbRead))
  2593.         goto ReadBitmap_free_bits;
  2594.         if (cbRead1 != cbRead)
  2595.         goto ReadBitmap_free_bits;
  2596.     }
  2597.  
  2598.     /* This will always be skipped on the last partial segment. */
  2599.     if (cbRead2 > 0)
  2600.     {
  2601.         if (DosRead( hfile
  2602.                , (PVOID)MAKEP(sel+(i<<hugeshift), cbRead1)
  2603.                , cbRead2
  2604.                , &cbRead))
  2605.         goto ReadBitmap_free_bits;
  2606.         if (cbRead2 != cbRead)
  2607.         goto ReadBitmap_free_bits;
  2608.     }
  2609.     }
  2610.  
  2611.  
  2612.     /**************************************************************************/
  2613.     /* Tell GPI to put the bits into the thread's PS. The function returns    */
  2614.     /* the number of scan lines of the bitmap that were copied.  We want      */
  2615.     /* all of them at once.                              */
  2616.     /**************************************************************************/
  2617.  
  2618.     if (pbfh->bmp.cbFix != sizeof(BITMAPINFOHEADER))
  2619.     {
  2620.     bmpBitmapFile.cx    = rb->bmWidth;
  2621.     bmpBitmapFile.cy    = rb->bmHeight;
  2622.     bmpBitmapFile.cPlanes    = rb->bmPlanes;
  2623.     bmpBitmapFile.cBitCount = rb->bmBitcount;
  2624.     hbmBitmapFile = GpiCreateBitmap( hpsBitmapFile
  2625.                        , &bmpBitmapFile
  2626.                        , 0L
  2627.                        , NULL
  2628.                        , NULL);
  2629.     if( !hbmBitmapFile)
  2630.         goto ReadBitmap_free_bits;
  2631.     GpiSetBitmap( hpsBitmapFile, hbmBitmapFile);
  2632.  
  2633.         pImage += rb->dwBitsOffset;
  2634.         rb->dwBitsOffset = sizeof(BITMAPINFOHEADER);
  2635.     cScans = GpiSetBitmapBits( hpsBitmapFile
  2636.                  , 0L
  2637.                  , (LONG)rb->bmHeight
  2638.                  , pImage
  2639.                  , (PBITMAPINFO)&(rb->dwBitsOffset));
  2640.     if (cScans != (ULONG)rb->bmHeight)  /* original number of scans ? */
  2641.         goto ReadBitmap_free_bits;
  2642.     }
  2643.     else
  2644.     {
  2645.     bmpBitmapFile.cx    = pbfh->bmp.cx;
  2646.     bmpBitmapFile.cy    = pbfh->bmp.cy;
  2647.     bmpBitmapFile.cPlanes    = pbfh->bmp.cPlanes;
  2648.     bmpBitmapFile.cBitCount = pbfh->bmp.cBitCount;
  2649.     hbmBitmapFile = GpiCreateBitmap( hpsBitmapFile
  2650.                        , &bmpBitmapFile
  2651.                        , 0L
  2652.                        , NULL
  2653.                        , NULL);
  2654.     if( !hbmBitmapFile)
  2655.         goto ReadBitmap_free_bits;
  2656.     GpiSetBitmap( hpsBitmapFile, hbmBitmapFile);
  2657.  
  2658.     cScans = GpiSetBitmapBits( hpsBitmapFile
  2659.                  , 0L
  2660.                  , (LONG)pbfh->bmp.cy
  2661.                  , pImage + pbfh->offBits
  2662.                  , (PBITMAPINFO)&(pbfh->bmp));
  2663.     if (cScans != (ULONG)pbfh->bmp.cy)  /* original number of scans ? */
  2664.         goto ReadBitmap_free_bits;
  2665.     }
  2666.  
  2667.     fRet = TRUE;     /* okey-dokey */
  2668.  
  2669.  
  2670.     /**************************************************************************/
  2671.     /* Close the file, free the buffer space and leave.  This is a          */
  2672.     /* common exit point from the function.  Since the same cleanup          */
  2673.     /* operations need to be performed for such a large number of          */
  2674.     /* possible error conditions, this is concise way to do the right          */
  2675.     /* thing.                                      */
  2676.     /**************************************************************************/
  2677.  
  2678. ReadBitmap_free_bits:
  2679.     DosFreeSeg( sel);
  2680. ReadBitmap_close_file:
  2681.     DosClose( hfile);
  2682.     return fRet;
  2683. }
  2684.