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