Annotation of pmsdk/samples/jigsaw/jigsaw.c, revision 1.1

1.1     ! root        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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.