home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / GDIINPUT.PAK / CLIENT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  34.0 KB  |  1,242 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1993-1995  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE: client.c
  9. //
  10. //  PURPOSE: Contains all input/drawing routines for the GDI Input sample.
  11. //
  12. //  FUNCTIONS:
  13. //    CreateClientWindow  - Creates the client window that handles input.
  14. //    ClientWndProc       - Processes messages for the client window.
  15. //    CmdDrawMode         - Changes the current drawing mode.
  16. //    CmdFill             - Toggles the fill mode.
  17. //    CmdCreatePen        - Puts up Pen Style dialog box.
  18. //    CmdCreateBrush      - Puts up Brush Style dialog box.
  19. //    MsgClientCreate     - Creates initial pens & brushes.
  20. //    MsgClientDestroy    - Frees owned objects.
  21. //    MsgClientKeyDown    - Looks for Escape key to cancel drawing.
  22. //    MsgClientPaint      - Paints the client window.
  23. //    MsgClientSize       - 
  24. //
  25. //    MsgClientLButtonDown
  26. //    MsgClientMouseMove => Pass mouse messages to the current handlers.
  27. //    MsgClientLButtonUp
  28. //
  29. //    PixelLBDown
  30. //    PixelMouseMove    ==> Draw individual pixels with SetPixelV.
  31. //    PixelLBUp
  32. //
  33. //    BezierLBDown
  34. //    BezierMouseMove   ==> Select 4 points for drawing a Bezier curve.
  35. //    BezierLBUp
  36. //
  37. //    RectLBDown
  38. //    RectMouseMove     ==> Select 2 points for a line, rect, or ellipse.
  39. //    RectLBUp
  40. //
  41. //    LineDraw              Given 2 points, draw a line, rect, or ellipse.
  42. //    RectDraw          ==> These functions all take identical parameters
  43. //    EllipseDraw           so they can be used interchangeably.
  44. //
  45. //    StartRubberBand     - Initiates drawing procedure.
  46. //    EndRubberBand       - Ends drawing procedure.
  47. //    ClientNewDrawing    - Erases current drawing and starts over.
  48. //
  49. //  COMMENTS:
  50. //
  51.  
  52. #include <windows.h>            // required for all Windows applications
  53. #include <windowsx.h>
  54. #include <commctrl.h>           // prototypes and defs for common controls
  55. #include "globals.h"            // prototypes specific to this application
  56. #include "resource.h"
  57. #include "toolbar.h"            // prototypes for the tool bar
  58. #include "statbar.h"            // prototypes for the status bar
  59. #include "pendlg.h"             // ID's and prototypes for the Pen dialog
  60. #include "brushdlg.h"           // ID's and prototypes for the Brush dialog
  61.  
  62. // Global variables local to this file
  63.  
  64. HPEN    hpenBlack;              // Useful stock objects
  65. HBRUSH  hbrNull;
  66.  
  67. HPEN    hpenDraw;               // for drawing lines and frames
  68. HBRUSH  hbrReal;                // for filling interiors
  69. HBRUSH  hbrDraw;                // equals either hbrReal or hbrNull
  70.  
  71. LOGPEN  logPen;
  72. LOGBRUSH logBrush;
  73.  
  74. HDC     hdcRB;                  // DC for Rubber Banding
  75. HDC     hdcDraw;                // DC for final drawing
  76. BOOL    bDrawing;               // Flag to indicate when we are drawing
  77. UINT    uDrawMode;              // IDM_LINE, IDM_RECT, etc.
  78.  
  79. HDC     hdcBitmap;              // Used with hbmBitmap
  80. HBITMAP hbmBitmap;              // All drawing is cached here for faster screen updates
  81. UINT    cxBitmap;               // Width of bitmap
  82. UINT    cyBitmap;               // Height of bitmap
  83. HBITMAP hbmStock;               // Stock 1x1 bitmap that mem DC's are initialized with
  84.  
  85. #define BEZ_MAXPOINTS   4       // # of points in a Bezier curve
  86. POINT   pPoints[BEZ_MAXPOINTS]; // Stores coordinates for various drawing fns
  87. UINT    cPoints;                // Current # of points (for bezier)
  88.  
  89.  
  90. // The following 3 function pointers are used to point to the current
  91. // mouse handling functons.  Different drawing types can each define a
  92. // set of mouse functions.  When a new drawing mode is selected, these
  93. // pointers are set to point to the correct functions for that mode.
  94. // The main mouse message handlers then dereference these pointers to
  95. // call the correct handlers.
  96.  
  97. LRESULT  (*pfnLBDown)   (HWND, POINT);      // current mouse handler
  98. LRESULT  (*pfnLBUp)     (HWND, POINT);      // functions for rubber
  99. LRESULT  (*pfnMouseMove)(HWND, POINT);      // banding and such
  100.  
  101.  
  102. // The Line, Rectangle, and Ellipse modes use a common set of mouse
  103. // handlers that select 2 points.  The following function pointer is
  104. // used so that the correct drawing function is always used.  Like the
  105. // mouse function pointers, this is set when the drawing mode is
  106. // selected.
  107.  
  108. BOOL (*pfnDrawRect)(HDC, POINT, POINT);     // current drawing function
  109.  
  110.  
  111. // Mouse handling functions for different types of drawing.
  112. LRESULT  PixelLBDown(HWND, POINT);          // Pixels
  113. LRESULT  PixelLBUp(HWND, POINT);
  114. LRESULT  PixelMouseMove(HWND, POINT);
  115.  
  116. LRESULT  BezierLBDown(HWND, POINT);         // Bezier Curves
  117. LRESULT  BezierLBUp(HWND, POINT);
  118. LRESULT  BezierMouseMove(HWND, POINT);
  119.  
  120. LRESULT  RectLBDown(HWND, POINT);           // 2-point selection
  121. LRESULT  RectLBUp(HWND, POINT);             // Used for Lines, Rects,
  122. LRESULT  RectMouseMove(HWND, POINT);        // and Ellipses
  123.  
  124. // Generic drawing functions for use with the above Rect functions
  125. BOOL     LineDraw(HDC, POINT, POINT);
  126. BOOL     RectDraw(HDC, POINT, POINT);
  127. BOOL     EllipseDraw(HDC, POINT, POINT);
  128.  
  129.  
  130. // Other helper functions
  131. void StartRubberBand(HWND hwnd);
  132. void EndRubberBand(HWND hwnd);
  133.  
  134.  
  135. // Client window message handling functions
  136. static LRESULT MsgClientCreate      (HWND, UINT, WPARAM, LPARAM);
  137. static LRESULT MsgClientDestroy     (HWND, UINT, WPARAM, LPARAM);
  138. static LRESULT MsgClientMouseMove   (HWND, UINT, WPARAM, LPARAM);
  139. static LRESULT MsgClientLButtonDown (HWND, UINT, WPARAM, LPARAM);
  140. static LRESULT MsgClientLButtonUp   (HWND, UINT, WPARAM, LPARAM);
  141. static LRESULT MsgClientKeyDown     (HWND, UINT, WPARAM, LPARAM);
  142. static LRESULT MsgClientPaint       (HWND, UINT, WPARAM, LPARAM);
  143. static LRESULT MsgClientSize        (HWND, UINT, WPARAM, LPARAM);
  144.  
  145.  
  146. // Client window message table definitions.
  147. MSD rgmsdClient[] =
  148. {
  149.     {WM_MOUSEMOVE,      MsgClientMouseMove    },
  150.     {WM_LBUTTONDOWN,    MsgClientLButtonDown  },
  151.     {WM_LBUTTONUP,      MsgClientLButtonUp    },
  152.     {WM_KEYDOWN,        MsgClientKeyDown      },
  153.     {WM_CREATE,         MsgClientCreate       },
  154.     {WM_DESTROY,        MsgClientDestroy      },
  155.     {WM_PAINT,          MsgClientPaint        },
  156.     {WM_SIZE,           MsgClientSize         },
  157. };
  158.  
  159.  
  160. MSDI msdiClient =
  161. {
  162.     sizeof(rgmsdClient) / sizeof(MSD),
  163.     rgmsdClient,
  164.     edwpWindow
  165. };
  166.  
  167.  
  168. //
  169. //  FUNCTION: CreateClientWindow(HWND)
  170. //
  171. //  PURPOSE: Create the client window.
  172. //
  173. //  PARAMETERS:
  174. //    hwndParent - The parent (main) window.
  175. //
  176. //  RETURN VALUE:
  177. //    HWND of client window or null if failure.
  178. //
  179. //  COMMENTS:
  180. //
  181.  
  182. HWND CreateClientWindow(HWND hwndParent)
  183. {
  184.     return CreateWindowEx(0, 
  185.                           "ClientWndClass",
  186.                           NULL,
  187.                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
  188.                           -100, -100, 10, 10,   // dummy initial coordinates
  189.                           hwndParent, 
  190.                           (HMENU)-1, 
  191.                           hInst, 
  192.                           NULL);
  193. }
  194.  
  195.  
  196. //
  197. //  FUNCTION: ClientWndProc(HWND, UINT, WPARAM, LPARAM)
  198. //
  199. //  PURPOSE:  Processes messages for the client window.
  200. //
  201. //  PARAMETERS:
  202. //    hwnd     - window handle
  203. //    uMessage - message number
  204. //    wparam   - additional information (dependant on message number)
  205. //    lparam   - additional information (dependant on message number)
  206. //
  207. //  RETURN VALUE:
  208. //    The return value depends on the message number.  If the message
  209. //    is implemented in the message dispatch table, the return value is
  210. //    the value returned by the message handling function.  Otherwise,
  211. //    the return value is the value returned by the default window procedure.
  212. //
  213. //  COMMENTS:
  214. //    Call the DispMessage() function with the client window's message
  215. //    dispatch information (msdiClient) and the message specific information.
  216. //
  217.  
  218. LRESULT CALLBACK ClientWndProc(HWND   hwnd, 
  219.                                UINT   uMessage, 
  220.                                WPARAM wparam, 
  221.                                LPARAM lparam)
  222. {
  223.     return DispMessage(&msdiClient, hwnd, uMessage, wparam, lparam);
  224. }
  225.  
  226.  
  227. //
  228. //  FUNCTION: CmdDrawMode(HWND, WORD, WORD, HWND)
  229. //
  230. //  PURPOSE: Handles WM_COMMAND messages to change drawing mode.
  231. //
  232. //  PARAMETERS:
  233. //    hwnd - The main window.
  234. //    wCommand - IDM_LINE, IDM_RECT, etc.
  235. //    wNotify  - Notification number (unused)
  236. //    hwndCtrl - NULL (unused)
  237. //
  238. //  RETURN VALUE:
  239. //    Always returns 0 - command handled.
  240. //
  241. //  COMMENTS:
  242. //
  243.  
  244. #pragma argsused
  245. LRESULT CmdDrawMode(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  246. {
  247.     HMENU hMenu;
  248.  
  249.     if (wCommand == uDrawMode)  // First see if the mode has changed
  250.         return 0;
  251.  
  252.     // Update the menu and toolbar states
  253.  
  254.     hMenu = GetMenu(hwnd);
  255.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_UNCHECKED);
  256.     uDrawMode = wCommand;
  257.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_CHECKED);
  258.  
  259.     SendMessage(hWndToolbar, TB_CHECKBUTTON, uDrawMode, MAKELONG(TRUE, 0));
  260.  
  261.     // Set the pointers for the mouse handler functions to
  262.     // the appropriate functions.
  263.  
  264.     switch(uDrawMode)
  265.     {
  266.         case IDM_PIXEL:
  267.             pfnLBDown     = PixelLBDown;
  268.             pfnLBUp       = PixelLBUp;
  269.             pfnMouseMove  = PixelMouseMove;
  270.             break;
  271.  
  272.         case IDM_BEZIER:
  273.             cPoints       = 0;
  274.             pfnLBDown     = BezierLBDown;
  275.             pfnLBUp       = BezierLBUp;
  276.             pfnMouseMove  = BezierMouseMove;
  277.             break;
  278.  
  279.         case IDM_RECT:
  280.             pfnDrawRect   = RectDraw;
  281.             pfnLBDown     = RectLBDown;
  282.             pfnLBUp       = RectLBUp;
  283.             pfnMouseMove  = RectMouseMove;
  284.             break;
  285.  
  286.         case IDM_ELLIPSE:
  287.             pfnDrawRect   = EllipseDraw;
  288.             pfnLBDown     = RectLBDown;
  289.             pfnLBUp       = RectLBUp;
  290.             pfnMouseMove  = RectMouseMove;
  291.             break;
  292.  
  293.         default:
  294.         case IDM_LINE:
  295.             pfnDrawRect   = LineDraw;
  296.             pfnLBDown     = RectLBDown;
  297.             pfnLBUp       = RectLBUp;
  298.             pfnMouseMove  = RectMouseMove;
  299.             break;
  300.     }
  301.  
  302.     return 0;
  303. }
  304.  
  305.  
  306. //
  307. //  FUNCTION: CmdFill(HWND, WORD, WORD, HWND)
  308. //
  309. //  PURPOSE: Handles the IDM_FILL and IDM_NOFILL command messages.
  310. //    Controls whether closed objects (rect's and ellipses) are filled
  311. //    with a brush when drawn.
  312. //
  313. //  PARAMETERS:
  314. //    hwnd     - The main window.
  315. //    wCommand - IDM_FILL or IDM_NOFILL
  316. //    wNotify  - Notification number (unused)
  317. //    hwndCtrl - NULL (unused)
  318. //
  319. //  RETURN VALUE:
  320. //    Always returns 0 - command handled.
  321. //
  322. //  COMMENTS:
  323. //    The toolbar has 2 buttons (IDM_FILL and IDM_NOFILL) but the
  324. //    menu only has IDM_FILL, so this function will turn filling
  325. //    OFF if wCommand is IDM_NOFILL, and TOGGLE the ON/OFF state
  326. //    if wCommand is IDM_FILL.
  327. //
  328.  
  329. #pragma argsused
  330. LRESULT CmdFill(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  331. {
  332.     static BOOL bFill;
  333.  
  334.     if (IDM_NOFILL == wCommand)
  335.         bFill = FALSE;                          // turn filling OFF
  336.     else
  337.         bFill = !bFill;                         // toggle fill state
  338.  
  339.     // Update the menu and toolbar states
  340.  
  341.      CheckMenuItem(GetMenu(hwnd),
  342.                   IDM_FILL,
  343.                   MF_BYCOMMAND | (bFill ? MF_CHECKED : MF_UNCHECKED));
  344.  
  345.     SendMessage(hWndToolbar,
  346.                 TB_CHECKBUTTON,
  347.                 (bFill ? IDM_FILL : IDM_NOFILL),
  348.                 MAKELONG(TRUE, 0));
  349.  
  350.     // Update hbrDraw and select it into the DC for drawing
  351.     hbrDraw = (bFill ? hbrReal : hbrNull);
  352.     SelectObject(hdcBitmap, hbrDraw);
  353.  
  354.      return 0;
  355. }
  356.  
  357.  
  358. //
  359. //  FUNCTION: CmdCreatePen(HWND, WORD, WORD, HWND)
  360. //
  361. //  PURPOSE: Handles the IDM_CREATEPEN command message. Puts up a dialog
  362. //    to let the user set the pen style to use for drawing.
  363. //
  364. //  PARAMETERS:
  365. //    hwnd     - The main window.
  366. //    wCommand - IDM_CREATEPEN (unused)
  367. //    wNotify  - Notification number (unused)
  368. //    hwndCtrl - NULL (unused)
  369. //
  370. //  RETURN VALUE:
  371. //    Always returns 0 - command handled.
  372. //
  373. //  COMMENTS:
  374. //
  375.  
  376. #pragma argsused
  377. LRESULT CmdCreatePen(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  378. {
  379.     LOGPEN lp = logPen;     // copy for the dialog to munge on
  380.  
  381.     if (DialogBoxParam(hInst, "PenDlg", hwnd, (DLGPROC)PenDlg, (LPARAM)&lp))
  382.     {
  383.         HPEN hpenNew = CreatePenIndirect(&lp);      // create new pen
  384.           if (!hpenNew)
  385.         {
  386.             // Should report an error here...
  387.             return 0;
  388.         }
  389.  
  390.         // Select the new pen for drawing (deselect old pen before deleting)
  391.         SelectObject(hdcBitmap, hpenNew);
  392.  
  393.         DeleteObject(hpenDraw);                     // delete old pen
  394.         hpenDraw = hpenNew;                         // save changes
  395.         logPen = lp;
  396.     }
  397.  
  398.     return 0;
  399. }
  400.  
  401.  
  402. //
  403. //  FUNCTION: CmdCreateBrush(HWND, WORD, WORD, HWND)
  404. //
  405. //  PURPOSE: Handles the IDM_CREATEBRUSH command message. Puts up a dialog
  406. //    to let the user set the brush style to use for drawing.
  407. //
  408. //  PARAMETERS:
  409. //    hwnd - The main window.
  410. //    wCommand - IDM_CREATEBRUSH (unused)
  411. //    wNotify  - Notification number (unused)
  412. //    hwndCtrl - NULL (unused)
  413. //
  414. //  RETURN VALUE:
  415. //    Always returns 0 - command handled.
  416. //
  417. //  COMMENTS:
  418. //
  419.  
  420. #pragma argsused
  421. LRESULT CmdCreateBrush(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  422. {
  423.     LOGBRUSH lb = logBrush; // copy for the dialog to munge on
  424.  
  425.     if (DialogBoxParam(hInst, "BrushDlg", hwnd, (DLGPROC)BrushDlg, (LPARAM)&lb))
  426.     {
  427.         HBRUSH hbrNew = CreateBrushIndirect(&lb);   // create new brush
  428.         if (!hbrNew)
  429.         {
  430.             // Should report an error here...
  431.                 return 0;
  432.         }
  433.  
  434.         // If "Fill Objects" is turned on, we need to update hbrDraw
  435.         if (GetMenuState(GetMenu(hwnd), IDM_FILL, MF_BYCOMMAND) & MF_CHECKED)
  436.         {
  437.             hbrDraw = hbrNew;                       // save new handle
  438.             SelectObject(hdcBitmap, hbrDraw);       // select new brush
  439.         }                                           // (deselect old brush)
  440.  
  441.         DeleteObject(hbrReal);                      // delete old brush
  442.         hbrReal = hbrNew;                           // save changes
  443.         logBrush = lb;
  444.      }
  445.  
  446.     return 0;
  447. }
  448.  
  449.  
  450. //
  451. //  FUNCTION: MsgClientCreate(HWND, UINT, WPARAM, LPARAM)
  452. //
  453. //  PURPOSE: Handles the WM_CREATE message for the client window.
  454. //
  455. //  PARAMETERS:
  456. //
  457. //    hwnd      - Window handle  (Unused)
  458. //    uMessage  - Message number (Unused)
  459. //    wparam    - Extra data     (Unused)
  460. //    lparam    - Extra data     (Unused)
  461. //
  462. //  RETURN VALUE:
  463. //
  464. //  COMMENTS:
  465. //    Initializes pens, brushes, and the off-screen bitmap.
  466. //
  467.  
  468. #pragma argsused
  469. LRESULT MsgClientCreate(HWND   hwnd,
  470.                         UINT   uMessage, 
  471.                         WPARAM wparam, 
  472.                         LPARAM lparam)
  473. {
  474.     HDC hdcTmp;
  475.     RECT rc;
  476.  
  477.     // Initialize pens and brushes
  478.  
  479.     hpenBlack = GetStockObject(BLACK_PEN);
  480.      hbrNull   = GetStockObject(NULL_BRUSH);
  481.  
  482.     hpenDraw  = hpenBlack;
  483.     hbrReal   = GetStockObject(GRAY_BRUSH);
  484.  
  485.     hbrDraw = hbrReal;
  486.  
  487.     GetObject(hpenDraw, sizeof(logPen), &logPen);
  488.     GetObject(hbrReal, sizeof(logBrush), &logBrush);
  489.  
  490.     // Create off-screen bitmap and DC
  491.  
  492.     hdcTmp = GetDC(hwnd);   // Screen DC for reference
  493.  
  494.     GetClientRect(hwnd, &rc);
  495.  
  496.     cxBitmap = max(rc.right, 1);
  497.     cyBitmap = max(rc.bottom, 1);
  498.     hbmBitmap = CreateCompatibleBitmap(hdcTmp, cxBitmap, cyBitmap);
  499.  
  500.     if (!hbmBitmap)
  501.     {
  502.         DeleteObject(hpenDraw);
  503.         DeleteObject(hbrReal);
  504.  
  505.         OutputDebugString("MsgClientCreate: Unable to create bitmap\r\n");
  506.           return -1;
  507.     }
  508.  
  509.     // Create memory DC to hold bitmap
  510.     hdcBitmap = CreateCompatibleDC(hdcTmp);
  511.     ReleaseDC(hwnd, hdcTmp);  // Done with this
  512.  
  513.     // Initialize memory DC
  514.     SelectObject(hdcBitmap, hpenDraw);
  515.     SelectObject(hdcBitmap, hbrDraw);
  516.     SetBkMode(hdcBitmap, TRANSPARENT);
  517.     hbmStock = SelectObject(hdcBitmap, hbmBitmap);
  518.  
  519.      // Initialize bitmap to white
  520.     PatBlt(hdcBitmap, 0, 0, cxBitmap, cyBitmap, WHITENESS);
  521.  
  522.     return 0;
  523. }
  524.  
  525.  
  526. //
  527. //  FUNCTION: MsgClientDestroy(HWND, UINT, WPARAM, LPARAM)
  528. //
  529. //  PURPOSE: Handles WM_DESTROY message for the client window.
  530. //
  531. //  PARAMETERS:
  532. //
  533. //    hwnd      - Window handle  (Unused)
  534. //    uMessage  - Message number (Unused)
  535. //    wparam    - Extra data     (Unused)
  536. //    lparam    - Extra data     (Unused)
  537. //
  538. //  RETURN VALUE:
  539. //
  540. //    Always returns 0 - Message handled
  541. //
  542. //  COMMENTS:
  543. //    General clean up happens here.
  544. //
  545.  
  546. #pragma argsused
  547. LRESULT MsgClientDestroy(HWND   hwnd,
  548.                          UINT   uMessage, 
  549.                          WPARAM wparam, 
  550.                          LPARAM lparam)
  551. {
  552.     // Reset bitmap DC before deleting objects
  553.  
  554.     SelectObject(hdcBitmap, hpenBlack);
  555.     SelectObject(hdcBitmap, hbrNull);
  556.     SelectObject(hdcBitmap, hbmStock);
  557.  
  558.     DeleteObject(hpenDraw);   // Delete pens/brushes
  559.     DeleteObject(hbrReal);
  560.  
  561.     DeleteObject(hbmBitmap);  // Delete bitmap and memory DC
  562.     DeleteDC(hdcBitmap);
  563.  
  564.     return 0;
  565. }
  566.  
  567.  
  568. //
  569. //  FUNCTION: MsgClientKeyDown(HWND, UINT, WPARAM, LPARAM)
  570. //
  571. //  PURPOSE: Handles WM_KEYDOWN message.
  572. //
  573. //  PARAMETERS:
  574. //
  575. //    hwnd      - Window handle
  576. //    uMessage  - Message number (Unused)
  577. //    wparam    - Key code (VK_xxx)
  578. //    lparam    - Extra data     (Unused)
  579. //
  580. //  RETURN VALUE:
  581. //
  582. //  COMMENTS:
  583. //    Looks for VK_ESCAPE key only.  If user hits the escape key
  584. //    while drawing, cancel the drawing operation.
  585. //
  586.  
  587. #pragma argsused
  588. LRESULT MsgClientKeyDown(HWND   hwnd,
  589.                          UINT   uMessage,
  590.                          WPARAM wparam,
  591.                          LPARAM lparam)
  592. {
  593.     if (VK_ESCAPE == wparam && bDrawing)
  594.     {
  595.         // End drawing.
  596.         EndRubberBand(hwnd);
  597.         cPoints = 0;
  598.  
  599.           // Force a repaint of the whole window to remove any junk
  600.         // left by the cancelled drawing operation.
  601.         InvalidateRect(hwnd, NULL, TRUE);
  602.     }
  603.  
  604.     return 0;
  605. }
  606.  
  607.  
  608. //
  609. //  FUNCTION: MsgClientPaint(HWND, UINT, WPARAM, LPARAM)
  610. //
  611. //  PURPOSE: Handles WM_PAINT message.
  612. //
  613. //  PARAMETERS:
  614. //
  615. //    hwnd      - Window handle
  616. //    uMessage  - Message number (Unused)
  617. //    wparam    - Extra data     (Unused)
  618. //    lparam    - Extra data     (Unused)
  619. //
  620. //  RETURN VALUE:
  621. //
  622. //  COMMENTS:
  623. //    Redraws the screen from the bitmap
  624. //
  625.  
  626. #pragma argsused
  627. LRESULT MsgClientPaint(HWND   hwnd,
  628.                               UINT   uMessage,
  629.                        WPARAM wparam,
  630.                        LPARAM lparam)
  631. {
  632.     PAINTSTRUCT ps;
  633.  
  634.     BeginPaint(hwnd, &ps);
  635.  
  636.     BitBlt(ps.hdc,                              // Destination DC
  637.            ps.rcPaint.left,                     // Dest origin
  638.            ps.rcPaint.top,
  639.            ps.rcPaint.right - ps.rcPaint.left,  // Dest extents
  640.            ps.rcPaint.bottom - ps.rcPaint.top,
  641.               hdcBitmap,                           // Source DC
  642.            ps.rcPaint.left,                     // Source origin
  643.            ps.rcPaint.top,
  644.            SRCCOPY);                            // ROP code
  645.  
  646.     EndPaint(hwnd, &ps);
  647.     return 0;
  648. }
  649.  
  650.  
  651. //
  652. //  FUNCTION: MsgClientSize(HWND, UINT, WPARAM, LPARAM)
  653. //
  654. //  PURPOSE: Handles the WM_SIZE message for the client window.
  655. //    size of the window.
  656. //
  657. //  PARAMETERS:
  658. //    hwnd - handle of Client window.
  659. //    uMessage - WM_SIZE.
  660. //    wParam - size flag (unused)
  661. //    lParam - width/height of client area (unused)
  662. //
  663. //  RETURN VALUE:
  664. //    0
  665. //
  666. //  COMMENTS:
  667. //    If the window has been enlarged, this will enlarge the bitmap to
  668. //    match the new window size.  The bitmap is never made smaller.
  669. //
  670.  
  671. #pragma argsused
  672. LRESULT MsgClientSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  673. {
  674.      HDC hdcTmp;
  675.      HBITMAP hbmNew;
  676.     UINT cxNew, cyNew;
  677.     UINT cxWindow = LOWORD(lparam);
  678.     UINT cyWindow = HIWORD(lparam);
  679.  
  680.  
  681.     // First see if there's anything to do...
  682.     if (cxBitmap >= cxWindow && cyBitmap >= cyWindow)
  683.         return 0;
  684.  
  685.     // Get new bitmap dimensions
  686.     cxNew = max(cxWindow, cxBitmap);
  687.     cyNew = max(cyWindow, cyBitmap);
  688.  
  689.     // Use a screen DC to create the new bitmap so we get the
  690.     // correct color format.
  691.     hdcTmp = GetDC(hwnd);
  692.     hbmNew = CreateCompatibleBitmap(hdcTmp, cxNew, cyNew);
  693.     ReleaseDC(hwnd, hdcTmp);
  694.  
  695.     // If we couldn't create a new bitmap, just bail out now and leave
  696.     // the old bitmap intact (that's better than having nothing).
  697.     if (hbmNew == NULL)
  698.         return 0;
  699.  
  700.     // Now we need a memory DC to copy old bitmap to new one.
  701.      hdcTmp = CreateCompatibleDC(NULL);
  702.  
  703.     // Select new bitmap into permanent DC (this deselects the old bitmap)
  704.     // and then select the old bitmap into temporary DC.
  705.     SelectObject(hdcBitmap, hbmNew);
  706.     SelectObject(hdcTmp, hbmBitmap);
  707.  
  708.     // Initialize the 'extra' parts of the new bitmap with white
  709.  
  710.     if (cxWindow > cxBitmap)
  711.         PatBlt(hdcBitmap, cxBitmap, 0, cxWindow, cyWindow, WHITENESS);
  712.  
  713.     if (cyWindow > cyBitmap)
  714.           PatBlt(hdcBitmap, 0, cyBitmap, cxWindow, cyWindow, WHITENESS);
  715.  
  716.     // Copy old bitmap to new one
  717.     BitBlt(hdcBitmap,
  718.            0, 0,
  719.            cxBitmap, cyBitmap,
  720.            hdcTmp,
  721.            0, 0,
  722.            SRCCOPY);
  723.  
  724.     // Clean up
  725.     SelectObject(hdcTmp, hbmStock);
  726.     DeleteObject(hbmBitmap);
  727.      DeleteDC(hdcTmp);
  728.  
  729.     // Save new info
  730.     hbmBitmap = hbmNew;
  731.     cxBitmap = cxNew;
  732.     cyBitmap = cyNew;
  733.  
  734.     return 0;
  735. }
  736.  
  737.  
  738. //
  739. //  FUNCTION: MsgClientLButtonDown(HWND, UINT, WPARAM, LPARAM)
  740. //
  741. //  PURPOSE: Handles WM_LBUTTONDOWN message.
  742. //
  743. //  PARAMETERS:
  744. //
  745. //    hwnd      - Window handle
  746. //    uMessage  - Message number (Unused)
  747. //    wparam    - Extra data     (Unused)
  748. //    lparam    - Mouse coordinates
  749. //
  750. //  RETURN VALUE:
  751. //
  752. //  COMMENTS:
  753. //    Initiates dragging/drawing operation by saving the mouse
  754. //    position and capturing mouse input.
  755. //
  756.  
  757. #pragma argsused
  758. LRESULT MsgClientLButtonDown(HWND   hwnd,
  759.                              UINT   uMessage,
  760.                              WPARAM wparam,
  761.                              LPARAM lparam)
  762. {
  763.     POINT pt;
  764.  
  765.     // If we aren't already drawing, set focus to the client window
  766.     // so we can get Escape key messages should the user decide to 
  767.     // quit drawing.
  768.  
  769.      if (!bDrawing)
  770.         SetFocus(hwnd);
  771.  
  772.     // Get mouse position
  773.     pt.x = (int)(short)LOWORD(lparam);
  774.     pt.y = (int)(short)HIWORD(lparam);
  775.  
  776.     // Call the current LBDown handler
  777.     return (*pfnLBDown)(hwnd, pt);
  778. }
  779.  
  780.  
  781. //
  782. //  FUNCTION: MsgClientMouseMove(HWND, UINT, WPARAM, LPARAM)
  783. //
  784. //  PURPOSE: Handles WM_MOUSEMOVE message.
  785. //
  786. //  PARAMETERS:
  787. //
  788. //    hwnd      - Window handle
  789. //    uMessage  - Message number (Unused)
  790. //    wparam    - Extra data     (Unused)
  791. //    lparam    - Mouse coordinates
  792. //
  793. //  RETURN VALUE:
  794. //
  795. //  COMMENTS:
  796. //    Performs "rubber-banding" by erasing the previous image and
  797. //    redrawing the object using an XOR ROP code.
  798. //
  799.  
  800. #pragma argsused
  801. LRESULT MsgClientMouseMove(HWND   hwnd,
  802.                            UINT   uMessage,
  803.                            WPARAM wparam,
  804.                            LPARAM lparam)
  805. {
  806.     POINT pt;
  807.     static char szBuf[20] ;     // Array for formatting mouse coordinates
  808.  
  809.      // Get new mouse position
  810.     pt.x = (int)(short)LOWORD(lparam);
  811.     pt.y = (int)(short)HIWORD(lparam);
  812.  
  813.     // Update the status bar with the new position
  814.     wsprintf(szBuf, "%d,%d", pt.x, pt.y);
  815.     UpdateStatusBar(szBuf, 2, 0);
  816.  
  817.     // Call the current MouseMove handler
  818.     return (*pfnMouseMove)(hwnd, pt);
  819. }
  820.  
  821.  
  822. //
  823. //  FUNCTION: MsgClientLButtonUp(HWND, UINT, WPARAM, LPARAM)
  824. //
  825. //  PURPOSE: Handles WM_LBUTTONUP message.
  826. //
  827. //  PARAMETERS:
  828. //
  829. //    hwnd      - Window handle
  830. //    uMessage  - Message number (Unused)
  831. //    wparam    - Extra data     (Unused)
  832. //    lparam    - Mouse coordinates
  833. //
  834. //  RETURN VALUE:
  835. //
  836. //  COMMENTS:
  837. //    Erases previous rubber-band image and draws the object in
  838. //      the final position.
  839. //
  840.  
  841. #pragma argsused
  842. LRESULT MsgClientLButtonUp(HWND   hwnd,
  843.                                     UINT   uMessage,
  844.                                     WPARAM wparam,
  845.                            LPARAM lparam)
  846. {
  847.     POINT pt;
  848.  
  849.     // Get new mouse position
  850.     pt.x = (int)(short)LOWORD(lparam);
  851.     pt.y = (int)(short)HIWORD(lparam);
  852.  
  853.     // Call the current LBUp handler
  854.     return (*pfnLBUp)(hwnd, pt);
  855. }
  856.  
  857.  
  858. //
  859. //  FUNCTION: xxxLBDown(HWND, UINT, WPARAM, LPARAM)
  860. //  FUNCTION: xxxMouseMove(HWND, UINT, WPARAM, LPARAM)
  861. //  FUNCTION: xxxLBUp(HWND, UINT, WPARAM, LPARAM)
  862. //
  863. //  PURPOSE: Handles WM_LBUTTONDOWN, WM_MOUSEMOVE, and WM_LBUTTONUP
  864. //
  865. //  PARAMETERS:
  866. //
  867. //    hwnd - Window handle
  868. //    pt   - Mouse coordinates
  869. //
  870. //  RETURN VALUE:
  871. //    Always return 0
  872. //
  873. //  COMMENTS:
  874. //    These functions are called by the actual Msgxxx mouse message
  875. //    handler functions (above) to perform input processing.  Three
  876. //    sets of these functions are implemented here.  One to input
  877. //    individual pixels; one to select 2 points (used to draw a
  878. //    line, rectangle, or ellipse); and one to select 4 points (for
  879. //    drawing Bezier curves).  Additional functions could be "plugged
  880. //    in" to support other types of drawing.
  881. //
  882. //    In general, xxxLBDown initializes the drawing process, for example
  883. //    by saving the initial coordinates and calling StartRubberBand;
  884. //    xxxMouseMove updates the coordinates; and xxxLBUp performs any
  885. //    final processing and clean up (EndRubberBand).
  886. //
  887.  
  888. // 
  889. //  FUNCTION: PixelLBDown(HWND, UINT, WPARAM, LPARAM)
  890. //  FUNCTION: PixelMouseMove(HWND, UINT, WPARAM, LPARAM)
  891. //  FUNCTION: PixelLBUp(HWND, UINT, WPARAM, LPARAM)
  892. //
  893. //  PURPOSE: Perform single point selection (individual pixels).
  894. //
  895.  
  896. LRESULT PixelLBDown(HWND hwnd, POINT pt)
  897. {
  898.     // Get a DC and capture mouse input
  899.     StartRubberBand(hwnd);
  900.     SetROP2(hdcRB, R2_COPYPEN);
  901.  
  902.     // Draw the pixel with the current pen color into both the
  903.     // rubber-banding DC and the bitmap DC.
  904.     SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  905.     SetPixelV(hdcBitmap, pt.x, pt.y, logPen.lopnColor);
  906.  
  907.     return 0;
  908. }
  909.  
  910.  
  911. #pragma argsused
  912. LRESULT PixelMouseMove(HWND hwnd, POINT pt)
  913. {
  914.     // If we are currently drawing, draw the pixel
  915.     if (bDrawing)
  916.     {
  917.         SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  918.         SetPixelV(hdcBitmap, pt.x, pt.y, logPen.lopnColor);
  919.     }
  920.  
  921.      return 0;
  922. }
  923.  
  924.  
  925. #pragma argsused
  926. LRESULT PixelLBUp(HWND hwnd, POINT pt)
  927. {
  928.     // Don't draw the pixel again here (it's already drawn at LBDown
  929.     // and MouseMove time).  Just need to clean up.
  930.  
  931.     if (bDrawing)
  932.         EndRubberBand(hwnd);
  933.  
  934.     return 0;
  935. }
  936.  
  937.  
  938. // 
  939. //  FUNCTION: BezierLBDown(HWND, UINT, WPARAM, LPARAM)
  940. //  FUNCTION: BezierMouseMove(HWND, UINT, WPARAM, LPARAM)
  941. //  FUNCTION: BezierLBUp(HWND, UINT, WPARAM, LPARAM)
  942. //
  943. //  PURPOSE: Perform 4-point selection for drawing Bezier curves.
  944. //
  945.  
  946. #pragma argsused
  947. LRESULT BezierLBDown(HWND hwnd, POINT pt)
  948. {
  949.      // If this is the first click, initialize rubber banding.
  950.  
  951.      if (0 == cPoints)
  952.           StartRubberBand(hwnd);
  953.  
  954.      return 0;
  955. }
  956.  
  957.  
  958. #pragma argsused
  959. LRESULT BezierMouseMove(HWND hwnd, POINT pt)
  960. {
  961.     int i;
  962.  
  963.     if (!bDrawing || 0 == cPoints)              // Are we currently drawing?
  964.         return 0;
  965.  
  966.     // Are we rubber banding a Bezier?
  967.     if (cPoints >= 2)
  968.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS); // Erase previous Bezier
  969.  
  970.      // Erase previous line segment
  971.     LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  972.  
  973.     // Set the rest of the points to be the same as this one
  974.     for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  975.         pPoints[i] = pt;
  976.  
  977.     // Draw new line segment
  978.     LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  979.  
  980.     // Are we rubber banding a Bezier?
  981.     if (cPoints >= 2)
  982.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  983.  
  984.     return 0;
  985. }
  986.  
  987.  
  988. LRESULT BezierLBUp(HWND hwnd, POINT pt)
  989. {
  990.     int i;
  991.  
  992.     if (!bDrawing)          // Are we currently drawing?
  993.         return 0;
  994.  
  995.     if (cPoints >= 2)       // Are we rubber banding a Bezier?
  996.     {
  997.         // Erase previous Bezier
  998.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  999.  
  1000.         if (cPoints == BEZ_MAXPOINTS - 1) // Is this the last point?
  1001.         {
  1002.             // If so, then erase all line segments.
  1003.             for (i = BEZ_MAXPOINTS - 1; i > 0; i--)
  1004.                 LineDraw(hdcRB, pPoints[i - 1], pPoints[i]);
  1005.         }
  1006.     }
  1007.     // else do *not* erase the previous line segments
  1008.  
  1009.     // Save new position and increment # of points
  1010.     pPoints[cPoints++] = pt;
  1011.  
  1012.     if (cPoints < BEZ_MAXPOINTS)    // Still more points to get.
  1013.     {
  1014.         // Set the rest of the points to be the same as the current one
  1015.         for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  1016.             pPoints[i] = pt;
  1017.  
  1018.         // Draw new line segment
  1019.         LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1020.  
  1021.         // Once we have at least 2 points let's draw the Bezier curve
  1022.         if (cPoints >= 2)
  1023.             PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1024.     }
  1025.     else    // Finish this Bezier and quit drawing.
  1026.     {
  1027.         // Setup DC for 'real' drawing
  1028.         SetROP2(hdcRB, R2_COPYPEN);
  1029.         SetBkMode(hdcRB, TRANSPARENT);
  1030.         SelectObject(hdcRB, hpenDraw);
  1031.  
  1032.         // Draw Bezier in permanent position
  1033.         PolyBezier(hdcRB, pPoints, cPoints);
  1034.  
  1035.         // Draw it again into the bitmap DC
  1036.         PolyBezier(hdcBitmap, pPoints, cPoints);
  1037.  
  1038.         // De-select the pen and clean up rubber banding stuff.
  1039.         SelectObject(hdcRB, hpenBlack);
  1040.         EndRubberBand(hwnd);
  1041.         cPoints = 0;
  1042.     }
  1043.  
  1044.     return 0;
  1045. }
  1046.  
  1047.  
  1048. // 
  1049. //  FUNCTION: RectLBDown(HWND, UINT, WPARAM, LPARAM)
  1050. //  FUNCTION: RectMouseMove(HWND, UINT, WPARAM, LPARAM)
  1051. //  FUNCTION: RectLBUp(HWND, UINT, WPARAM, LPARAM)
  1052. //
  1053. //  PURPOSE: Perform 2-point selection for drawing lines, rectangles,
  1054. //    and ellipses.
  1055. //
  1056.  
  1057. LRESULT RectLBDown(HWND hwnd, POINT pt)
  1058. {
  1059.     // Current position = starting position = mouse position
  1060.     pPoints[0] = pPoints[1] = pt;
  1061.  
  1062.     // Initialize rubber banding
  1063.     StartRubberBand(hwnd);
  1064.  
  1065.     // Draw initial state
  1066.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1067.  
  1068.     return 0;
  1069. }
  1070.  
  1071.  
  1072. #pragma argsused
  1073. LRESULT RectMouseMove(HWND hwnd, POINT pt)
  1074. {
  1075.     if (!bDrawing)          // Are we currently drawing?
  1076.         return 0;
  1077.  
  1078.     // Un-draw previous position by redrawing
  1079.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1080.  
  1081.     // Save new position
  1082.     pPoints[1] = pt;
  1083.  
  1084.     // Draw in new position
  1085.      (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1086.  
  1087.     return 0;
  1088. }
  1089.  
  1090.  
  1091. LRESULT RectLBUp(HWND hwnd, POINT pt)
  1092. {
  1093.     if (!bDrawing)          // Are we currently drawing?
  1094.         return 0;
  1095.  
  1096.     // Un-draw previous position by redrawing
  1097.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1098.  
  1099.     // Save new position
  1100.     pPoints[1] = pt;
  1101.  
  1102.     // Setup DC for 'real' drawing
  1103.     SetROP2(hdcRB, R2_COPYPEN);
  1104.     SetBkMode(hdcRB, TRANSPARENT);
  1105.     SelectObject(hdcRB, hpenDraw);
  1106.     SelectObject(hdcRB, hbrDraw);
  1107.  
  1108.     // Draw object in permanent position
  1109.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1110.  
  1111.     // Draw it again into the bitmap DC
  1112.     (*pfnDrawRect)(hdcBitmap, pPoints[0], pPoints[1]);
  1113.  
  1114.     // De-select the pen and brush by selecting stock objects 
  1115.     // and clean up rubber banding stuff.
  1116.     SelectObject(hdcRB, hpenBlack);
  1117.     SelectObject(hdcRB, hbrNull);
  1118.     EndRubberBand(hwnd);
  1119.  
  1120.     return 0;
  1121. }
  1122.  
  1123.  
  1124. //
  1125. //  FUNCTION: xxxxDraw(HDC hdc, POINT pt1, POINT pt2)
  1126. //
  1127. //  PURPOSE: Draws a Line, Rectangle, or Ellipse at the coordinates
  1128. //    given by pt1 and pt2.
  1129. //
  1130. //  PARAMETERS:
  1131. //    hdc   - Device context to draw into.
  1132. //    pt1   - Beginning point (upper-left corner of rect)
  1133. //    pt2   - Ending point    (lower-right corner of rect)
  1134. //
  1135. //  RETURN VALUE:
  1136. //    TRUE for success, FALSE otherwise.
  1137. //
  1138. //  COMMENTS:
  1139. //    Assumes the DC is already correctly set up.
  1140. //
  1141. //
  1142.  
  1143. BOOL LineDraw(HDC hdc, POINT pt1, POINT pt2)
  1144. {
  1145.     MoveToEx(hdc, pt1.x, pt1.y, NULL);
  1146.     return LineTo(hdc, pt2.x, pt2.y);
  1147. }
  1148.  
  1149.  
  1150. BOOL RectDraw(HDC hdc, POINT pt1, POINT pt2)
  1151. {
  1152.     return Rectangle(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1153. }
  1154.  
  1155.  
  1156. BOOL EllipseDraw(HDC hdc, POINT pt1, POINT pt2)
  1157. {
  1158.     return Ellipse(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1159. }
  1160.  
  1161.  
  1162. //
  1163. //  FUNCTION: StartRubberBand(HWND)
  1164. //
  1165. //  PURPOSE: Sets up DC for rubber banding and captures the mouse.
  1166. //
  1167. //  PARAMETERS:
  1168. //    hwnd - Window for GetDC
  1169. //
  1170. //  RETURN VALUE:
  1171. //    None
  1172. //
  1173. //  COMMENTS:
  1174. //
  1175.  
  1176. void StartRubberBand(HWND hwnd)
  1177. {
  1178.     hdcRB = GetDC(hwnd);                        // Get a DC to draw to
  1179.  
  1180.     // R2_NOT causes drawing with a pen to invert the screen pixels.
  1181.     // This makes it easy to erase something by drawing it a 2nd time.
  1182.     SetROP2(hdcRB, R2_NOT);
  1183.  
  1184.     // For rubber banding with R2_NOT, we use a single pixel pen and
  1185.     // a NULL brush.
  1186.     SelectObject(hdcRB, hpenBlack);
  1187.     SelectObject(hdcRB, hbrNull);
  1188.  
  1189.     SetCapture(hwnd);                           // Capture mouse input
  1190.     bDrawing = TRUE;
  1191. }
  1192.  
  1193.  
  1194. //
  1195. //  FUNCTION: EndRubberBand(HWND)
  1196. //
  1197. //  PURPOSE: Releases rubber banding DC and mouse capture.
  1198. //
  1199. //  PARAMETERS:
  1200. //    hwnd - Window for ReleaseDC
  1201. //
  1202. //  RETURN VALUE:
  1203. //    None
  1204. //
  1205. //  COMMENTS:
  1206. //
  1207.  
  1208. void EndRubberBand(HWND hwnd)
  1209. {
  1210.     ReleaseDC(hwnd, hdcRB);
  1211.     ReleaseCapture();
  1212.     bDrawing = FALSE;
  1213. }
  1214.  
  1215.  
  1216. //
  1217. //  FUNCTION: ClientNewDrawing(VOID)
  1218. //
  1219. //  PURPOSE: Erase drawing and start over
  1220. //
  1221. //  PARAMETERS:
  1222. //    None
  1223. //
  1224. //  RETURN VALUE:
  1225. //    None
  1226. //
  1227. //  COMMENTS:
  1228. //
  1229.  
  1230. VOID ClientNewDrawing(VOID)
  1231. {
  1232.     if (hWndClient && hdcBitmap)
  1233.     {
  1234.         HCURSOR hcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));
  1235.  
  1236.         PatBlt(hdcBitmap, 0, 0, cxBitmap, cyBitmap, WHITENESS);
  1237.         InvalidateRect(hWndClient, NULL, TRUE);
  1238.  
  1239.         SetCursor(hcurSave);
  1240.     }
  1241. }
  1242.