home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / GDIPRINT.PAK / CLIENT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  44.6 KB  |  1,578 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: Handles general 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. //    CmdClear            - Forces client window to repaint.
  20. //    InitBitmap          - Initialize the bitmap surface and paint the display.
  21. //    MsgClientCreate     - Creates initial pen and brush objects.  
  22. //    InitDrawObjects     - Initializes the pen and brush used for drawing.
  23. //    MsgClientDestroy    - Frees owned objects.
  24. //    MsgClientKeyDown    - Looks for Escape key to cancel drawing.
  25. //
  26. //    MsgClientLButtonDown
  27. //    MsgClientMouseMove => Pass mouse messages to the current handlers.
  28. //    MsgClientLButtonUp
  29. //
  30. //    PixelLBDown
  31. //    PixelMouseMove    ==> Draw individual pixels with SetPixelV.
  32. //    PixelLBUp
  33. //
  34. //    BezierLBDown
  35. //    BezierMouseMove   ==> Select 4 points for drawing a Bezier curve.
  36. //    BezierLBUp
  37. //
  38. //    RectLBDown
  39. //    RectMouseMove     ==> Select 2 points for a line, rect, or ellipse.
  40. //    RectLBUp
  41. //
  42. //    LineDraw              Given 2 points, draw a line, rect, or ellipse.
  43. //    RectDraw          ==> These functions all take identical parameters
  44. //    EllipseDraw           so they can be used interchangeably.
  45. //
  46. //    StartRubberBand     - Initiates drawing procedure.
  47. //    EndRubberBand       - Ends drawing procedure.
  48. //
  49. //    SizeClientWindow    - Creates scrollbars if client can't display entire bitmap.                                                         
  50. //    MsgClientPaint      - Paints the client window in response to WM_PAINT message.
  51. //    MsgClientScroll     - Scrolls the client window in response to scroll message.
  52. //
  53. //  COMMENTS:
  54. //
  55.  
  56. #include <windows.h>            // required for all Windows applications
  57. #include <windowsx.h>
  58. #include  <commctrl.h>          // prototypes and structure for the common controls.
  59.  
  60. #include "globals.h"            // prototypes specific to this application
  61. #include "toolbar.h"            // prototypes for the tool bar
  62. #include "statbar.h"            // prototypes for the status bar
  63. #include "pendlg.h"             // ID's and prototypes for the Pen dialog
  64. #include "brushdlg.h"           // ID's and prototypes for the Brush dialog
  65. #include "dibutil.h"
  66. #include "resource.h"
  67.  
  68. // Global variables local to this file
  69.  
  70. HPEN    hpenBlack;              // Useful stock objects
  71. HBRUSH  hbrNull;
  72.  
  73. HPEN    hpenDraw;               // for drawing lines and frames
  74. HBRUSH  hbrReal;                // for filling interiors
  75. HBRUSH  hbrDraw;                // equals either hbrReal or hbrNull
  76.  
  77. HDC     hdcRB;                  // DC for Rubber Banding
  78. BOOL    bDrawing;               // Flag to indicate when we are drawing
  79. UINT    uDrawMode;              // IDM_LINE, IDM_RECT, etc.
  80.  
  81. #define BEZ_MAXPOINTS   4       // # of points in a Bezier curve
  82. POINT   pPoints[BEZ_MAXPOINTS]; // Stores coordinates for various drawing fns
  83. UINT    cPoints;                // Current # of points
  84.  
  85. // for working with bitmap in memory DC
  86. HDC     hdcMem;
  87. HBITMAP hbmOld;
  88. HPALETTE hpalRB, hpalMem;
  89.  
  90. // for scrolling
  91. int cxHorzScrollPos = 0;
  92. int cyVertScrollPos = 0;
  93.  
  94. // The following 3 function pointers are used to point to the current
  95. // mouse handling functons.  Different drawing types can each define a
  96. // set of mouse functions.  When a new drawing mode is selected, these
  97. // pointers are set to point to the correct functions for that mode.
  98. // The main mouse message handlers then dereference these pointers to
  99. // call the correct handlers.
  100.  
  101. LRESULT  (*pfnLBDown)   (HWND, POINT);      // current mouse handler
  102. LRESULT  (*pfnLBUp)     (HWND, POINT);      // functions for rubber
  103. LRESULT  (*pfnMouseMove)(HWND, POINT);      // banding and such
  104.  
  105.  
  106. // The Line, Rectangle, and Ellipse modes use a common set of mouse
  107. // handlers that select 2 points.  The following function pointer is
  108. // used so that the correct drawing function is always used.  Like the
  109. // mouse function pointers, this is set when the drawing mode is
  110. // selected.
  111.  
  112. BOOL (*pfnDrawRect)(HDC, POINT, POINT);     // current drawing function
  113.  
  114. // Mouse handling functions for different types of drawing.
  115. LRESULT  PixelLBDown(HWND, POINT);          // Pixels
  116. LRESULT  PixelLBUp(HWND, POINT);
  117. LRESULT  PixelMouseMove(HWND, POINT);
  118.  
  119. LRESULT  BezierLBDown(HWND, POINT);         // Bezier Curves
  120. LRESULT  BezierLBUp(HWND, POINT);
  121. LRESULT  BezierMouseMove(HWND, POINT);
  122.  
  123. LRESULT  RectLBDown(HWND, POINT);           // 2-point selection
  124. LRESULT  RectLBUp(HWND, POINT);             // Used for Lines, Rects,
  125. LRESULT  RectMouseMove(HWND, POINT);        // and Ellipses
  126.  
  127. // Generic drawing functions for use with the above Rect functions
  128. BOOL     LineDraw(HDC, POINT, POINT);
  129. BOOL     RectDraw(HDC, POINT, POINT);
  130. BOOL     EllipseDraw(HDC, POINT, POINT);
  131.  
  132. // Other helper functions
  133. void StartRubberBand(HWND hwnd);
  134. void EndRubberBand(HWND hwnd);
  135. UINT BitmapWidth(HBITMAP);
  136. UINT BitmapHeight(HBITMAP);
  137.  
  138. // Client window message handling functions
  139. static LRESULT MsgClientCreate         (HWND, UINT, WPARAM, LPARAM);
  140. static LRESULT MsgClientDestroy        (HWND, UINT, WPARAM, LPARAM);
  141. static LRESULT MsgClientMouseMove      (HWND, UINT, WPARAM, LPARAM);
  142. static LRESULT MsgClientLButtonDown    (HWND, UINT, WPARAM, LPARAM);
  143. static LRESULT MsgClientLButtonUp      (HWND, UINT, WPARAM, LPARAM);
  144. static LRESULT MsgClientKeyDown        (HWND, UINT, WPARAM, LPARAM);   
  145. static LRESULT MsgClientPaint          (HWND, UINT, WPARAM, LPARAM);
  146. static LRESULT MsgClientScroll         (HWND, UINT, WPARAM, LPARAM);     
  147.  
  148.  
  149. // Client window message table definitions.
  150. MSD rgmsdClient[] =
  151. {
  152.     {WM_MOUSEMOVE,       MsgClientMouseMove       },
  153.     {WM_LBUTTONDOWN,     MsgClientLButtonDown     },
  154.     {WM_LBUTTONUP,       MsgClientLButtonUp       },
  155.     {WM_KEYDOWN,         MsgClientKeyDown         },
  156.     {WM_CREATE,          MsgClientCreate          },
  157.     {WM_DESTROY,         MsgClientDestroy         },   
  158.     {WM_PAINT,           MsgClientPaint           },
  159.     {WM_HSCROLL,         MsgClientScroll          },
  160.     {WM_VSCROLL,         MsgClientScroll          }
  161. };
  162.  
  163.  
  164. MSDI msdiClient =
  165. {
  166.     sizeof(rgmsdClient) / sizeof(MSD),
  167.     rgmsdClient,
  168.     edwpWindow
  169. };
  170.  
  171.  
  172. //
  173. //  FUNCTION: CreateClientWindow(HWND)
  174. //
  175. //  PURPOSE: Create the client window.
  176. //
  177. //  PARAMETERS:
  178. //    hwndParent - The parent (main) window.
  179. //
  180. //  RETURN VALUE:
  181. //    HWND of client window or null if failure.
  182. //
  183. //  COMMENTS:
  184. //
  185.  
  186. HWND CreateClientWindow(HWND hwndParent)
  187. {
  188.     return CreateWindowEx(0, 
  189.                           "ClientWndClass",
  190.                           NULL,
  191.                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
  192.                           -100, -100, 10, 10,   // dummy initial coordinates
  193.                           hwndParent, 
  194.                           (HMENU)-1, 
  195.                           hInst, 
  196.                           NULL);
  197. }
  198.  
  199.  
  200. //
  201. //  FUNCTION: ClientWndProc(HWND, UINT, WPARAM, LPARAM)
  202. //
  203. //  PURPOSE:  Processes messages for the client window.
  204. //
  205. //  PARAMETERS:
  206. //    hwnd     - window handle
  207. //    uMessage - message number
  208. //    wparam   - additional information (dependant on message number)
  209. //    lparam   - additional information (dependant on message number)
  210. //
  211. //  RETURN VALUE:
  212. //    The return value depends on the message number.  If the message
  213. //    is implemented in the message dispatch table, the return value is
  214. //    the value returned by the message handling function.  Otherwise,
  215. //    the return value is the value returned by the default window procedure.
  216. //
  217. //  COMMENTS:
  218. //    Call the DispMessage() function with the client window's message
  219. //    dispatch information (msdiClient) and the message specific information.
  220. //
  221.  
  222. LRESULT CALLBACK ClientWndProc(HWND   hwnd, 
  223.                                UINT   uMessage, 
  224.                                WPARAM wparam, 
  225.                                LPARAM lparam)
  226. {
  227.     return DispMessage(&msdiClient, hwnd, uMessage, wparam, lparam);
  228. }
  229.  
  230.  
  231. //
  232. //  FUNCTION: SizeClientWindow(HWND)
  233. //
  234. //  PURPOSE: Resizes the client window when the main window's size has changed or
  235. //           a bitmap is created or loaded.  Attempts to keep as much of the bitmap
  236. //         visible as possible.
  237. //
  238. //  PARAMETERS:
  239. //    hwnd - handle of Client window.
  240. //
  241. //  RETURN VALUE:
  242. //    none.
  243. //
  244. //  COMMENTS:
  245. //
  246.  
  247. void SizeClientWindow(HWND hwnd)    
  248. {
  249.     RECT rc, rcClient, rcFrame; 
  250.     int  cxRange = 0, cyRange = 0;     // Range needed for horz and vert 
  251.     int cxBitmap, cyBitmap;
  252.     
  253.     if (!hBitmap)
  254.     {
  255.         // no bitmap, so the client window is not needed
  256.         MoveWindow(hWndClient, 0, 0, 0, 0, TRUE);
  257.         return;
  258.     }
  259.                            
  260.     cxBitmap = BitmapWidth(hBitmap);
  261.     cyBitmap = BitmapHeight(hBitmap);
  262.                                
  263.     rcClient.left = rcClient.top = 0;
  264.    
  265.     // adjust size of client window to bitmap       
  266.     rcClient.right = cxBitmap;
  267.     rcClient.bottom = cyBitmap;     
  268.     
  269.     // get screen coordinates of toolbar window
  270.     GetWindowRect(hWndToolbar, &rc);
  271.     
  272.     // convert rc.right, rc.bottom to client coordinates
  273.     ScreenToClient(hwnd, ((LPPOINT)&rc) + 1);
  274.     
  275.     // adjust origin of Client window to be flush below Toolbar
  276.     rcClient.top = rc.bottom + 1;
  277.  
  278.     // get screen coordinates of statusbar window
  279.     GetWindowRect(hWndStatusbar, &rc);
  280.     
  281.     // convert rc.left, rc.top to client coordinates
  282.     ScreenToClient(hwnd, (LPPOINT)&rc);
  283.                                         
  284.     // get size of frame window
  285.     GetClientRect(hwnd, &rcFrame);
  286.                                             
  287.     // if client window would overlap statusbar, adjust the client window's size to be
  288.     // flush with the top of the statusbar window instead
  289.     if (rc.top < rcClient.bottom + rcClient.top)
  290.     {
  291.         // rcClient.bottom is the height in the MoveWindow call which follows below
  292.         rcClient.bottom = rc.top - rcClient.top;  
  293.       
  294.         // widen the client window to accomodate a vertical scrollbar    
  295.         rcClient.right += GetSystemMetrics(SM_CXVSCROLL);     
  296.         
  297.         // setup vertical scrollbar range
  298.         cyRange = cyBitmap - rcClient.bottom;      
  299.     }
  300.  
  301.     // adjust width of Client window to fit into frame window
  302.     if (rcClient.right > rcFrame.right)   
  303.     {
  304.         // rcClient.right is the width in the MoveWindow call which follows below        
  305.         rcClient.right = rcFrame.right;
  306.       
  307.         // make room for horizontal scrollbar if possible
  308.         rcClient.bottom = min(rcClient.bottom + GetSystemMetrics(SM_CYHSCROLL), 
  309.                               rc.top - rcClient.top);    
  310.                               
  311.         // setup horizontal scrollbar range
  312.         cxRange = cxBitmap - rcClient.right;
  313.             
  314.         // recalculate vertical scrollbar range
  315.         cyRange = cyBitmap - rcClient.bottom + GetSystemMetrics(SM_CYHSCROLL);            
  316.             
  317.         if (cyRange > 0)                    
  318.             // make allowance for a vertical scrollbar in the horizontal range
  319.             cxRange += GetSystemMetrics(SM_CXVSCROLL);                
  320.     }        
  321.    
  322.     // Set the ranges we've calculated (0 to 0 means invisible scrollbar)
  323.     SetScrollRange(hWndClient, SB_VERT, 0, cyRange, TRUE);
  324.     SetScrollRange(hWndClient, SB_HORZ, 0, cxRange, TRUE); 
  325.     
  326.     // reset global variable if a scrollbar is turned off                               
  327.     if (cyRange == 0)
  328.         cyVertScrollPos = 0;                                    
  329.     if (cxRange == 0)
  330.         cxHorzScrollPos = 0;    
  331.  
  332.     // resize the client window
  333.     MoveWindow(hWndClient,
  334.                rcClient.left,
  335.                rcClient.top,
  336.                rcClient.right,
  337.                rcClient.bottom,
  338.                TRUE);   
  339.            
  340.     InvalidateRect(hWndClient, NULL, FALSE);        
  341. }
  342.  
  343.  
  344. //
  345. //  FUNCTION: SetupScrollBars(HWND, int, int)
  346. //
  347. //  PURPOSE: Creates scrollbars if client can't display entire bitmap.
  348. //
  349. //  PARAMETERS:
  350. //    hwnd - handle of client window.
  351. //    cxBitmap - width of bitmap.
  352. //    cyBitmap - height of bitmap.
  353. //
  354. //  RETURN VALUE:
  355. //    none.
  356. //
  357. //  COMMENTS:
  358. //   
  359.  
  360. void SetupScrollBars(HWND hWnd, int cxBitmap, int cyBitmap)
  361. {
  362.     RECT        rect;                         // Client Rectangle
  363.     BOOL        fNeedScrollBars = FALSE;      // Need Scroll bars?
  364.     unsigned    cxWindow, cyWindow;           // Width and height of client area
  365.     int         cxRange = 0, cyRange = 0;     // Range needed for horz and vert  
  366.     DWORD dwWinStyle = GetWindowLong(hWnd, GWL_STYLE);
  367.  
  368.     GetClientRect(hWnd, &rect);
  369.  
  370.     if (dwWinStyle & WS_HSCROLL)
  371.         rect.bottom += GetSystemMetrics(SM_CYHSCROLL); 
  372.  
  373.     if (dwWinStyle & WS_VSCROLL)
  374.         rect.right  += GetSystemMetrics(SM_CXVSCROLL);
  375.       
  376.     cxWindow = RECTWIDTH(&rect);
  377.     cyWindow = RECTHEIGHT(&rect);
  378.  
  379.     // Now determine if we need the scroll bars
  380.     if ((cxWindow < (unsigned)cxBitmap) || (cyWindow < (unsigned)cyBitmap))
  381.         fNeedScrollBars = TRUE;
  382.  
  383.     // Setup the scroll bar ranges.  We want to be able to scroll the window so that 
  384.     // all the bitmap can appear within the client area.  Take into account that if the 
  385.     // opposite scroll bar is activated, it eats up some client area.
  386.     if (fNeedScrollBars)
  387.     {
  388.         cyRange = (unsigned)cyBitmap - cyWindow;
  389.         cxRange = (unsigned)cxBitmap - cxWindow; 
  390.         
  391.         if (cxRange)
  392.             cyRange -= GetSystemMetrics(SM_CYHSCROLL);
  393.             
  394.         if (cyRange)
  395.             cxRange -= GetSystemMetrics(SM_CXVSCROLL);            
  396.     }
  397.  
  398.     // Set the ranges we've calculated (0 to 0 means invisible scrollbar)
  399.     SetScrollRange(hWnd, SB_VERT, 0, cyRange, TRUE);
  400.     SetScrollRange(hWnd, SB_HORZ, 0, cxRange, TRUE); 
  401.     
  402.     // reset global variable if a scrollbar is turned off 
  403.                                
  404.     if (cyRange == 0)
  405.         cyVertScrollPos = 0;
  406.                                     
  407.     if (cxRange == 0)
  408.         cxHorzScrollPos = 0;    
  409. }
  410.  
  411. //
  412. //  FUNCTION: CmdDrawMode(HWND, WORD, WORD, HWND)
  413. //
  414. //  PURPOSE: Handles WM_COMMAND messages to change drawing mode.
  415. //
  416. //  PARAMETERS:
  417. //    hwnd - The main window.
  418. //    wCommand - IDM_LINE, IDM_RECT, etc.
  419. //    wNotify  - Notification number (unused)
  420. //    hwndCtrl - NULL (unused)
  421. //
  422. //  RETURN VALUE:
  423. //    Always returns 0 - command handled.
  424. //
  425. //  COMMENTS:
  426. //
  427.        
  428. #pragma argsused
  429. LRESULT CmdDrawMode(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  430. {   
  431.     if (wCommand == uDrawMode)  // First see if the mode has changed
  432.         return 0;
  433.  
  434.      // Update the menu and toolbar states
  435.   
  436.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_UNCHECKED);
  437.     uDrawMode = wCommand;
  438.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_CHECKED);
  439.  
  440.     SendMessage(hWndToolbar, TB_CHECKBUTTON, uDrawMode, MAKELONG(TRUE, 0));
  441.  
  442.     // Set the pointers for the mouse handler functions to
  443.     // the appropriate functions.
  444.  
  445.     switch(uDrawMode)
  446.     {
  447.         case IDM_PIXEL:
  448.             pfnLBDown     = PixelLBDown;
  449.             pfnLBUp       = PixelLBUp;
  450.             pfnMouseMove  = PixelMouseMove;
  451.             break;
  452.  
  453.         case IDM_BEZIER:
  454.             cPoints       = 0;
  455.             pfnLBDown     = BezierLBDown;
  456.             pfnLBUp       = BezierLBUp;
  457.             pfnMouseMove  = BezierMouseMove;
  458.             break;
  459.  
  460.         case IDM_RECT:
  461.             pfnDrawRect   = RectDraw;
  462.             pfnLBDown     = RectLBDown;
  463.             pfnLBUp       = RectLBUp;
  464.             pfnMouseMove  = RectMouseMove;
  465.             break;
  466.  
  467.         case IDM_ELLIPSE:
  468.             pfnDrawRect   = EllipseDraw;
  469.             pfnLBDown     = RectLBDown;
  470.             pfnLBUp       = RectLBUp;
  471.             pfnMouseMove  = RectMouseMove;
  472.             break;
  473.  
  474.         default:
  475.         case IDM_LINE:
  476.             pfnDrawRect   = LineDraw;
  477.             pfnLBDown     = RectLBDown;
  478.             pfnLBUp       = RectLBUp;
  479.             pfnMouseMove  = RectMouseMove;
  480.             break;
  481.     }
  482.  
  483.     return 0;
  484. }
  485.  
  486.  
  487. //
  488. //  FUNCTION: CmdFill(HWND, WORD, WORD, HWND)
  489. //
  490. //  PURPOSE: Handles the IDM_FILL and IDM_NOFILL command messages.
  491. //    Controls whether closed objects (rect's and ellipses) are filled
  492. //    with a brush when drawn.
  493. //
  494. //  PARAMETERS:
  495. //    hwnd     - The main window.
  496. //    wCommand - IDM_FILL or IDM_NOFILL
  497. //    wNotify  - Notification number (unused)
  498. //    hwndCtrl - NULL (unused)
  499. //
  500. //  RETURN VALUE:
  501. //    Always returns 0 - command handled.
  502. //
  503. //  COMMENTS:
  504. //    The toolbar has 2 buttons (IDM_FILL and IDM_NOFILL) but the
  505. //    menu only has IDM_FILL, so this function will turn filling
  506. //    OFF if wCommand is IDM_NOFILL, and TOGGLE the ON/OFF state
  507. //    if wCommand is IDM_FILL.
  508. //
  509.  
  510. #pragma argsused
  511. LRESULT CmdFill(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  512. {
  513.     static BOOL bFill;
  514.  
  515.     if (IDM_NOFILL == wCommand)
  516.         bFill = FALSE;                          // turn filling OFF
  517.     else
  518.         bFill = !bFill;                         // toggle fill state
  519.  
  520.     // Update the menu and toolbar states
  521.  
  522.      CheckMenuItem(hMenu,
  523.                   IDM_FILL,
  524.                   MF_BYCOMMAND | (bFill ? MF_CHECKED : MF_UNCHECKED));
  525.  
  526.     SendMessage(hWndToolbar,
  527.                 TB_CHECKBUTTON,
  528.                 (bFill ? IDM_FILL : IDM_NOFILL),
  529.                 MAKELONG(TRUE, 0));
  530.  
  531.     // Update hbrDraw
  532.     hbrDraw = (bFill ? hbrReal : hbrNull);
  533.  
  534.      return 0;
  535. }
  536.  
  537.  
  538. //
  539. //  FUNCTION: CmdCreatePen(HWND, WORD, WORD, HWND)
  540. //
  541. //  PURPOSE: Handles the IDM_CREATEPEN command message. Puts up a dialog
  542. //    to let the user set the pen style to use for drawing.
  543. //
  544. //  PARAMETERS:
  545. //    hwnd     - The main window.
  546. //    wCommand - IDM_CREATEPEN (unused)
  547. //    wNotify  - Notification number (unused)
  548. //    hwndCtrl - NULL (unused)
  549. //
  550. //  RETURN VALUE:
  551. //    Always returns 0 - command handled.
  552. //
  553. //  COMMENTS:
  554. //
  555.  
  556. #pragma argsused
  557. LRESULT CmdCreatePen(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  558. {
  559.     LOGPEN lp = logPen;     // copy for the dialog to munge on
  560.  
  561.     if (DialogBoxParam(hInst, "PenDlg", hwnd, (DLGPROC)PenDlg, (LPARAM)&lp))
  562.     {
  563.         DeleteObject(hpenDraw);                     // delete old pen
  564.         logPen = lp;                                // save changes
  565.         hpenDraw = CreatePenIndirect(&logPen);      // create new pen
  566.     }
  567.  
  568.     return 0;
  569. }
  570.  
  571.  
  572. //
  573. //  FUNCTION: CmdCreateBrush(HWND, WORD, WORD, HWND)
  574. //
  575. //  PURPOSE: Handles the IDM_CREATEBRUSH command message. Puts up a dialog
  576. //    to let the user set the brush style to use for drawing.
  577. //
  578. //  PARAMETERS:
  579. //    hwnd - The main window.
  580. //    wCommand - IDM_CREATEBRUSH (unused)
  581. //    wNotify  - Notification number (unused)
  582. //    hwndCtrl - NULL (unused)
  583. //
  584. //  RETURN VALUE:
  585. //    Always returns 0 - command handled.
  586. //
  587. //  COMMENTS:
  588. //
  589.  
  590. #pragma argsused
  591. LRESULT CmdCreateBrush(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  592. {
  593.     LOGBRUSH lb = logBrush; // copy for the dialog to munge on
  594.  
  595.     if (DialogBoxParam(hInst, "BrushDlg", hwnd, (DLGPROC)BrushDlg, (LPARAM)&lb))
  596.     {
  597.         DeleteObject(hbrReal);                      // delete old brush
  598.         logBrush = lb;                              // save changes
  599.         hbrReal = CreateBrushIndirect(&logBrush);   // create new brush
  600.  
  601.         // If "Fill Objects" is turned on, we need to update hbrDraw
  602.         if (GetMenuState(hMenu, IDM_FILL, MF_BYCOMMAND) & MF_CHECKED)
  603.             hbrDraw = hbrReal;
  604.      }
  605.  
  606.     return 0;
  607. }
  608.  
  609.  
  610. //
  611. //  FUNCTION: CmdClear(HWND, WORD, WORD, HWND)
  612. //
  613. //  PURPOSE: Clears the contents of the client window.
  614. //
  615. //  PARAMETERS:
  616. //    hwnd     - The main window.
  617. //    wCommand - IDM_CLEAR (unused)
  618. //    wNotify  - Notification number (unused)
  619. //    hwndCtrl - NULL (unused)
  620. //
  621. //  RETURN VALUE:
  622. //    Always returns 0 - command handled.
  623. //
  624. //  COMMENTS:
  625. //
  626. //
  627.  
  628. #pragma argsused
  629. LRESULT CmdClear(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  630. {
  631.     char szBuffer[9];
  632.     int iResult;
  633.        
  634.     LoadString(hInst, IDS_UNTITLED, szBuffer, sizeof(szBuffer));
  635.               
  636.     // warn the user if important data might be lost
  637.     if (strcmp(szCurrentFile, szBuffer))
  638.     {
  639.           // query the user before blowing old bits away
  640.         iResult = MessageBox(hwnd, 
  641.                              "Rename initialized bitmap as Untitled?", 
  642.                              SZAPPNAME, 
  643.                              MB_YESNOCANCEL);
  644.                              
  645.         switch (iResult)
  646.         {
  647.             case IDYES:
  648.                 // rename the file before continuting, otherwise the original
  649.                 // bitmap file may be inadvertently overwritten on disk
  650.                 strcpy(szCurrentFile, szBuffer);
  651.                      break;
  652.                 
  653.             case IDNO:
  654.                 // allow the operation to continue
  655.                 break;
  656.                 
  657.             case IDCANCEL:
  658.                 // don't erase the drawing surface
  659.                 return 0; 
  660.         }
  661.     }
  662.  
  663.  
  664.     InitDIBSection(hWndClient);
  665.     
  666.     return 0;
  667. }
  668.  
  669.                        
  670. //
  671. //  FUNCTION: MsgClientCreate(HWND, UINT, WPARAM, LPARAM)
  672. //
  673. //  PURPOSE: Handles the WM_CREATE message for the client window.
  674. //
  675. //  PARAMETERS:
  676. //
  677. //    hwnd      - Window handle  (Unused)
  678. //    uMessage  - Message number (Unused)
  679. //    wparam    - Extra data     (Unused)
  680. //    lparam    - Extra data     (Unused)
  681. //
  682. //  RETURN VALUE:
  683. //
  684. //  COMMENTS:
  685. //    Initializes pen and brush objects.
  686. //
  687.  
  688. #pragma argsused
  689. LRESULT MsgClientCreate(HWND   hwnd,
  690.                         UINT   uMessage, 
  691.                         WPARAM wparam, 
  692.                         LPARAM lparam)
  693. {
  694.     // Initialize pens and brushes
  695.  
  696.     hpenBlack = GetStockObject(BLACK_PEN);
  697.      hbrNull   = GetStockObject(NULL_BRUSH);
  698.                 
  699.     InitDrawObjects();            
  700.  
  701.     return 0;
  702. }
  703.  
  704.  
  705. //
  706. //  FUNCTION: InitDrawObjects()
  707. //
  708. //  PURPOSE: Initializes the pen and brush used for drawing.
  709. //
  710. //  PARAMETERS:
  711. //    none
  712. //
  713. //  RETURN VALUE:
  714. //
  715. //  COMMENTS:
  716. //    
  717. //
  718.  
  719. void InitDrawObjects()
  720. {
  721.     hpenDraw  = hpenBlack;
  722.     hbrReal   = GetStockObject(GRAY_BRUSH);
  723.  
  724.     hbrDraw = hbrReal;
  725.  
  726.     GetObject(hpenDraw, sizeof(logPen), &logPen);
  727.     GetObject(hbrReal, sizeof(logBrush), &logBrush);
  728. }
  729.  
  730.  
  731. //
  732. //  FUNCTION: MsgClientDestroy(HWND, UINT, WPARAM, LPARAM)
  733. //
  734. //  PURPOSE: Handles WM_DESTROY message for the client window.
  735. //
  736. //  PARAMETERS:
  737. //
  738. //    hwnd      - Window handle  (Unused)
  739. //    uMessage  - Message number (Unused)
  740. //    wparam    - Extra data     (Unused)
  741. //    lparam    - Extra data     (Unused)
  742. //
  743. //  RETURN VALUE:
  744. //
  745. //    Always returns 0 - Message handled
  746. //
  747. //  COMMENTS:
  748. //    General clean up happens here.
  749. //
  750.  
  751. #pragma argsused
  752. LRESULT MsgClientDestroy(HWND   hwnd,
  753.                          UINT   uMessage, 
  754.                          WPARAM wparam, 
  755.                          LPARAM lparam)
  756. {
  757.     // Delete the pen and brush used for drawing.  Don't need to delete
  758.      // hpenBlack and hbrNull since these are always stock objects.
  759.  
  760.     DeleteObject(hpenDraw);
  761.     DeleteObject(hbrReal);
  762.  
  763.     return 0;
  764. }
  765.  
  766.  
  767. //
  768. //  FUNCTION: MsgClientLButtonDown(HWND, UINT, WPARAM, LPARAM)
  769. //
  770. //  PURPOSE: Handles WM_LBUTTONDOWN message.
  771. //
  772. //  PARAMETERS:
  773. //
  774. //    hwnd      - Window handle
  775. //    uMessage  - Message number (Unused)
  776. //    wparam    - Extra data     (Unused)
  777. //    lparam    - Mouse coordinates
  778. //
  779. //  RETURN VALUE:
  780. //
  781. //  COMMENTS:
  782. //    Initiates dragging/drawing operation by saving the mouse
  783. //    position and capturing mouse input.
  784. //
  785.  
  786. #pragma argsused
  787. LRESULT MsgClientLButtonDown(HWND   hwnd,
  788.                                       UINT   uMessage,
  789.                              WPARAM wparam,
  790.                              LPARAM lparam)
  791. {
  792.     POINT pt;
  793.  
  794.     // If we aren't already drawing, set focus to the client window
  795.     // so we can get Escape key messages should the user decide to 
  796.     // quit drawing.
  797.  
  798.     if (!bDrawing)
  799.         SetFocus(hwnd);
  800.  
  801.     // Get mouse position
  802.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  803.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  804.  
  805.     // Call the current LBDown handler
  806.     return (*pfnLBDown)(hwnd, pt);
  807. }
  808.  
  809.  
  810. //
  811. //  FUNCTION: MsgClientMouseMove(HWND, UINT, WPARAM, LPARAM)
  812. //
  813. //  PURPOSE: Handles WM_MOUSEMOVE message.
  814. //
  815. //  PARAMETERS:
  816. //
  817. //    hwnd      - Window handle
  818. //    uMessage  - Message number (Unused)
  819. //    wparam    - Extra data     (Unused)
  820. //    lparam    - Mouse coordinates
  821. //
  822. //  RETURN VALUE:
  823. //
  824. //  COMMENTS:
  825. //    Performs "rubber-banding" by erasing the previous image and
  826. //    redrawing the object using an XOR ROP code.
  827. //
  828.  
  829. #pragma argsused
  830. LRESULT MsgClientMouseMove(HWND   hwnd,
  831.                            UINT   uMessage,
  832.                            WPARAM wparam,
  833.                            LPARAM lparam)
  834. {
  835.     POINT pt;
  836.     static char szBuf[20] ;     // Array for formatting mouse coordinates
  837.  
  838.     // Get new mouse position
  839.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  840.      pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  841.                                       
  842.     // Update the status bar with the new position
  843.     wsprintf(szBuf, "%d,%d", pt.x, pt.y);
  844.     UpdateStatusBar(szBuf, 2, 0);
  845.  
  846.     // Call the current MouseMove handler
  847.     return (*pfnMouseMove)(hwnd, pt);
  848. }
  849.  
  850.  
  851. //
  852. //  FUNCTION: MsgClientLButtonUp(HWND, UINT, WPARAM, LPARAM)
  853. //
  854. //  PURPOSE: Handles WM_LBUTTONUP message.
  855. //
  856. //  PARAMETERS:
  857. //
  858. //    hwnd      - Window handle
  859. //    uMessage  - Message number (Unused)
  860. //    wparam    - Extra data     (Unused)
  861. //    lparam    - Mouse coordinates
  862. //
  863. //  RETURN VALUE:
  864. //
  865. //  COMMENTS:
  866. //    Erases previous rubber-band image and draws the object in
  867. //      the final position.
  868. //
  869.  
  870. #pragma argsused
  871. LRESULT MsgClientLButtonUp(HWND   hwnd,
  872.                                     UINT   uMessage,
  873.                                     WPARAM wparam,
  874.                                     LPARAM lparam)
  875. {
  876.     POINT pt;
  877.  
  878.     // Get new mouse position
  879.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  880.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  881.   
  882.     // Call the current LBUp handler
  883.     return (*pfnLBUp)(hwnd, pt);
  884. }
  885.  
  886.  
  887. //
  888. //  FUNCTION: MsgClientKeyDown(HWND, UINT, WPARAM, LPARAM)
  889. //
  890. //  PURPOSE: Handles WM_KEYDOWN message.
  891. //
  892. //  PARAMETERS:
  893. //
  894. //    hwnd      - Window handle
  895. //    uMessage  - Message number (Unused)
  896. //    wparam    - Key code (VK_xxx)
  897. //    lparam    - Extra data     (Unused)
  898. //
  899. //  RETURN VALUE:
  900. //
  901. //  COMMENTS:
  902. //    Looks for VK_ESCAPE key only.  If user hits the escape key
  903. //    while drawing, cancel the drawing operation.
  904. //
  905.  
  906. #pragma argsused
  907. LRESULT MsgClientKeyDown(HWND   hwnd,
  908.                                  UINT   uMessage,
  909.                                  WPARAM wparam,
  910.                                  LPARAM lparam)
  911. {
  912.      if (VK_ESCAPE == wparam && bDrawing)
  913.     {
  914.         EndRubberBand(hwnd);
  915.         cPoints = 0;
  916.  
  917.         // get rid of residue from the aborted drawing operation
  918.         InvalidateRect(hwnd, NULL, TRUE);
  919.     }
  920.  
  921.     return 0;
  922. }
  923.  
  924.  
  925. //
  926. //  FUNCTION: xxxLBDown(HWND, UINT, WPARAM, LPARAM)
  927. //  FUNCTION: xxxMouseMove(HWND, UINT, WPARAM, LPARAM)
  928. //  FUNCTION: xxxLBUp(HWND, UINT, WPARAM, LPARAM)
  929. //
  930. //  PURPOSE: Handles WM_LBUTTONDOWN, WM_MOUSEMOVE, and WM_LBUTTONUP
  931. //
  932. //  PARAMETERS:
  933. //
  934. //    hwnd - Window handle
  935. //    pt   - Mouse coordinates
  936. //
  937. //  RETURN VALUE:
  938. //    Always return 0
  939. //
  940. //  COMMENTS:
  941. //    These functions are called by the actual Msgxxx mouse message
  942. //    handler functions (above) to perform input processing.  Three
  943. //    sets of these functions are implemented here.  One to input
  944. //    individual pixels; one to select 2 points (used to draw a
  945. //    line, rectangle, or ellipse); and one to select 4 points (for
  946. //    drawing Bezier curves).  Additional functions could be "plugged
  947. //    in" to support other types of drawing.
  948. //
  949. //    In general, xxxLBDown initializes the drawing process, for example
  950. //    by saving the initial coordinates and calling StartRubberBand;
  951. //    xxxMouseMove updates the coordinates; and xxxLBUp performs any
  952. //    final processing and clean up (EndRubberBand).
  953. //
  954.  
  955. // 
  956. //  FUNCTION: PixelLBDown(HWND, UINT, WPARAM, LPARAM)
  957. //  FUNCTION: PixelMouseMove(HWND, UINT, WPARAM, LPARAM)
  958. //  FUNCTION: PixelLBUp(HWND, UINT, WPARAM, LPARAM)
  959. //
  960. //  PURPOSE: Perform single point selection (individual pixels).
  961. //
  962.  
  963. LRESULT PixelLBDown(HWND hwnd, POINT pt)
  964. {
  965.     // Get a DC and capture mouse input
  966.     StartRubberBand(hwnd);
  967.     SetROP2(hdcRB, R2_COPYPEN);
  968.     SetROP2(hdcMem, R2_COPYPEN);
  969.     
  970.     // Draw the pixel (use current pen color)
  971.     SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  972.  
  973.     // do it again in memory DC
  974.     SetPixelV(hdcMem, pt.x, pt.y, logPen.lopnColor);
  975.  
  976.     return 0;
  977. }
  978.  
  979.  
  980. #pragma argsused
  981. LRESULT PixelMouseMove(HWND hwnd, POINT pt)
  982. {
  983.     // If we are currently drawing, draw the pixel
  984.     if (bDrawing)
  985.     {
  986.         SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  987.         
  988.          // do it again in memory DC
  989.         SetPixelV(hdcMem, pt.x, pt.y, logPen.lopnColor);
  990.     }
  991.  
  992.     return 0;
  993. }
  994.  
  995.  
  996. #pragma argsused
  997. LRESULT PixelLBUp(HWND hwnd, POINT pt)
  998. {   
  999.     // Don't draw the pixel again here (it's already drawn at LBDown
  1000.     // and MouseMove time).  Just need to clean up.
  1001.  
  1002.     if (bDrawing)
  1003.         EndRubberBand(hwnd);
  1004.  
  1005.     return 0;
  1006. }
  1007.  
  1008.  
  1009. // 
  1010. //  FUNCTION: BezierLBDown(HWND, UINT, WPARAM, LPARAM)
  1011. //  FUNCTION: BezierMouseMove(HWND, UINT, WPARAM, LPARAM)
  1012. //  FUNCTION: BezierLBUp(HWND, UINT, WPARAM, LPARAM)
  1013. //
  1014. //  PURPOSE: Perform 4-point selection for drawing Bezier curves.
  1015. //
  1016.  
  1017. #pragma argsused
  1018. LRESULT BezierLBDown(HWND hwnd, POINT pt)
  1019. {
  1020.      // If this is the first click, initialize rubber banding.
  1021.  
  1022.      if (0 == cPoints)
  1023.           StartRubberBand(hwnd);
  1024.  
  1025.      return 0;
  1026. }
  1027.  
  1028.  
  1029. #pragma argsused
  1030. LRESULT BezierMouseMove(HWND hwnd, POINT pt)
  1031. {
  1032.     int i;
  1033.  
  1034.     if (!bDrawing || 0 == cPoints)              // Are we currently drawing?
  1035.         return 0;
  1036.  
  1037.     // Are we rubber banding a Bezier?
  1038.     if (cPoints >= 2)
  1039.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS); // Erase previous Bezier
  1040.  
  1041.     // Erase previous line segment
  1042.      LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1043.  
  1044.     // Set the rest of the points to be the same as this one
  1045.     for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  1046.         pPoints[i] = pt;
  1047.  
  1048.     // Draw new line segment
  1049.     LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1050.  
  1051.     // Are we rubber banding a Bezier?
  1052.     if (cPoints >= 2)
  1053.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1054.  
  1055.     return 0;
  1056. }
  1057.  
  1058.  
  1059. LRESULT BezierLBUp(HWND hwnd, POINT pt)
  1060. {
  1061.     int i;
  1062.  
  1063.     if (!bDrawing)          // Are we currently drawing?
  1064.         return 0;
  1065.  
  1066.     if (cPoints >= 2)       // Are we rubber banding a Bezier?
  1067.     {
  1068.         // Erase previous Bezier
  1069.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1070.  
  1071.         if (cPoints == BEZ_MAXPOINTS - 1) // Is this the last point?
  1072.         {
  1073.             // If so, then erase all line segments.
  1074.             for (i = BEZ_MAXPOINTS - 1; i > 0; i--)
  1075.                 LineDraw(hdcRB, pPoints[i - 1], pPoints[i]);
  1076.         }
  1077.     }
  1078.     // else do *not* erase the previous line segments
  1079.  
  1080.     // Save new position and increment # of points
  1081.     pPoints[cPoints++] = pt;
  1082.  
  1083.     if (cPoints < BEZ_MAXPOINTS)    // Still more points to get.
  1084.     {
  1085.         // Set the rest of the points to be the same as the current one
  1086.         for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  1087.             pPoints[i] = pt;
  1088.  
  1089.         // Draw new line segment
  1090.         LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1091.  
  1092.         // Once we have at least 2 points let's draw the Bezier curve
  1093.         if (cPoints >= 2)
  1094.             PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1095.     }
  1096.     else    // Finish this Bezier and quit drawing.
  1097.     {
  1098.         // Setup DC for 'real' drawing
  1099.         SetROP2(hdcRB, R2_COPYPEN);
  1100.         SetBkMode(hdcRB, TRANSPARENT);
  1101.         SelectObject(hdcRB, hpenDraw);
  1102.  
  1103.         // Draw Bezier in permanent position
  1104.         PolyBezier(hdcRB, pPoints, cPoints);
  1105.  
  1106.         // De-select the pen and clean up rubber banding stuff.
  1107.         SelectObject(hdcRB, hpenBlack);     
  1108.         
  1109.         // do it again in Memory DC         
  1110.         SetROP2(hdcMem, R2_COPYPEN);
  1111.         SetBkMode(hdcMem, TRANSPARENT);
  1112.         SelectObject(hdcMem, hpenDraw);
  1113.  
  1114.         // Draw Bezier in permanent position
  1115.         PolyBezier(hdcMem, pPoints, cPoints);
  1116.  
  1117.         // De-select the pen and clean up rubber banding stuff.
  1118.         SelectObject(hdcMem, hpenBlack);
  1119.         
  1120.         EndRubberBand(hwnd);
  1121.         cPoints = 0;
  1122.     }
  1123.  
  1124.     return 0;
  1125. }
  1126.  
  1127.  
  1128. // 
  1129. //  FUNCTION: RectLBDown(HWND, UINT, WPARAM, LPARAM)
  1130. //  FUNCTION: RectMouseMove(HWND, UINT, WPARAM, LPARAM)
  1131. //  FUNCTION: RectLBUp(HWND, UINT, WPARAM, LPARAM)
  1132. //
  1133. //  PURPOSE: Perform 2-point selection for drawing lines, rectangles,
  1134. //    and ellipses.
  1135. //
  1136.  
  1137. LRESULT RectLBDown(HWND hwnd, POINT pt)
  1138. {
  1139.     // Current position = starting position = mouse position
  1140.      pPoints[0] = pPoints[1] = pt;
  1141.  
  1142.     // Initialize rubber banding
  1143.     StartRubberBand(hwnd);
  1144.  
  1145.     // Draw initial state
  1146.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1147.  
  1148.     return 0;
  1149. }
  1150.  
  1151.  
  1152. #pragma argsused
  1153. LRESULT RectMouseMove(HWND hwnd, POINT pt)
  1154. {
  1155.     if (!bDrawing)          // Are we currently drawing?
  1156.         return 0;
  1157.  
  1158.     // Un-draw previous position by redrawing
  1159.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1160.  
  1161.     // Save new position
  1162.     pPoints[1] = pt;
  1163.  
  1164.     // Draw in new position
  1165.      (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1166.  
  1167.     return 0;
  1168. }
  1169.  
  1170.  
  1171. LRESULT RectLBUp(HWND hwnd, POINT pt)
  1172. {
  1173.     if (!bDrawing)          // Are we currently drawing?
  1174.         return 0;
  1175.  
  1176.     // Un-draw previous position by redrawing
  1177.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1178.  
  1179.     // Save new position
  1180.     pPoints[1] = pt;
  1181.  
  1182.     // Setup DC for 'real' drawing
  1183.     SetROP2(hdcRB, R2_COPYPEN);
  1184.     SetBkMode(hdcRB, TRANSPARENT);
  1185.     SelectObject(hdcRB, hpenDraw);
  1186.     SelectObject(hdcRB, hbrDraw);
  1187.  
  1188.     // Draw object in permanent position
  1189.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1190.  
  1191.     // De-select the pen and brush by selecting stock objects 
  1192.     // and clean up rubber banding stuff.
  1193.     SelectObject(hdcRB, hpenBlack);
  1194.     SelectObject(hdcRB, hbrNull); 
  1195.     
  1196.     // draw it again onto bitmap in memory DC   
  1197.     SetROP2(hdcMem, R2_COPYPEN);
  1198.     SetBkMode(hdcMem, TRANSPARENT);
  1199.     SelectObject(hdcMem, hpenDraw);
  1200.     SelectObject(hdcMem, hbrDraw);
  1201.  
  1202.     // Draw object in permanent position
  1203.     (*pfnDrawRect)(hdcMem, pPoints[0], pPoints[1]);
  1204.  
  1205.     // De-select the pen and brush by selecting stock objects 
  1206.     // and clean up rubber banding stuff.
  1207.     SelectObject(hdcMem, hpenBlack);
  1208.     SelectObject(hdcMem, hbrNull);
  1209.     
  1210.     EndRubberBand(hwnd);
  1211.  
  1212.     return 0;
  1213. }
  1214.  
  1215.  
  1216. //
  1217. //  FUNCTION: xxxxDraw(HDC hdc, POINT pt1, POINT pt2)
  1218. //
  1219. //  PURPOSE: Draws a Line, Rectangle, or Ellipse at the coordinates
  1220. //    given by pt1 and pt2.
  1221. //
  1222. //  PARAMETERS:
  1223. //    hdc   - Device context to draw into.
  1224. //    pt1   - Beginning point (upper-left corner of rect)
  1225. //    pt2   - Ending point    (lower-right corner of rect)
  1226. //
  1227. //  RETURN VALUE:
  1228. //    TRUE for success, FALSE otherwise.
  1229. //
  1230. //  COMMENTS:
  1231. //    Assumes the DC is already correctly set up.
  1232. //
  1233. //
  1234.  
  1235. BOOL LineDraw(HDC hdc, POINT pt1, POINT pt2)
  1236. {
  1237.     MoveToEx(hdc, pt1.x, pt1.y, NULL);
  1238.     return LineTo(hdc, pt2.x, pt2.y);
  1239. }
  1240.  
  1241.  
  1242. BOOL RectDraw(HDC hdc, POINT pt1, POINT pt2)
  1243. {
  1244.     return Rectangle(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1245. }
  1246.  
  1247.  
  1248. BOOL EllipseDraw(HDC hdc, POINT pt1, POINT pt2)
  1249. {
  1250.     return Ellipse(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1251. }
  1252.  
  1253.  
  1254. //
  1255. //  FUNCTION: StartRubberBand(HWND)
  1256. //
  1257. //  PURPOSE: Sets up DC for rubber banding and captures the mouse.
  1258. //
  1259. //  PARAMETERS:
  1260. //    hwnd - Window for GetDC
  1261. //
  1262. //  RETURN VALUE:
  1263. //    None
  1264. //
  1265. //  COMMENTS:
  1266. //
  1267.  
  1268. void StartRubberBand(HWND hwnd)
  1269. {         
  1270.     // Get a DC to draw in
  1271.     hdcRB = GetDC(hwnd);                        
  1272.     
  1273.     // adjust the viewport origin for scrolled view
  1274.     SetViewportOrgEx(hdcRB, -1 * cxHorzScrollPos, -1 * cyVertScrollPos, NULL); 
  1275.  
  1276.     // R2_NOT causes drawing with a pen to invert the screen pixels.
  1277.     // This makes it easy to erase something by drawing it a 2nd time.
  1278.     SetROP2(hdcRB, R2_NOT);
  1279.  
  1280.     // For rubber banding with R2_NOT, we use a single pixel pen and
  1281.     // a NULL brush.
  1282.     SelectObject(hdcRB, hpenBlack);
  1283.     SelectObject(hdcRB, hbrNull);
  1284.  
  1285.     SetCapture(hwnd);                           // Capture mouse input
  1286.     bDrawing = TRUE;     
  1287.     
  1288.     // select the DIB's palette into the screen DC so it will select into the
  1289.     // memory DC properly
  1290.     if (hPalette)
  1291.         hpalRB = SelectPalette(hdcRB, hPalette, TRUE);
  1292.     
  1293.     // setup a memory DC to update the bitmap  
  1294.     // do not adjust its viewport because we are only scrolling the screen DC
  1295.     // i.e., we aren't viewing the image in the memory DC
  1296.     hdcMem = CreateCompatibleDC(hdcRB);      
  1297.     hbmOld = SelectObject(hdcMem, hBitmap);
  1298.     if (hpalMem)
  1299.         hpalMem = SelectPalette(hdcMem, hPalette, TRUE);    
  1300. }
  1301.  
  1302.  
  1303. //
  1304. //  FUNCTION: EndRubberBand(HWND)
  1305. //
  1306. //  PURPOSE: Releases rubber banding DC and mouse capture.
  1307. //
  1308. //  PARAMETERS:
  1309. //    hwnd - Window for ReleaseDC
  1310. //
  1311. //  RETURN VALUE:
  1312. //    None
  1313. //
  1314. //  COMMENTS:
  1315. //
  1316.  
  1317. void EndRubberBand(HWND hwnd)
  1318. {   
  1319.     BOOL fOldChanges = fChanges; 
  1320.     
  1321.     if (hpalMem)
  1322.         SelectPalette(hdcMem, hpalMem, FALSE);
  1323.     SelectObject(hdcMem, hbmOld);
  1324.     DeleteDC(hdcMem);
  1325.     fChanges = TRUE;
  1326.     EnableMenuItem(hMenu, IDM_FILESAVE, MF_ENABLED);
  1327.     SendMessage(hWndToolbar, TB_ENABLEBUTTON, IDM_FILESAVE, MAKELONG(TRUE, 0));
  1328.                  
  1329.     if (hpalRB)
  1330.         SelectPalette(hdcRB, hpalRB, FALSE);
  1331.     SetViewportOrgEx(hdcRB, 0, 0, NULL);
  1332.     ReleaseDC(hwnd, hdcRB);
  1333.     ReleaseCapture();    
  1334.     bDrawing = FALSE;
  1335.     
  1336.     if (fOldChanges == FALSE)
  1337.         // need to update main window text now so '*' is included for changes
  1338.         SetWindowTitle(GetParent(hwnd), szCurrentFile);
  1339. }        
  1340.  
  1341.  
  1342. //
  1343. //  FUNCTION: MsgClientPaint(HWND, UINT, WPARAM, LPARAM)
  1344. //
  1345. //  PURPOSE: Paints the client window in response to WM_PAINT message.
  1346. //
  1347. //  PARAMETERS:
  1348. //    hwnd - Handle of Client window.
  1349. //    uMessage - (unused)
  1350. //    wparam - (unused)
  1351. //    lparam - (unused)
  1352. //
  1353. //  RETURN VALUE:
  1354. //    0
  1355. //
  1356. //  COMMENTS:
  1357. //
  1358.  
  1359. #pragma argsused
  1360. LRESULT MsgClientPaint(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  1361. {
  1362.      HDC         hDC;                 // Handle to DC,
  1363.      PAINTSTRUCT ps;                  // Painting structure
  1364.      RECT        rcClient, rcDDB;     // Client and bitmap rectangles
  1365.      int         xScroll, yScroll;    // Scroll positions
  1366.  
  1367.     // Begin painting
  1368.     hDC = BeginPaint(hwnd, &ps);
  1369.  
  1370.     // if there is a bitmap, display it
  1371.     if (hBitmap)
  1372.     {                      
  1373.         int dx = BitmapWidth(hBitmap);
  1374.         int dy = BitmapHeight(hBitmap);
  1375.     
  1376.         // Get scroll bar positions
  1377.         xScroll  = GetScrollPos(hwnd, SB_HORZ);
  1378.           yScroll  = GetScrollPos(hwnd, SB_VERT);
  1379.  
  1380.         // Set up the necessary rectangles -- i.e. the rectangle
  1381.         // we're rendering into, and the rectangle in the bitmap
  1382.         GetClientRect(hwnd, &rcClient);
  1383.  
  1384.         rcDDB.left   = xScroll;
  1385.         rcDDB.top    = yScroll;
  1386.         rcDDB.right  = xScroll + RECTWIDTH(&rcClient);
  1387.         rcDDB.bottom = yScroll + RECTHEIGHT(&rcClient);        
  1388.                     
  1389.         if (rcDDB.right > dx)
  1390.           {
  1391.             dx -= rcDDB.right;
  1392.  
  1393.             rcDDB.right     += dx;
  1394.             rcClient.right  += dx;
  1395.         }
  1396.       
  1397.         if (rcDDB.bottom > dy)
  1398.         {
  1399.             dy -= rcDDB.bottom;
  1400.             
  1401.             rcDDB.bottom    += dy;
  1402.                 rcClient.bottom += dy;
  1403.         }
  1404.  
  1405.         // Go do the actual painting.
  1406.         PaintBitmap(hDC, &rcClient, hBitmap, &rcDDB, hPalette);
  1407.     }   
  1408.     else
  1409.     {         
  1410.         // Turn off scroll bars in case they were on
  1411.         SetScrollRange (hwnd, SB_VERT, 0, 0, TRUE);
  1412.         SetScrollRange (hwnd, SB_HORZ, 0, 0, TRUE);         
  1413.         cxHorzScrollPos = cyVertScrollPos = 0;
  1414.      }
  1415.         
  1416.     EndPaint(hwnd, &ps);
  1417.     return 0;
  1418. }
  1419.  
  1420.  
  1421. //
  1422. //  FUNCTION: MsgClientScroll(HWND, UINT, WPARAM, LPARAM)
  1423. //
  1424. //  PURPOSE: Scrolls the client window in response to scroll message.
  1425. //
  1426. //  PARAMETERS:
  1427. //    hwnd - handle of Client window. 
  1428. //    uMessage - the scroll message.
  1429. //    wParam - contains scroll position and scrollbar type
  1430. //    lParam - (unused)
  1431. //
  1432. //  RETURN VALUE:
  1433. //    None
  1434. //
  1435. //  COMMENTS:
  1436. //    MsgClientScroll performs scrolling in both horiziontal and vertical directions.
  1437. //    If the user clicks on one of the scrolling arrows, the window is scrolled
  1438. //    by (1 / SCROLL_RATIO) of the client area.  For example, if 
  1439. //    SCROLL_RATIO == 4, then the client area is moved over a 1/4 of the 
  1440. //    width or height (as the case may be) of the screen.  If the user pages
  1441. //    up/down the window is scrolled an amount equal to the client area's
  1442. //    width or height as the case may be. If the user moves the thumb to an 
  1443. //    absolute position, the window is scrolled accordingly.
  1444. //
  1445. //
  1446.  
  1447. #define SCROLL_RATIO 4
  1448.  
  1449. #pragma argsused
  1450. LRESULT MsgClientScroll(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  1451. {
  1452.      int nPos;          // Scrollbar position.
  1453.      int nMin;          // Minumum scroll bar value.
  1454.      int nMax;          // Maximum scroll bar value.
  1455.     int nMove;         // How much to move.
  1456.     int nLine;         // # of pixels for LINEUP/LINEDOWN
  1457.     int nPage;         // # of pixels for PAGEUP/PAGEDOWN
  1458.     int nHorzOrVert;   // SB_HORZ if doing horizontal, SB_VERT if doing vertical
  1459.     RECT rcClient;     // Client area.
  1460.  
  1461.     GetClientRect(hwnd, &rcClient);
  1462.  
  1463.     if (uMessage == WM_HSCROLL)
  1464.     {
  1465.         nHorzOrVert = SB_HORZ;
  1466.           nPage = BitmapWidth(hBitmap) - RECTWIDTH(&rcClient);
  1467.     }
  1468.     else
  1469.     {
  1470.         nHorzOrVert = SB_VERT;
  1471.         nPage = BitmapHeight(hBitmap) - RECTHEIGHT(&rcClient);
  1472.     }
  1473.  
  1474.     if (nPage < 0)
  1475.     {
  1476.         OutputDebugString("Scrolling not necessary\r\n");
  1477.         return 0;
  1478.      }
  1479.  
  1480.     // On a SB_LINEUP/SB_LINEDOWN we will move the picture by
  1481.     //  1 / SCROLL_RATIO of the page amount (i.e. if SCROLL_RATIO
  1482.     //  is 4, it will scroll the picture a quarter of the way)
  1483.  
  1484.     nLine = nPage / SCROLL_RATIO;
  1485.     if (nLine == 0)
  1486.         nLine = 1;
  1487.  
  1488.     nPos = GetScrollPos(hwnd, nHorzOrVert);
  1489.     GetScrollRange(hwnd, nHorzOrVert, &nMin, &nMax);
  1490.  
  1491.     switch (GET_WM_HSCROLL_CODE(wparam, lparam))
  1492.     {
  1493.         case SB_LINEDOWN:             // One line right.
  1494.             nMove = nLine;
  1495.             break;
  1496.  
  1497.         case SB_LINEUP:               // One line left.
  1498.             nMove = -nLine;
  1499.             break;
  1500.  
  1501.         case SB_PAGEDOWN:             // One page right.
  1502.                 nMove = nPage;
  1503.             break;
  1504.  
  1505.         case SB_PAGEUP:               // One page left.
  1506.             nMove = -nPage;
  1507.             break;
  1508.  
  1509.         case SB_THUMBPOSITION:        // Absolute position.
  1510.             nMove = GET_WM_HSCROLL_POS(wparam, lparam) - nPos;
  1511.             break;
  1512.  
  1513.         default:                      // No change.
  1514.                 nMove = 0;
  1515.             break;
  1516.     }
  1517.  
  1518.     if (nMove)
  1519.     {
  1520.         nPos += nMove;
  1521.  
  1522.         if (nPos < nMin)
  1523.         {
  1524.             nMove -= nPos - nMin;
  1525.             nPos = nMin;
  1526.           }
  1527.  
  1528.         if (nPos > nMax)
  1529.         {
  1530.             nMove -= nPos - nMax;
  1531.             nPos = nMax;
  1532.         }
  1533.  
  1534.         if (nMove)
  1535.         {
  1536.             SetScrollPos(hwnd, nHorzOrVert, nPos, TRUE);
  1537.  
  1538.                 if (nHorzOrVert == SB_HORZ)
  1539.             {
  1540.                 ScrollWindow(hwnd, -nMove, 0, NULL, NULL);
  1541.  
  1542.                 // set the global variable
  1543.                 cxHorzScrollPos = nPos;
  1544.             }
  1545.             else 
  1546.             {
  1547.                 ScrollWindow(hwnd, 0, -nMove, NULL, NULL);
  1548.  
  1549.                 // set the global variable
  1550.                      cyVertScrollPos = nPos;
  1551.             }
  1552.         }
  1553.     }
  1554.     return 0;
  1555. }
  1556.  
  1557. UINT BitmapWidth(HBITMAP hbm)
  1558. {
  1559.     BITMAP bm;
  1560.  
  1561.     if (!hbm)
  1562.         return 0;
  1563.         
  1564.     GetObject(hbm, sizeof(bm), &bm);    
  1565.     return bm.bmWidth;
  1566. }  
  1567.  
  1568. UINT BitmapHeight(HBITMAP hbm)
  1569. {
  1570.     BITMAP bm;
  1571.  
  1572.     if (!hbm)
  1573.         return 0;
  1574.         
  1575.     GetObject(hbm, sizeof(bm), &bm);    
  1576.     return bm.bmHeight;
  1577. }
  1578.