home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c480 / 17.ddi / SAMPLES / DIBVIEW / CHILD.C_ / CHILD.C
Encoding:
C/C++ Source or Header  |  1993-02-08  |  63.2 KB  |  2,240 lines

  1. /*************************************************************************
  2.  
  3.       File:  CHILD.C
  4.  
  5.    Purpose:  Contains the routines to implement displaying a bitmap in
  6.              an MDI child window.  Each window has its own set of
  7.              information stored in its window words which identifies
  8.              the bitmap to be displayed, etc..
  9.  
  10.  Functions:  ChildWndProc
  11.              ChildWndCreate
  12.              ChildWndPaint
  13.              ChildWndDestroy
  14.              ChildWndScroll
  15.              ChildWndKeyDown
  16.              ChildWndQueryNewPalette
  17.              ChildWndPaletteChanged
  18.              ChildWndStartAnimate
  19.              ChildWndLeftButton
  20.              ChildWndSize
  21.              SetupScrollBars
  22.              ScrollBarsOff
  23.              GetCurrentMDIWnd
  24.              CurrentDIBPalette
  25.              GetCurrentDIBStretchFlag
  26.              SetCurrentDIBStretchFlag
  27.              ReallyGetClientRect
  28.              Hourglass
  29.              SetMessageToAllChildren
  30.              CloseAllDIBWindows
  31.              TrackMouse
  32.              NormalizeRect
  33.              DrawSelect
  34.              GetCurrentClipRect
  35.              GetCurrentDIBSize
  36.  
  37.   Comments:  Special considerations are made in this module to get
  38.              Windows to handle the system palette correctly for
  39.              child windows (MDI child windows in this case).
  40.  
  41.              Noramlly, an application with the focus has the
  42.              "foreground" palette.  Since this application uses
  43.              multiple palettes (one per bitmap), special handling
  44.              must be used to force all the windows without the focus
  45.              to "background" palettes.
  46.  
  47.              We accomplish this by forcing the "active" child window's
  48.              palette to be the whole application's foreground palette
  49.              (i.e. get a DC for the frame, and realize the child's
  50.              palette in that DC as a foreground palette).
  51.  
  52.              All other times we realize palettes, we realize them
  53.              as background palettes.  If the palette being realized
  54.              was realized as the frame's foreground palette, then
  55.              when it is realized as a background palette, all the
  56.              colors will map to the correct colors!  If the palette
  57.              being realized was not the foreground palette, the colors
  58.              are mapped into the system palette on a "best match"
  59.              basis.
  60.  
  61.    History:   Date      Reason
  62.              6/ 1/91     Created
  63.             12/03/91     Fixed palette handling code.
  64.  
  65. *************************************************************************/
  66.  
  67.  
  68.  
  69. #include "master.h"
  70.  
  71.  
  72.    // Some magic numbers.
  73.  
  74. #define TIMER_ID        1     // Timer ID when palette animating.
  75. #define TIMER_INTERVAL  100   // # of ms between timer ticks when animating.
  76. #define SCROLL_RATIO    4     // WM_VSCROLL scrolls DIB by 1/x of client area.
  77.  
  78.  
  79.  
  80.    // The following defines are the default values for the OPTIONSINFO
  81.    //  structure stored in the child window's INFOSTRUCT (which is a
  82.    //  structure containing information on the child window's bitmap,
  83.    //  and list of options).  See ChildWndCreate() to see how these
  84.    //  are used.
  85.  
  86. #define OPT_DEF_STRETCH   FALSE         // Don't stretch the DIB on display.
  87. #define OPT_DEF_BANDING   TRUE          // Band DIB to printer.
  88. #define OPT_DEF_USE31PRNAPIS FALSE      // Don't use the 3.1 Printing APIs.
  89. #define OPT_DEF_DISP      DISP_USE_DDBS // Display DDBs instead of DIBs.
  90. #define OPT_DEF_PRNSIZE   PRINT_BESTFIT // Print in "best fit" mode.
  91. #define OPT_DEF_PRNSCALEX 1             // X stretch factor = 1 (in PRINT_STRETCH mode)
  92. #define OPT_DEF_PRNSCALEY 1             // Y stretch factor = 1 (in PRINT_STRETCH mode)
  93.  
  94.  
  95.  
  96.    // Some useful macros.
  97.  
  98. #define MAX(a,b)     ((a) > (b) ? (a) : (b))
  99. #define MIN(a,b)     ((a) < (b) ? (a) : (b))
  100.  
  101.  
  102.  
  103.    // Some globals.
  104.  
  105. HWND  hWndAnimate = NULL;     // HWND of currently palette animated DIB.
  106. HWND  hWndClip    = NULL;     // Current Window to be rendered to clipboard.
  107. POINT ptClipSize  = {0,0};    // Size of DIB at time of copy (i.e. was the DIB stretched?)
  108. int   nDIBsOpen   = 0;        // # of MDI child windows currently open.
  109.  
  110.  
  111.    // Local function prototypes.
  112.  
  113. void  ChildWndCreate      (HWND hWnd, LPDIBCREATEINFO lpDIBCreateInfo);
  114. void  ChildWndPaint       (HWND hWnd);
  115. void  ChildWndDestroy     (HWND hWnd);
  116. void  ChildWndScroll      (HWND hWnd, int message, WORD wPos, WORD wScrollType);
  117. BOOL  ChildWndQueryNewPalette (HWND hWnd, HWND hWndFrame);
  118. void  ChildWndPaletteChanged (HWND hWnd);
  119. void  ChildWndStartAnimate(HWND hWnd);
  120. void  ChildWndLeftButton  (HWND hWnd, int x, int y);
  121. DWORD ChildWndKeyDown     (HWND hWnd, WORD wKeyCode, LONG lParam);
  122. void  ChildWndSize        (HWND hWnd);
  123. void  SetupScrollBars     (HWND hWnd, WORD cxDIB, WORD cyDIB);
  124. void  ScrollBarsOff       (HWND hWnd);
  125. void  ReallyGetClientRect (HWND hWnd, LPRECT lpRect);
  126. void  Hourglass           (BOOL bDisplay);
  127. void  TrackMouse          (HWND hWnd, LPRECT lpClipRect, int cxDIB, int cyDIB);
  128. void  NormalizeRect       (LPRECT lpRect);
  129. void  DrawSelect          (HDC hDC, RECT rcClip);
  130.  
  131.  
  132. //---------------------------------------------------------------------
  133. //
  134. // Function:   ChildWndProc
  135. //
  136. // Purpose:    Window procedure for DIB MDI child windows.
  137. //             Handles all messages destined for these windows.
  138. //
  139. // Parms:      hWnd    == Handle to this MDI child window.
  140. //             message == Message for window.
  141. //             wParam  == Depends on message.
  142. //             lParam  == Depends on message.
  143. //
  144. // History:   Date      Reason
  145. //             6/01/91  Created
  146. //            10/15/91  Moved WM_CREATE handler to own function.
  147. //                      Moved WM_DESTROY handler to own function.
  148. //                      Moved WM_PAINT handler to own function.
  149. //                      Moved WM_?SCROLL handler to own function.
  150. //
  151. //---------------------------------------------------------------------
  152.  
  153. long FAR PASCAL __export ChildWndProc (HWND hWnd,
  154.                   UINT message,
  155.                   WPARAM wParam,
  156.                   LPARAM lParam)
  157. {
  158.    switch (message)
  159.       {
  160.          // Window being created, do initialization.  In MDI, the lParam
  161.          //  is a far pointer to a CREATESTRUCT.  The CREATESTRUCT's
  162.          //  lpCreateParams is a far pointer to an MDICREATESTRUCT.
  163.          //  The MDICREATESTRUCT's lParam is an application supplied
  164.          //  LONG.  In DIBView, this LONG is actually a far pointer to
  165.          //  a DIBCREATEINFO structure.  This structure is initialized
  166.          //  by the routine which sent the WM_MDICREATE message to the
  167.          //  MDI frame (in FRAME.C).
  168.  
  169.       case WM_CREATE:
  170.          ChildWndCreate (hWnd,
  171.                          (LPDIBCREATEINFO)
  172.                          ((LPMDICREATESTRUCT)
  173.                           ((LPCREATESTRUCT) lParam)->lpCreateParams)->lParam);
  174.          break;
  175.  
  176.  
  177.  
  178.          // If this window is being activated, simulate a
  179.          //  MYWM_QUERYNEWPALETTE message.
  180.  
  181.       case WM_MDIACTIVATE:
  182.          {
  183.          HWND hWndFrame;
  184.  
  185.          if (wParam)
  186.             {
  187.             hWndFrame = GetParent (GetParent (hWnd));
  188.             SendMessage (hWnd, MYWM_QUERYNEWPALETTE, hWndFrame, 0L);
  189.             }
  190.  
  191.          break;
  192.          }
  193.  
  194.  
  195.  
  196.          // Need to paint, call the paint routine.
  197.  
  198.       case WM_PAINT:
  199.          ChildWndPaint (hWnd);
  200.          break;
  201.  
  202.  
  203.  
  204.          // User's dragging a minimized MDI child, return the cursor to drag.
  205.  
  206.       case WM_QUERYDRAGICON:
  207.          return LoadCursor (hInst, DRAGCURSOR);
  208.  
  209.  
  210.  
  211.          // Window's being destroyed, call the destroy routine.
  212.  
  213.       case WM_DESTROY:
  214.          ChildWndDestroy (hWnd);
  215.          break;
  216.  
  217.  
  218.  
  219.          // Ensure that the clipboard data can be rendered even though
  220.          //  this window is being destroyed.  First open the clipboard.
  221.          //  Then empty what we put there earlier and re-render everything.
  222.  
  223.       case WM_RENDERALLFORMATS:
  224.          {
  225.          if (!OpenClipboard (hWnd))
  226.             break;
  227.  
  228.          EmptyClipboard ();
  229.  
  230.          SendMessage(hWnd, WM_RENDERFORMAT, CF_DIB,     0L);
  231.          SendMessage(hWnd, WM_RENDERFORMAT, CF_BITMAP,  0L);
  232.          SendMessage(hWnd, WM_RENDERFORMAT, CF_PALETTE, 0L);
  233.  
  234.          CloseClipboard ();
  235.          break;
  236.          }
  237.  
  238.  
  239.          // Format the data in the manner requested and pass the handle of
  240.          // the data to the clipboard.
  241.  
  242.       case WM_RENDERFORMAT:
  243.          {
  244.          HANDLE hClipBoardData;
  245.  
  246.          hClipBoardData = RenderFormat (hWndClip, wParam, ptClipSize);
  247.  
  248.          if (hClipBoardData)
  249.             SetClipboardData (wParam, hClipBoardData);
  250.          break;
  251.          }
  252.  
  253.  
  254.          // Window's being scrolled, call the scroll handler.
  255.  
  256.       case WM_HSCROLL:
  257.       case WM_VSCROLL:
  258.          ChildWndScroll (hWnd, message, LOWORD (lParam), wParam);
  259.          break;
  260.  
  261.  
  262.  
  263.          // Keypress -- go handle it.
  264.  
  265.       case WM_KEYDOWN:
  266.          return ChildWndKeyDown (hWnd, wParam, lParam);
  267.  
  268.  
  269.  
  270.          // Window's getting focus, realize our palette.  We set it
  271.          //  up so that the HWND of the frame window is in wParam.
  272.          //  This is so we can realize our palette as the foreground
  273.          //  palette of the *entire* application (Windows is designed
  274.          //  so that the application has one foreground palette -- owned
  275.          //  by the top-level window of the app).  We could realize it
  276.          //  as foreground, and supply our own hWnd, but this can lead
  277.          //  to some weird results...
  278.  
  279.       case MYWM_QUERYNEWPALETTE:
  280.          return ChildWndQueryNewPalette (hWnd, (HWND) wParam);
  281.  
  282.  
  283.  
  284.          // Someone changed the system's palette.  Update our window
  285.          //  to reflect the new palette.
  286.  
  287.       case WM_PALETTECHANGED:
  288.          if (hWnd == (HWND) wParam)
  289.             break;
  290.  
  291.          ChildWndPaletteChanged (hWnd);
  292.          break;
  293.  
  294.  
  295.  
  296.          // User wants to animate palette, call routine to start
  297.          //  animation.
  298.  
  299.       case MYWM_ANIMATE:
  300.          ChildWndStartAnimate (hWnd);
  301.          break;
  302.  
  303.  
  304.  
  305.  
  306.          // Timer went off -- this only happens when we're animating
  307.          //  the palette.
  308.  
  309.       case WM_TIMER:
  310.          {
  311.          HANDLE    hDIBInfo;
  312.          LPDIBINFO lpDIBInfo;
  313.  
  314.          hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  315.  
  316.          if (!hDIBInfo)
  317.             break;
  318.  
  319.          lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  320.  
  321.          MyAnimatePalette (hWnd, lpDIBInfo->hPal);
  322.  
  323.          GlobalUnlock (hDIBInfo);
  324.          break;
  325.          }
  326.  
  327.  
  328.  
  329.          // Restore the DIB's palette after palette animation.  Stop
  330.          //  animation.  Delete what we created for animation.  Also,
  331.          //  we need to re-create the bitmap, since we earlier re-created
  332.          //  it to reflect the animation palette (see MYWM_ANIMATE).
  333.          //  Finally, re-draw the bitmap.
  334.  
  335.       case MYWM_RESTOREPALETTE:
  336.          {
  337.          HANDLE    hDIBInfo;
  338.          LPDIBINFO lpDIBInfo;
  339.  
  340.          SendMessage (hWnd, WM_RBUTTONDOWN, 0, 0L);
  341.  
  342.          hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  343.  
  344.          if (!hDIBInfo)
  345.             break;
  346.  
  347.          lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  348.  
  349.          if (lpDIBInfo->hBitmap)
  350.             DeleteObject (lpDIBInfo->hBitmap);
  351.  
  352.          if (lpDIBInfo->hPal)
  353.             DeleteObject (lpDIBInfo->hPal);
  354.  
  355.          lpDIBInfo->hPal    = CreateDIBPalette (lpDIBInfo->hDIB);
  356.          lpDIBInfo->hBitmap = DIBToBitmap (lpDIBInfo->hDIB, lpDIBInfo->hPal);
  357.  
  358.          GlobalUnlock (hDIBInfo);
  359.  
  360.          InvalidateRect (hWnd, NULL, FALSE);
  361.          break;
  362.          }
  363.  
  364.  
  365.  
  366.  
  367.          // If the user presses the right mouse button and we're
  368.          //  palette animating, stop animating.  The bitmap is
  369.          //  left in it's animated state, as is the palette.  The
  370.          //  restore option must be picked to return the bitmap/
  371.          //  palette to their original states.
  372.  
  373.       case WM_RBUTTONDOWN:
  374.          if (hWndAnimate == hWnd)
  375.             {
  376.             KillTimer (hWnd, TIMER_ID);
  377.             hWndAnimate = NULL;
  378.             }
  379.          break;
  380.  
  381.  
  382.  
  383.          // Left button pressed -- track a clipping rectangle for clipboard.
  384.  
  385.       case WM_LBUTTONDOWN:
  386.           ChildWndLeftButton (hWnd, LOWORD (lParam), HIWORD (lParam));
  387.           break;
  388.  
  389.  
  390.  
  391.  
  392.          // Handle the WM_SIZE message.
  393.          //
  394.          // Note:  This routine calls SetupScrollBars, which can
  395.          //        change the size of the window's client rectangle.
  396.          //        This, in turn, can send a WM_SIZE message to
  397.          //        the window.  An infinite loop occurs because of
  398.          //        this -- therefore, a semaphore is set up to not
  399.          //        allow WM_SIZE to be processed while another
  400.          //        WM_SIZE is still being processed.
  401.  
  402.       case WM_SIZE:
  403.          {
  404.          static BOOL bInSize = FALSE;
  405.  
  406.             // Check the semaphore, return if it's set.
  407.  
  408.          if (bInSize)
  409.             return NULL;
  410.  
  411.          bInSize = TRUE;
  412.          ChildWndSize (hWnd);
  413.          bInSize = FALSE;
  414.          }
  415.  
  416.  
  417.          /*** WM_SIZE falls through to default (necessary for MDI) ****/
  418.  
  419.  
  420.  
  421.  
  422.              // Since the MDI default behavior is a little different,
  423.             // call DefMDIChildProc instead of DefWindowProc().
  424.  
  425.       default:
  426.          return DefMDIChildProc (hWnd, message, wParam, lParam);
  427.       }
  428.  
  429.  
  430.    return NULL;
  431. }
  432.  
  433.  
  434.  
  435.  
  436. //---------------------------------------------------------------------
  437. //
  438. // Function:   ChildWndCreate
  439. //
  440. // Purpose:    Called by ChildWndProc() on WM_CREATE.  Does initial
  441. //             setup of MDI child winodw.
  442. //
  443. //             The lpDIBCreateInfo contains a handle to the DIB to
  444. //             be displayed in this window.  Get information on this
  445. //             DIB, create an INFOSTRUCT, and store the handle to
  446. //             this INFOSTRUCT in this window's window words.  This
  447. //             information is then used extensively on many messages
  448. //             handled by ChildWndProc().
  449. //
  450. //             An OPTIONSINFO structure is in the INFOSTRUCT.  All
  451. //             the options are set by the options dialog (in OPTIONS.C).
  452. //             During creation of the DIB window, options are set to
  453. //             default values.
  454. //
  455. //             Also, on creation, set the focus to this window.
  456. //             And, incremente the # of windows open global variable.
  457. //
  458. // Parms:      hWnd            == Handle to window being created.
  459. //             lpDIBCreateInfo == Far pointer to DIBCREATEINFO structure
  460. //                                passed in during WM_MDICREATE message
  461. //                                (by FRAME.C).
  462. //
  463. // History:   Date      Reason
  464. //
  465. //            10/15/91  Cut code out from WM_CREATE case.
  466. //                      Also cleaned up code, and got rid
  467. //                      of the hDIBCreateInfo handle.
  468. //            10/27/91  Added bPrinterBand to options.
  469. //                      Use #define's for options.
  470. //            10/28/91  Added bUse31PrintAPIs
  471. //
  472. //---------------------------------------------------------------------
  473.  
  474. void ChildWndCreate (HWND hWnd, LPDIBCREATEINFO lpDIBCreateInfo)
  475. {
  476.    HANDLE          hDIB = NULL;
  477.    LPSTR           lpDIB;
  478.    HANDLE          hDIBInfo = NULL;
  479.    LPDIBINFO       lpDIBInfo;
  480.    DWORD           dwDIBHeight, dwDIBWidth;
  481.    WORD            wBPP, wCompression;
  482.    char            szFileName [129];
  483.    char            szTitleBuf[160];
  484.  
  485.    hDIB = lpDIBCreateInfo->hDIB;
  486.    lstrcpy (szFileName, lpDIBCreateInfo->szFileName);
  487.  
  488.  
  489.       // Get some information about the DIB.  Some of the info
  490.       //  is obtained from within the header (be it a BITMAPINFOHEADER
  491.       //  or a BITMAPCOREHEADER).
  492.  
  493.    lpDIB       = GlobalLock (hDIB);
  494.    dwDIBHeight = DIBHeight (lpDIB);
  495.    dwDIBWidth  = DIBWidth (lpDIB);
  496.    if (IS_WIN30_DIB (lpDIB))
  497.       {
  498.       wCompression = (WORD) ((LPBITMAPINFOHEADER) lpDIB)->biCompression;
  499.       wBPP = ((LPBITMAPINFOHEADER) lpDIB)->biBitCount;
  500.       }
  501.    else
  502.       {
  503.       wCompression = BI_PM;
  504.       wBPP = ((LPBITMAPCOREHEADER) lpDIB)->bcBitCount;
  505.       }
  506.    GlobalUnlock (hDIB);
  507.  
  508.  
  509.       // Allocate room for the DIBINFO structure and fill it in.
  510.  
  511.    if (hDIB)
  512.       hDIBInfo = GlobalAlloc (GHND, sizeof (DIBINFO));
  513.  
  514.    if (hDIBInfo)
  515.       {
  516.       lpDIBInfo                = (LPDIBINFO) GlobalLock (hDIBInfo);
  517.       lpDIBInfo->hDIB          = hDIB;
  518.       lpDIBInfo->hPal          = CreateDIBPalette (hDIB);
  519.       lpDIBInfo->hBitmap       = DIBToBitmap (hDIB, lpDIBInfo->hPal);
  520.  
  521.       lpDIBInfo->wDIBType      = wCompression;
  522.       lpDIBInfo->wDIBBits      = wBPP;
  523.       lpDIBInfo->wDIBWidth     = (WORD) dwDIBWidth;
  524.       lpDIBInfo->wDIBHeight    = (WORD) dwDIBHeight;
  525.  
  526.       lpDIBInfo->rcClip.left   = 0;
  527.       lpDIBInfo->rcClip.right  = 0;
  528.       lpDIBInfo->rcClip.top    = 0;
  529.       lpDIBInfo->rcClip.bottom = 0;
  530.  
  531.       lstrcpy (lpDIBInfo->szFileName, szFileName);
  532.  
  533.       lpDIBInfo->Options.bStretch        = OPT_DEF_STRETCH;
  534.       lpDIBInfo->Options.bPrinterBand    = OPT_DEF_BANDING;
  535.       lpDIBInfo->Options.bUse31PrintAPIs = OPT_DEF_USE31PRNAPIS;
  536.       lpDIBInfo->Options.wDispOption     = OPT_DEF_DISP;
  537.       lpDIBInfo->Options.wPrintOption    = OPT_DEF_PRNSIZE;
  538.       lpDIBInfo->Options.wXScale         = OPT_DEF_PRNSCALEX;
  539.       lpDIBInfo->Options.wYScale         = OPT_DEF_PRNSCALEY;
  540.  
  541.       wsprintf((LPSTR)szTitleBuf, (LPSTR)"%s (%ld x %ld x %d BPP)",
  542.                (LPSTR) szFileName, dwDIBWidth, dwDIBHeight, wBPP);
  543.  
  544.       SetWindowText (hWnd, (LPSTR) szTitleBuf);
  545.  
  546.       GlobalUnlock (hDIBInfo);
  547.       }
  548.    else
  549.       DIBError (ERR_MEMORY);
  550.  
  551.  
  552.       // Set the window word for the handle to the DIBINFO structure.
  553.  
  554.    SetWindowWord (hWnd, WW_DIB_HINFO, hDIBInfo);
  555.  
  556.  
  557.       // On initial creation, focus isn't set to us, so set it
  558.       //  explicitly.
  559.  
  560.    SetFocus (hWnd);
  561.  
  562.  
  563.       // Increment the # of DIBs open variable and insure that the
  564.       //  "Window" pull down menu is not grayed.
  565.  
  566.    nDIBsOpen++;
  567.    EnableWindowAndOptionsMenus (TRUE);
  568. }
  569.  
  570.  
  571.  
  572.  
  573.  
  574. //---------------------------------------------------------------------
  575. //
  576. // Function:   ChildWndPaint
  577. //
  578. // Purpose:    Called by ChildWndProc() on WM_PAINT.  Does all paints
  579. //             for this MDI child window.
  580. //
  581. //             Reads position of scroll bars to find out what part
  582. //             of the DIB to display.
  583. //
  584. //             Checks the stretching flag in the DIBINFO structure for
  585. //             this window to see if we are stretching to the window
  586. //             (if we're iconic, we always stretch to a tiny bitmap).
  587. //
  588. //             Selects/Realizes the palette as a background palette.
  589. //             ChildWndQueryNewPalette realized it already as the
  590. //             foreground palette if this window is the active MDI
  591. //             child.
  592. //
  593. //             Calls the appropriate paint routine depending on the
  594. //             option set for this window (i.e. DIB, DDB, or SetDIBits).
  595. //
  596. //             Draws the selection rectangle for copying to the
  597. //             clipboard.
  598. //
  599. // Parms:      hWnd == Handle to window being painted.
  600. //
  601. // History:   Date      Reason
  602. //
  603. //            10/15/91  Cut code out from WM_PAINT case.
  604. //            12/03/91  Always force SelectPalette() to a
  605. //                        background palette.  If it was
  606. //                        the foreground palette, it would
  607. //                        have been realized during
  608. //                        WM_QUERYNEWPALETTE.
  609. //
  610. //---------------------------------------------------------------------
  611.  
  612. void ChildWndPaint (HWND hWnd)
  613. {
  614.    HDC         hDC;
  615.    PAINTSTRUCT ps;
  616.    int         xScroll, yScroll;
  617.    HPALETTE    hOldPal = NULL;
  618.    RECT        rectClient, rectDDB;
  619.    BOOL        bStretch;
  620.    HANDLE      hDIBInfo;
  621.    LPDIBINFO   lpDIBInfo;
  622.    BITMAP      Bitmap;
  623.  
  624.    Hourglass (TRUE);
  625.  
  626.    hDC      = BeginPaint (hWnd, &ps);
  627.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  628.    xScroll  = GetScrollPos  (hWnd, SB_HORZ);
  629.    yScroll  = GetScrollPos  (hWnd, SB_VERT);
  630.  
  631.    if (!hDIBInfo)
  632.       goto ABORTPAINT;
  633.  
  634.    lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  635.  
  636.    if (!lpDIBInfo->hDIB || !lpDIBInfo->hBitmap)
  637.       {
  638.       GlobalUnlock (hDIBInfo);
  639.       goto ABORTPAINT;
  640.       }
  641.  
  642.    bStretch = lpDIBInfo->Options.bStretch;
  643.  
  644.  
  645.       // When we're iconic, we'll always stretch the DIB
  646.       //  to our icon.  Otherwise, we'll use the stretching
  647.       //  option the user picked.
  648.  
  649.    if (IsIconic (hWnd))
  650.       bStretch = TRUE;
  651.    else
  652.       bStretch = lpDIBInfo->Options.bStretch;
  653.  
  654.  
  655.       // Set up the scroll bars appropriately.
  656.  
  657.    if (bStretch)
  658.       ScrollBarsOff (hWnd);
  659.    else
  660.       SetupScrollBars (hWnd, lpDIBInfo->wDIBWidth, lpDIBInfo->wDIBHeight);
  661.  
  662.  
  663.       // Set up the necessary rectangles -- i.e. the rectangle
  664.       //  we're rendering into, and the rectangle in the DIB.
  665.  
  666.    GetClientRect (hWnd, &rectClient);
  667.    GetObject (lpDIBInfo->hBitmap, sizeof (Bitmap), (LPSTR) &Bitmap);
  668.  
  669.    if (bStretch)
  670.       {
  671.       rectDDB.left   = 0;
  672.       rectDDB.top    = 0;
  673.       rectDDB.right  = Bitmap.bmWidth;
  674.       rectDDB.bottom = Bitmap.bmHeight;
  675.       }
  676.    else
  677.       {
  678.       rectDDB.left   = xScroll;
  679.       rectDDB.top    = yScroll;
  680.       rectDDB.right  = xScroll + rectClient.right - rectClient.left;
  681.       rectDDB.bottom = yScroll + rectClient.bottom - rectClient.top;
  682.  
  683.       if (rectDDB.right > Bitmap.bmWidth)
  684.          {
  685.          int dx;
  686.  
  687.          dx = Bitmap.bmWidth - rectDDB.right;
  688.  
  689.          rectDDB.right     += dx;
  690.          rectClient.right  += dx;
  691.          }
  692.  
  693.       if (rectDDB.bottom > Bitmap.bmHeight)
  694.          {
  695.          int dy;
  696.  
  697.          dy = Bitmap.bmHeight - rectDDB.bottom;
  698.  
  699.          rectDDB.bottom    += dy;
  700.          rectClient.bottom += dy;
  701.          }
  702.       }
  703.  
  704.  
  705.       // Setup the palette.
  706.  
  707.    if (lpDIBInfo->hPal)
  708.       hOldPal = SelectPalette (hDC, lpDIBInfo->hPal, TRUE);
  709.  
  710.    RealizePalette (hDC);
  711.  
  712.  
  713.       // Go do the actual painting.
  714.  
  715.    switch (lpDIBInfo->Options.wDispOption)
  716.       {
  717.       case DISP_USE_DIBS:
  718.          DIBPaint (hDC, &rectClient, lpDIBInfo->hDIB, &rectDDB,
  719.                      lpDIBInfo->hPal);
  720.          break;
  721.  
  722.  
  723.       case DISP_USE_SETDIBITS:
  724.          SetDIBitsPaint (hDC, &rectClient, lpDIBInfo->hDIB, &rectDDB,
  725.                            lpDIBInfo->hPal);
  726.          break;
  727.  
  728.  
  729.       case DISP_USE_DDBS:
  730.       default:
  731.          DDBPaint (hDC, &rectClient, lpDIBInfo->hBitmap, &rectDDB,
  732.                      lpDIBInfo->hPal);
  733.          break;
  734.       }
  735.  
  736.  
  737.       // Draw the clipboard selection rubber-band.
  738.  
  739.    SetWindowOrg (hDC,
  740.                   GetScrollPos (hWnd, SB_HORZ),
  741.                   GetScrollPos (hWnd, SB_VERT));
  742.    DrawSelect (hDC, lpDIBInfo->rcClip);
  743.  
  744.  
  745.    if (hOldPal)
  746.       SelectPalette (hDC, hOldPal, FALSE);
  747.  
  748.    GlobalUnlock (hDIBInfo);
  749.  
  750. ABORTPAINT:
  751.  
  752.    EndPaint (hWnd, &ps);
  753.  
  754.    Hourglass (FALSE);
  755. }
  756.  
  757.  
  758.  
  759. //---------------------------------------------------------------------
  760. //
  761. // Function:   ChildWndDestroy
  762. //
  763. // Purpose:    Called by ChildWndProc() on WM_DESTROY.  Window is
  764. //             being destroyed, do all necessary cleanup.
  765. //
  766. //             Window's going away, free up the DIB, DDB, Palette, and
  767. //             DIBINFO structure.
  768. //
  769. //             If we're palette animating, kill the timer and free
  770. //             up animation palette.
  771. //
  772. //             If we have the clipboard, send the WM_RENDERALLFORMATS to
  773. //             our window (Windows will only send it to our app if the
  774. //             main window owns the clipboard;  in this app, the MDI child
  775. //             window owns the clipboard).
  776. //
  777. //             Decrement the # of DIB windows open global variable.
  778. //
  779. //
  780. // Parms:      hWnd == Handle to window being destroyed.
  781. //
  782. // History:   Date      Reason
  783. //
  784. //            10/15/91  Cut code out from WM_DESTROY case.
  785. //
  786. //---------------------------------------------------------------------
  787.  
  788. void ChildWndDestroy (HWND hWnd)
  789. {
  790.    HANDLE    hDIBInfo;
  791.    LPDIBINFO lpDIBInfo;
  792.  
  793.  
  794.       // If we have the clipboard, render all our formats
  795.       //  now.
  796.  
  797.    if (hWnd == GetClipboardOwner ())
  798.       {
  799.       SendMessage (hWnd, WM_RENDERALLFORMATS, 0, 0L);
  800.       hWndClip = NULL;
  801.       }
  802.  
  803.  
  804.       // Free up resources connected to this window.
  805.  
  806.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  807.    if (hDIBInfo)
  808.       {
  809.       lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  810.  
  811.       if (lpDIBInfo->hDIB)
  812.          GlobalFree (lpDIBInfo->hDIB);
  813.  
  814.       if (lpDIBInfo->hPal)
  815.          DeleteObject (lpDIBInfo->hPal);
  816.  
  817.       if (lpDIBInfo->hBitmap)
  818.          DeleteObject (lpDIBInfo->hBitmap);
  819.  
  820.       GlobalUnlock (hDIBInfo);
  821.       GlobalFree (hDIBInfo);
  822.  
  823.       SetWindowWord (hWnd, WW_DIB_HINFO, NULL);
  824.  
  825.       if (--nDIBsOpen == 0)
  826.          EnableWindowAndOptionsMenus (FALSE);
  827.       }
  828.  
  829.  
  830.       // If we're animating, turn off the timer.
  831.  
  832.    if (hWndAnimate == hWnd)
  833.       {
  834.       KillTimer (hWnd, TIMER_ID);
  835.       hWndAnimate = NULL;
  836.       }
  837. }
  838.  
  839.  
  840.  
  841. //---------------------------------------------------------------------
  842. //
  843. // Function:   ChildWndScroll
  844. //
  845. // Purpose:    Called by ChildWndProc() on WM_HSCROLL and WM_VSCROLL.
  846. //             Window needs to be scrolled (user has clicked on one
  847. //             of the scroll bars.
  848. //
  849. //             Does scrolling in both horiziontal and vertical directions.
  850. //             Note that the variables are all named as if we were
  851. //             doing a horizontal scroll.  However, if we're doing a
  852. //             vertical scroll, they are initialized to the appropriate
  853. //             values for a vertical scroll.
  854. //
  855. //             If we scroll by one (i.e. user clicks on one of the
  856. //             scrolling arrows), we scroll the window by 1/SCROLL_RATIO
  857. //             of the client area.  In other words, if SCROLL_RATION==4,
  858. //             then we move the client area over a 1/4 of the width/height
  859. //             of the screen.
  860. //
  861. //             If the user is paging up/down we move a full client area's
  862. //             worth.
  863. //
  864. //             If the user moves the thumb to an absolute position, we
  865. //             just move there.
  866. //
  867. //             ScrollWindow/re-painting do the actual work of scrolling.
  868. //
  869. // Parms:      hWnd        == Handle to window being scrolled.
  870. //             message     == Message being handled (WM_HSCROLL or WM_VSCROLL)
  871. //             wPos        == Thumb position (only valid for SB_THUMBPOSITION
  872. //                            and SB_THUMBTRACK).
  873. //             wScrollType == wParam to WM_SCROLL (one of the SB_* constants)
  874. //
  875. // History:   Date      Reason
  876. //
  877. //            10/15/91  Cut code out from WM_?SCROLL case.
  878. //
  879. //---------------------------------------------------------------------
  880.  
  881. void ChildWndScroll (HWND hWnd, int message, WORD wPos, WORD wScrollType)
  882. {
  883.    int  xBar;                       // Where scrollbar is now.
  884.    int  nMin;                       // Minumum scroll bar value.
  885.    int  nMax;                       // Maximum scroll bar value.
  886.    int  dx;                         // How much to move.
  887.    int  nOneUnit;                   // # of pixels for LINEUP/LINEDOWN
  888.    int  cxClient;                   // Width of client area.
  889.    int  nHorzOrVert;                // Doing the horizontal or vertical?
  890.    RECT rect;                       // Client area.
  891.  
  892.  
  893.    GetClientRect (hWnd, &rect);
  894.  
  895.    if (message == WM_HSCROLL)
  896.       {
  897.       nHorzOrVert = SB_HORZ;
  898.       cxClient    = rect.right - rect.left;
  899.       }
  900.    else
  901.       {
  902.       nHorzOrVert = SB_VERT;
  903.       cxClient    = rect.bottom - rect.top;
  904.       }
  905.  
  906.       // One a SB_LINEUP/SB_LINEDOWN we will move the DIB by
  907.       //  1/SCROLL_RATIO of the client area (i.e. if SCROLL_RATIO
  908.       //  is 4, it will scroll the DIB a quarter of the client
  909.       //  area's height or width.
  910.  
  911.    nOneUnit = cxClient / SCROLL_RATIO;
  912.    if (!nOneUnit)
  913.       nOneUnit = 1;
  914.  
  915.    xBar = GetScrollPos (hWnd, nHorzOrVert);
  916.    GetScrollRange (hWnd, nHorzOrVert, &nMin, &nMax);
  917.  
  918.    switch (wScrollType)
  919.       {
  920.       case SB_LINEDOWN:             // One line right.
  921.          dx = nOneUnit;
  922.          break;
  923.  
  924.       case SB_LINEUP:               // One line left.
  925.          dx = -nOneUnit;
  926.          break;
  927.  
  928.       case SB_PAGEDOWN:             // One page right.
  929.          dx = cxClient;
  930.          break;
  931.  
  932.       case SB_PAGEUP:               // One page left.
  933.          dx = -cxClient;
  934.          break;
  935.  
  936.       case SB_THUMBPOSITION:        // Absolute position.
  937.          dx = wPos - xBar;
  938.          break;
  939.  
  940.       default:                      // No change.
  941.          dx = 0;
  942.          break;
  943.       }
  944.  
  945.    if (dx)
  946.       {
  947.       xBar += dx;
  948.  
  949.       if (xBar < nMin)
  950.          {
  951.          dx  -= xBar - nMin;
  952.          xBar = nMin;
  953.          }
  954.  
  955.       if (xBar > nMax)
  956.          {
  957.          dx  -= xBar - nMax;
  958.          xBar = nMax;
  959.          }
  960.  
  961.       if (dx)
  962.          {
  963.          SetScrollPos (hWnd, nHorzOrVert, xBar, TRUE);
  964.  
  965.          if (nHorzOrVert == SB_HORZ)
  966.             ScrollWindow (hWnd, -dx, 0, NULL, NULL);
  967.          else
  968.             ScrollWindow (hWnd, 0, -dx, NULL, NULL);
  969.  
  970.          UpdateWindow (hWnd);
  971.          }
  972.       }
  973. }
  974.  
  975.  
  976.  
  977. //---------------------------------------------------------------------
  978. //
  979. // Function:   ChildWndKeyDown
  980. //
  981. // Purpose:    Called by ChildWndProc() on WM_KEYDOWN.  Keyboard interface
  982. //             for MDI DIB Child window.
  983. //
  984. //             Keyboard interface.  Handles scrolling around the DIB
  985. //             using the keypad, and translates ESC's into WM_RBUTTONDOWN
  986. //             messages (to stop any palette animation in progress).
  987. //
  988. //             The numeric keypad/arrows are translated into scroll
  989. //             bar messages.
  990. //
  991. // Parms:      hWnd     == Handle to window where key was pressed.
  992. //             wKeyCode == Key code pressed (wParam to WM_KEYDOWN -- one
  993. //                         of the VK_* constants)
  994. //             lParam   == lParam for WM_KEYDOWN.
  995. //
  996. // History:   Date      Reason
  997. //
  998. //            10/15/91  Cut code out from WM_KEYDOWN case.
  999. //
  1000. //---------------------------------------------------------------------
  1001.  
  1002. DWORD ChildWndKeyDown (HWND hWnd, WORD wKeyCode, LONG lParam)
  1003. {
  1004.    unsigned uMsg;
  1005.    WORD     wSB;
  1006.  
  1007.    switch (wKeyCode)
  1008.       {
  1009.       case VK_ESCAPE:
  1010.          SendMessage (hWnd, WM_RBUTTONDOWN, 0, 0L);
  1011.          break;
  1012.  
  1013.       case VK_UP:
  1014.          uMsg = WM_VSCROLL;
  1015.          wSB  = SB_LINEUP;
  1016.          break;
  1017.  
  1018.       case VK_DOWN:
  1019.          uMsg = WM_VSCROLL;
  1020.          wSB  = SB_LINEDOWN;
  1021.          break;
  1022.  
  1023.       case VK_LEFT:
  1024.          uMsg = WM_HSCROLL;
  1025.          wSB  = SB_LINEUP;
  1026.          break;
  1027.  
  1028.       case VK_RIGHT:
  1029.          uMsg = WM_HSCROLL;
  1030.          wSB  = SB_LINEDOWN;
  1031.          break;
  1032.  
  1033.       case VK_NUMPAD9:
  1034.          uMsg = WM_VSCROLL;
  1035.          wSB  = SB_PAGEUP;
  1036.          break;
  1037.  
  1038.       case VK_NUMPAD3:
  1039.          uMsg = WM_VSCROLL;
  1040.          wSB  = SB_PAGEDOWN;
  1041.          break;
  1042.  
  1043.       case VK_NUMPAD7:
  1044.          uMsg = WM_HSCROLL;
  1045.          wSB  = SB_PAGEUP;
  1046.          break;
  1047.  
  1048.       case VK_NUMPAD1:
  1049.          uMsg = WM_HSCROLL;
  1050.          wSB  = SB_PAGEDOWN;
  1051.          break;
  1052.  
  1053.       default:
  1054.          return DefMDIChildProc (hWnd, uMsg, wSB, lParam);
  1055.       }
  1056.  
  1057.    return SendMessage (hWnd, uMsg, wSB, 0L);
  1058. }
  1059.  
  1060.  
  1061.  
  1062.  
  1063. //---------------------------------------------------------------------
  1064. //
  1065. // Function:   ChildWndQueryNewPalette
  1066. //
  1067. // Purpose:    Called by ChildWndProc() on WM_QUERYNEWPALETTE.
  1068. //
  1069. //             We get this message when an MDI child is getting
  1070. //             focus (by hocus pockus in FRAME.C, and by passing
  1071. //             this message when we get WM_MDIACTIVATE).  Normally
  1072. //             this message is passed only to the top level window(s)
  1073. //             of an application.
  1074. //
  1075. //             We want this window to have the foreground palette when this
  1076. //             happens, so we select and realize the palette as
  1077. //             a foreground palette (of the frame Window).  Then make
  1078. //             sure the window repaints, if necessary.
  1079. //
  1080. // Parms:      hWnd      == Handle to window getting WM_QUERYNEWPALETTE.
  1081. //             hWndFrame == Handle to the frame window (i.e. the top-level
  1082. //                            window of this app.
  1083. //
  1084. // History:   Date      Reason
  1085. //
  1086. //            10/15/91  Cut code out from WM_QUERYNEWPALETTE case.
  1087. //            12/03/91  Added hWndFrame parameter, realization
  1088. //                      of palette as palette of frame window,
  1089. //                      and updating the window only if the
  1090. //                      palette changed.
  1091. //
  1092. //---------------------------------------------------------------------
  1093.  
  1094. BOOL ChildWndQueryNewPalette (HWND hWnd, HWND hWndFrame)
  1095. {
  1096.    HPALETTE  hOldPal;
  1097.    HDC       hDC;
  1098.    HANDLE    hDIBInfo;
  1099.    LPDIBINFO lpDIBInfo;
  1100.    int       nColorsChanged;
  1101.  
  1102.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  1103.  
  1104.    if (!hDIBInfo)
  1105.       return FALSE;
  1106.  
  1107.    lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  1108.  
  1109.    if (!lpDIBInfo->hPal)
  1110.       {
  1111.       GlobalUnlock (hDIBInfo);
  1112.       return FALSE;
  1113.       }
  1114.  
  1115.  
  1116.       // We're going to make our palette the foreground palette for
  1117.       //  this application.  Window's palette manager expects the
  1118.       //  top-level window of the application to have the palette,
  1119.       //  so, we get a DC for the frame here!
  1120.  
  1121.    hDC     = GetDC (hWndFrame);
  1122.    hOldPal = SelectPalette (hDC, lpDIBInfo->hPal, FALSE);
  1123.  
  1124.    nColorsChanged = RealizePalette (hDC);
  1125.  
  1126.    if (nColorsChanged)
  1127.       InvalidateRect (hWnd, NULL, FALSE);
  1128.  
  1129.    if (hOldPal)
  1130.       SelectPalette (hDC, hOldPal, FALSE);
  1131.  
  1132.    ReleaseDC (hWndFrame, hDC);
  1133.  
  1134.    GlobalUnlock (hDIBInfo);
  1135.  
  1136.    return (nColorsChanged != 0);
  1137. }
  1138.  
  1139.  
  1140.  
  1141.  
  1142. //---------------------------------------------------------------------
  1143. //
  1144. // Function:   ChildWndPaletteChanged
  1145. //
  1146. // Purpose:    Called by ChildWndProc() on WM_PALETTECHANGED.
  1147. //
  1148. //             WM_PALETTECHANGED messages are passed to all MDI
  1149. //             children by the frame window (in FRAME.C).  Normally,
  1150. //             these messages are only sent to the top-level window
  1151. //             in an application.
  1152. //
  1153. //             On a palette changed, we want to realize this window's
  1154. //             palette.  We realize it always as a background palette.
  1155. //             See the comments section at the top of this file for
  1156. //             an explanation why.
  1157. //
  1158. // Parms:      hWnd == Handle to window getting WM_PALETTECHANGED.
  1159. //
  1160. // History:   Date      Reason
  1161. //
  1162. //            10/15/91  Cut code out from WM_PALETTECHANGED case.
  1163. //            12/03/91  Always force SelectPalette() to a
  1164. //                        background palette.  If it was
  1165. //                        the foreground palette, it would
  1166. //                        have been realized during
  1167. //                         WM_QUERYNEWPALETTE.
  1168. //
  1169. //---------------------------------------------------------------------
  1170.  
  1171. void ChildWndPaletteChanged (HWND hWnd)
  1172. {
  1173.    HPALETTE  hOldPal;
  1174.    HDC       hDC;
  1175.    HANDLE    hDIBInfo;
  1176.    LPDIBINFO lpDIBInfo;
  1177.  
  1178.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  1179.  
  1180.    if (!hDIBInfo)
  1181.       return;
  1182.  
  1183.    lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  1184.  
  1185.    if (!lpDIBInfo->hPal)
  1186.       {
  1187.       GlobalUnlock (hDIBInfo);
  1188.       return;
  1189.       }
  1190.  
  1191.    hDC     = GetDC (hWnd);
  1192.    hOldPal = SelectPalette (hDC, lpDIBInfo->hPal, TRUE);
  1193.  
  1194.    GlobalUnlock (hDIBInfo);
  1195.  
  1196.    RealizePalette (hDC);
  1197.    UpdateColors (hDC);
  1198.  
  1199.    if (hOldPal)
  1200.       SelectPalette (hDC, hOldPal, FALSE);
  1201.  
  1202.    ReleaseDC (hWnd, hDC);
  1203. }
  1204.  
  1205.  
  1206.  
  1207.  
  1208. //---------------------------------------------------------------------
  1209. //
  1210. // Function:   ChildWndStartAnimate
  1211. //
  1212. // Purpose:    Called by ChildWndProc() on MYWM_ANIMATE.
  1213. //
  1214. //             Animate this DIB's palette.  First do some setup (see if
  1215. //             we're already doing animation, and start a timer).  Then
  1216. //             Create a new palette with the PC_RESERVED flag so it can
  1217. //             be animated.  Then re-create the bitmap so it uses this
  1218. //             new palette.  Finally, set our window words and re-draw
  1219. //             the bitmap.
  1220. //
  1221. //
  1222. // Parms:      hWnd == Handle to window getting MYWM_ANIMATE.
  1223. //
  1224. // History:   Date      Reason
  1225. //
  1226. //            10/15/91  Cut code out from MYWM_ANIMATE case.
  1227. //
  1228. //---------------------------------------------------------------------
  1229.  
  1230. void ChildWndStartAnimate (HWND hWnd)
  1231. {
  1232.    HPALETTE  hNewPal;
  1233.    HANDLE    hDIBInfo;
  1234.    LPDIBINFO lpDIBInfo;
  1235.  
  1236.  
  1237.       // Don't allow more than one window to animate at a time.
  1238.  
  1239.    if (hWndAnimate)
  1240.       {
  1241.       DIBError (ERR_ANIMATE);
  1242.       return;
  1243.       }
  1244.  
  1245.  
  1246.       // Set a timer to animate on.
  1247.  
  1248.    if (!SetTimer (hWnd, TIMER_ID, TIMER_INTERVAL, NULL))
  1249.       {
  1250.       DIBError (ERR_NOTIMERS);
  1251.       return;
  1252.       }
  1253.  
  1254.  
  1255.       // Remember who's animating, get the palette for this window,
  1256.       //  copy it changing its flags to PC_RESERVED (so each palette
  1257.       //  entry can be animated).
  1258.  
  1259.    hWndAnimate = hWnd;
  1260.    hDIBInfo    = GetWindowWord (hWnd, WW_DIB_HINFO);
  1261.  
  1262.    if (!hDIBInfo)
  1263.       return;
  1264.  
  1265.    lpDIBInfo   = (LPDIBINFO) GlobalLock (hDIBInfo);
  1266.    hNewPal     = CopyPalForAnimation (lpDIBInfo->hPal);
  1267.  
  1268.  
  1269.       // Delete the old device dependent bitmap, and palette.  Device
  1270.       //  dependent bitmaps rely on colors mapping to the exact same
  1271.       //  place in the system palette (for speed reasons).  Se we
  1272.       //  changed the palette flags to PC_RESERVED, the palette entries
  1273.       //  might not map to the same palette entries.  Therefore, it is
  1274.       //  necessary to create a new device dependent bitmap here!
  1275.  
  1276.    if (lpDIBInfo->hBitmap)
  1277.       DeleteObject (lpDIBInfo->hBitmap);
  1278.  
  1279.    if (lpDIBInfo->hPal)
  1280.       DeleteObject (lpDIBInfo->hPal);
  1281.  
  1282.    lpDIBInfo->hBitmap = DIBToBitmap (lpDIBInfo->hDIB, hNewPal);
  1283.    lpDIBInfo->hPal    = hNewPal;
  1284.  
  1285.    GlobalUnlock (hDIBInfo);
  1286.  
  1287.    InvalidateRect (hWnd, NULL, FALSE);
  1288. }
  1289.  
  1290.  
  1291.  
  1292. //---------------------------------------------------------------------
  1293. //
  1294. // Function:   ChildWndLeftButton
  1295. //
  1296. // Purpose:    Called by ChildWndProc() on WM_LBUTTONDOWN.
  1297. //
  1298. //             If the user presses the left button, erase the currently
  1299. //             selected rectangle.  Then, start drawing a
  1300. //             rectangle (for the area of the DIB to be put in the
  1301. //             clipboard on an Edit/Paste operation).
  1302. //
  1303. //
  1304. // Parms:      hWnd == Handle to window getting WM_LBUTTONDOWN.
  1305. //
  1306. // History:   Date      Reason
  1307. //
  1308. //            10/15/91  Cut code out from WM_LBUTTONDOWN case.
  1309. //
  1310. //---------------------------------------------------------------------
  1311.  
  1312. void ChildWndLeftButton (HWND hWnd, int x, int y)
  1313. {
  1314.    HANDLE    hDIBInfo;
  1315.    LPDIBINFO lpDIBInfo;
  1316.    HDC       hDC;
  1317.    RECT      rcClip;
  1318.    int       cxDIB, cyDIB;
  1319.  
  1320.  
  1321.       // Find the old clip rectangle and erase it.
  1322.  
  1323.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  1324.  
  1325.    if (!hDIBInfo)
  1326.       return;
  1327.  
  1328.    hDC       = GetDC (hWnd);
  1329.    lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  1330.    SetWindowOrg (hDC,
  1331.                  GetScrollPos (hWnd, SB_HORZ),
  1332.                  GetScrollPos (hWnd, SB_VERT));
  1333.    DrawSelect (hDC, lpDIBInfo->rcClip);
  1334.    ReleaseDC (hWnd, hDC);
  1335.  
  1336.  
  1337.       // Determine the DIB's extents.  This is different than the
  1338.       //  DIB's height/width when the DIB's stretched.
  1339.  
  1340.    if (lpDIBInfo->Options.bStretch)
  1341.       {
  1342.       RECT rcClient;
  1343.  
  1344.       GetClientRect (hWnd, &rcClient);
  1345.       cxDIB = rcClient.right;
  1346.       cyDIB = rcClient.bottom;
  1347.       }
  1348.    else
  1349.       {
  1350.       cxDIB = lpDIBInfo->wDIBWidth;
  1351.       cyDIB = lpDIBInfo->wDIBHeight;
  1352.       }
  1353.  
  1354.  
  1355.       // Start a new clip rectangle.  Track the rubber band. Rubber
  1356.       //  band won't be allowed to extend past the extents of the
  1357.       //  DIB.
  1358.  
  1359.    rcClip.top  = y;
  1360.    rcClip.left = x;
  1361.    TrackMouse (hWnd, &rcClip, cxDIB, cyDIB);
  1362.  
  1363.  
  1364.       // Store the new clipboard coordinates.
  1365.  
  1366.    lpDIBInfo->rcClip = rcClip;
  1367.    GlobalUnlock (hDIBInfo);
  1368. }
  1369.  
  1370.  
  1371.  
  1372.  
  1373. //---------------------------------------------------------------------
  1374. //
  1375. // Function:   ChildWndSize
  1376. //
  1377. // Purpose:    Called by ChildWndProc() on WM_SIZE.
  1378. //
  1379. //             When the window is sized -- set up the scroll bars.
  1380. //             Also, if we're in "stretch to window" mode, the entire
  1381. //             client area must be repainted.
  1382. //
  1383. //             The window will be repainted if the new size, combined
  1384. //             with the current scroll bar positions would create "white
  1385. //             space at the left or bottom of the window.  For example,
  1386. //             if the DIB is 100x100, the window _was_ 50x50, the new
  1387. //             size of the window is 75x75, and the current window is
  1388. //             scrolled 50 units to the right; then, if the current
  1389. //             scroll position weren't changed, we'd need to paint
  1390. //             starting at row 50, and extending through row 125.  BUT
  1391. //             since the DIB is only 100 pixels wide, white space would
  1392. //             appear at the right margin!  Instead, the thumb is placed
  1393. //             at column 25 (in SetScrollPos), and columns 25 through
  1394. //             100 are displayed (by invalidating the client window!
  1395. //
  1396. //             Re-read the above paragraph (slowly this time)!
  1397. //
  1398. // Parms:      hWnd == Handle to window getting WM_SIZE.
  1399. //
  1400. // History:   Date      Reason
  1401. //            10/15/91  Cut code out from WM_SIZE case.
  1402. //
  1403. //---------------------------------------------------------------------
  1404.  
  1405. void ChildWndSize (HWND hWnd)
  1406. {
  1407.    HANDLE      hDIBInfo;
  1408.    LPDIBINFO   lpDIBInfo;
  1409.    LPSTR       lpDIB;
  1410.    int         cxScroll, cyScroll, cxDIB = 0, cyDIB = 0;
  1411.    RECT        rect;
  1412.  
  1413.  
  1414.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  1415.  
  1416.    if (hDIBInfo)
  1417.       {
  1418.       lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  1419.  
  1420.  
  1421.          // Find out the DIB's height/width.
  1422.  
  1423.       if (lpDIBInfo->hDIB)
  1424.          {
  1425.          lpDIB = GlobalLock (lpDIBInfo->hDIB);
  1426.          cxDIB = (int) DIBWidth (lpDIB);
  1427.          cyDIB = (int) DIBHeight (lpDIB);
  1428.          GlobalUnlock (lpDIBInfo->hDIB);
  1429.          }
  1430.  
  1431.  
  1432.          // Find out the dimensions of the window, and the current
  1433.          //  thumb positions.
  1434.  
  1435.       GetClientRect (hWnd, &rect);
  1436.       cxScroll = GetScrollPos (hWnd, SB_HORZ);
  1437.       cyScroll = GetScrollPos (hWnd, SB_VERT);
  1438.  
  1439.  
  1440.          // If we are in "stretch to window" more, or the current
  1441.          //  thumb positions would cause "white space" at the right
  1442.          //  or bottom of the window, repaint.
  1443.  
  1444.       if (lpDIBInfo->Options.bStretch ||
  1445.             cxScroll + rect.right > cxDIB ||
  1446.             cyScroll + rect.bottom > cyDIB)
  1447.          InvalidateRect (hWnd, NULL, FALSE);
  1448.  
  1449.       if (!IsIconic (hWnd) && !lpDIBInfo->Options.bStretch)
  1450.          SetupScrollBars (hWnd, lpDIBInfo->wDIBWidth, lpDIBInfo->wDIBHeight);
  1451.  
  1452.       GlobalUnlock (hDIBInfo);
  1453.       }
  1454. }
  1455.  
  1456.  
  1457.  
  1458.  
  1459.  
  1460.  
  1461. //---------------------------------------------------------------------
  1462. //
  1463. // Function:   SetupScrollBars
  1464. //
  1465. // Purpose:    Sets up MDI Child's scroll bars
  1466. //
  1467. //             Either we display both scroll bars, or no scroll bars.
  1468. //
  1469. // Parms:      hWnd == Handle to window who's scroll bars we'll set up.
  1470. //             hDIB == A handle to the current DIB.
  1471. //
  1472. // History:   Date      Reason
  1473. //            6/1/91    Created.
  1474. //
  1475. //---------------------------------------------------------------------
  1476.  
  1477. void SetupScrollBars (HWND hWnd, WORD cxDIB, WORD cyDIB)
  1478. {
  1479.    RECT        rect;                         // Client Rectangle.
  1480.    BOOL        bNeedScrollBars = FALSE;      // Need Scroll bars?
  1481.    unsigned    cxWindow,                     // Width of client area.
  1482.                cyWindow;                     // Height of client area.
  1483.    int         cxRange         = 0,          // Range needed for horz bar.
  1484.                cyRange         = 0;          // Range needed for vert bar.
  1485.  
  1486.  
  1487.       // Do some initialization.
  1488.  
  1489.    ReallyGetClientRect (hWnd, &rect);
  1490.  
  1491.    cxWindow = rect.right - rect.left;
  1492.    cyWindow = rect.bottom - rect.top;
  1493.  
  1494.  
  1495.       // Now determine if we need the scroll bars.  Since the
  1496.       //  window is not allowed to be larger than the DIB, if
  1497.       //  we need one scroll bar, we need _both_.  Since if
  1498.       //  one scroll bar is turned on, it eats up some of
  1499.       //  the client area.
  1500.  
  1501.    if ((cxWindow < (unsigned) cxDIB) || (cyWindow < (unsigned) cyDIB))
  1502.       bNeedScrollBars = TRUE;
  1503.  
  1504.  
  1505.       // Setup the scroll bar ranges.  We want to be able to
  1506.       //  scroll the window so that all the DIB can appear
  1507.       //  within the client area.  Take into account that
  1508.       //  if the opposite scroll bar is activated, it eats
  1509.       //  up some client area.
  1510.  
  1511.    if (bNeedScrollBars)
  1512.       {
  1513.       cyRange = (unsigned) cyDIB - cyWindow - 1 + GetSystemMetrics (SM_CYHSCROLL);
  1514.       cxRange = (unsigned) cxDIB - cxWindow - 1 + GetSystemMetrics (SM_CXVSCROLL);
  1515.       }
  1516.  
  1517.  
  1518.       // Set the ranges we've calculated (0->0 means invisible scrollbar).
  1519.  
  1520.    SetScrollRange (hWnd, SB_VERT, 0, cyRange, TRUE);
  1521.    SetScrollRange (hWnd, SB_HORZ, 0, cxRange, TRUE);
  1522. }
  1523.  
  1524.  
  1525.  
  1526. //---------------------------------------------------------------------
  1527. //
  1528. // Function:   ScrollBarsOff
  1529. //
  1530. // Purpose:    Turns off scroll bars on the specified window.
  1531. //
  1532. // Parms:      hWnd == Handle to window to turn the scroll bars off in.
  1533. //
  1534. // History:   Date      Reason
  1535. //
  1536. //            6/1/91    Created.
  1537. //
  1538. //----------------------------------------------------------------------
  1539.  
  1540. void ScrollBarsOff (HWND hWnd)
  1541. {
  1542.    SetScrollRange (hWnd, SB_VERT, 0, 0, TRUE);
  1543.    SetScrollRange (hWnd, SB_HORZ, 0, 0, TRUE);
  1544. }
  1545.  
  1546.  
  1547.  
  1548.  
  1549.  
  1550. //---------------------------------------------------------------------
  1551. //
  1552. // Function:   GetCurrentMDIWnd
  1553. //
  1554. // Purpose:    Returns the currently active MDI child window.
  1555. //
  1556. // Parms:      None.
  1557. //
  1558. // History:   Date      Reason
  1559. //
  1560. //            6/1/91    Created.
  1561. //
  1562. //---------------------------------------------------------------------
  1563.  
  1564. HWND GetCurrentMDIWnd (void)
  1565. {
  1566.    return LOWORD (SendMessage (hWndMDIClient, WM_MDIGETACTIVE, 0, 0L));
  1567. }
  1568.  
  1569.  
  1570.  
  1571.  
  1572. //---------------------------------------------------------------------
  1573. //
  1574. // Function:   CurrentDIBPalette
  1575. //
  1576. // Purpose:    Returns a handle to a duplicate of the current MDI
  1577. //             child window's palette.
  1578. //
  1579. //             This is used whenever anyone wants to use the same
  1580. //             palette -- a duplicate is created, since the MDI
  1581. //             child window could be destroyed at any time (and it's
  1582. //             palette is destroyed when the window goes away).
  1583. //
  1584. // Parms:      None.
  1585. //
  1586. // History:   Date      Reason
  1587. //
  1588. //            6/1/91    Created.
  1589. //
  1590. //---------------------------------------------------------------------
  1591.  
  1592. HPALETTE CurrentDIBPalette (void)
  1593. {
  1594.    HWND         hWnd;
  1595.    HPALETTE     hDIBPal;
  1596.    HANDLE       hDIBInfo;
  1597.    LPDIBINFO    lpDIBInfo;
  1598.  
  1599.  
  1600.       // Get a handle to the current MDI Child.
  1601.  
  1602.    hWnd = GetCurrentMDIWnd ();
  1603.  
  1604.    if (!hWnd)
  1605.       return NULL;
  1606.  
  1607.  
  1608.       // Get the current palette from the MDI Child's window words.
  1609.  
  1610.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  1611.  
  1612.    if (!hDIBInfo)
  1613.       return NULL;
  1614.  
  1615.    lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  1616.    hDIBPal   = lpDIBInfo->hPal;
  1617.    GlobalUnlock (hDIBInfo);
  1618.  
  1619.    if (!hDIBPal)
  1620.       return NULL;
  1621.  
  1622.    return ((HPALETTE)CopyPalette (hDIBPal));
  1623. }
  1624.  
  1625.  
  1626.  
  1627. //---------------------------------------------------------------------
  1628. //
  1629. // Function:   GetCurrentDIBStretchFlag
  1630. //
  1631. // Purpose:    Returns the current MDI child window's stretch flag
  1632. //             (stored in the INFOSTRUCT stored in a DIB window's
  1633. //             window words).
  1634. //
  1635. // Parms:      None.
  1636. //
  1637. // History:   Date      Reason
  1638. //
  1639. //            6/1/91    Created.
  1640. //
  1641. //---------------------------------------------------------------------
  1642.  
  1643. BOOL GetCurrentDIBStretchFlag (void)
  1644. {
  1645.    HWND      hWndDIB;
  1646.    HANDLE    hDIBInfo;
  1647.    LPDIBINFO lpDIBInfo;
  1648.    BOOL      bStretch;
  1649.  
  1650.    hWndDIB = GetCurrentMDIWnd ();
  1651.  
  1652.    if (hWndDIB)
  1653.       {
  1654.       hDIBInfo = GetWindowWord (hWndDIB, WW_DIB_HINFO);
  1655.  
  1656.       if (!hDIBInfo)
  1657.          return FALSE;
  1658.  
  1659.       lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  1660.       bStretch  = lpDIBInfo->Options.bStretch;
  1661.       GlobalUnlock (hDIBInfo);
  1662.  
  1663.       return bStretch;
  1664.       }
  1665.    else
  1666.       return FALSE;
  1667. }
  1668.  
  1669.  
  1670. //---------------------------------------------------------------------
  1671. //
  1672. // Function:   SetCurrentDIBStretchFlag
  1673. //
  1674. // Purpose:    Sets the current MDI child window's stretch flag
  1675. //             (stored in the INFOSTRUCT stored in a DIB window's
  1676. //             window words).
  1677. //
  1678. // Parms:      bFlag == New flag setting.
  1679. //
  1680. // History:   Date      Reason
  1681. //
  1682. //            6/1/91    Created.
  1683. //
  1684. //---------------------------------------------------------------------
  1685.  
  1686. void SetCurrentDIBStretchFlag (BOOL bFlag)
  1687. {
  1688.    HANDLE    hDIBInfo;
  1689.    LPDIBINFO lpDIBInfo;
  1690.    HWND      hWnd;
  1691.  
  1692.    hWnd = GetCurrentMDIWnd ();
  1693.  
  1694.    if (!hWnd)
  1695.       return;
  1696.  
  1697.    hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  1698.  
  1699.    if (!hDIBInfo)
  1700.       return;
  1701.  
  1702.    lpDIBInfo                   = (LPDIBINFO) GlobalLock (hDIBInfo);
  1703.    lpDIBInfo->Options.bStretch = bFlag;
  1704.    GlobalUnlock (hDIBInfo);
  1705. }
  1706.  
  1707.  
  1708.  
  1709. //---------------------------------------------------------------------
  1710. //
  1711. // Function:   ReallyGetClientRect
  1712. //
  1713. // Purpose:    Gets the rectangular area of the client rect including
  1714. //             the area underneath visible scroll bars.  Stolen from
  1715. //             ShowDIB.
  1716. //
  1717. // Parms:      hWnd   == Window to get the client area of.
  1718. //             lpRect == Where to copy the rectangle to.
  1719. //
  1720. // History:   Date      Reason
  1721. //
  1722. //            6/1/91    Created.
  1723. //
  1724. //---------------------------------------------------------------------
  1725.  
  1726. void ReallyGetClientRect (HWND hWnd, LPRECT lpRect)
  1727. {
  1728.    DWORD dwWinStyle;
  1729.  
  1730.    dwWinStyle = GetWindowLong (hWnd, GWL_STYLE);
  1731.  
  1732.    GetClientRect (hWnd, lpRect);
  1733.  
  1734.    if (dwWinStyle & WS_HSCROLL)
  1735.       lpRect->bottom += GetSystemMetrics (SM_CYHSCROLL);
  1736.  
  1737.    if (dwWinStyle & WS_VSCROLL)
  1738.       lpRect->right  += GetSystemMetrics (SM_CXVSCROLL);
  1739. }
  1740.  
  1741.  
  1742.  
  1743.  
  1744.  
  1745. //---------------------------------------------------------------------
  1746. //
  1747. // Function:   Hourglass
  1748. //
  1749. // Purpose:    Displays or hides the hourglass during lengthy operations.
  1750. //
  1751. // Parms:      bDisplay == TRUE to display, false to put it away.
  1752. //
  1753. // History:   Date      Reason
  1754. //
  1755. //            6/1/91    Created.
  1756. //
  1757. //---------------------------------------------------------------------
  1758.  
  1759. void Hourglass (BOOL bDisplay)
  1760. {
  1761.    static HCURSOR hOldCursor = NULL;
  1762.    static int     nCount     = 0;
  1763.  
  1764.  
  1765.    if (bDisplay)
  1766.       {
  1767.          // Check if we already have the hourglass up and increment
  1768.          //  the number of times Hourglass (TRUE) has been called.
  1769.  
  1770.       if (nCount++)
  1771.          return;
  1772.  
  1773.       hOldCursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
  1774.  
  1775.          // If this machine doesn't have a mouse, display the
  1776.          //  hourglass by calling ShowCursor(TRUE) (if it does
  1777.          //  have a mouse this doesn't do anything much).
  1778.  
  1779.       ShowCursor (TRUE);
  1780.       }
  1781.    else
  1782.       {
  1783.          // If we haven't changed the cursor, return to caller.
  1784.  
  1785.       if (!nCount)
  1786.          return;
  1787.  
  1788.  
  1789.          // If our usage count drops to zero put back the cursor
  1790.          //  we originally replaced.
  1791.  
  1792.       if (!(--nCount))
  1793.          {
  1794.          SetCursor (hOldCursor);
  1795.          hOldCursor = NULL;
  1796.          ShowCursor (FALSE);
  1797.          }
  1798.       }
  1799. }
  1800.  
  1801.  
  1802.  
  1803. //---------------------------------------------------------------------
  1804. //
  1805. // Function:   SetMessageToAllChildren
  1806. //
  1807. // Purpose:    Passes a message to all children of the specified window.
  1808. //
  1809. // Parms:      hWnd == Parent window.
  1810. //             message == message to pass to all children.
  1811. //             wParam  == wParam of message to pass to all children.
  1812. //             lParam  == lParam of message to pass to all children.
  1813. //
  1814. // History:   Date      Reason
  1815. //
  1816. //            6/1/91    Created.
  1817. //
  1818. //---------------------------------------------------------------------
  1819.  
  1820. void SendMessageToAllChildren (HWND hWnd,
  1821.                            unsigned message,
  1822.                                WORD wParam,
  1823.                                LONG lParam)
  1824. {
  1825.    HWND hChild;
  1826.  
  1827.    if (hChild = GetWindow(hWnd, GW_CHILD))     // Get 1st child.
  1828.       do
  1829.          SendMessage(hChild, message, wParam, lParam);
  1830.       while (hChild = GetWindow(hChild, GW_HWNDNEXT));
  1831. }
  1832.  
  1833.  
  1834.  
  1835.  
  1836. //---------------------------------------------------------------------
  1837. //
  1838. // Function:   CloseAllDIBWindows
  1839. //
  1840. // Purpose:    Close all DIB windows currently open.
  1841. //
  1842. //             First hides the MDI client -- this insures that no drawing
  1843. //             occurs in the DIB windows while we delete windows.
  1844. //             Then enumerate all the MDI client's children.  If the
  1845. //             child is a DIB window, delete it.  If it's a title bar,
  1846. //             leave it alone, and let Windows take care of it.
  1847. //
  1848. //
  1849. // Parms:      None
  1850. //
  1851. // History:   Date      Reason
  1852. //
  1853. //            6/1/91    Created.
  1854. //
  1855. //---------------------------------------------------------------------
  1856.  
  1857. void CloseAllDIBWindows (void)
  1858. {
  1859.     register HWND hChild;
  1860.     BOOL          bWasVisible;
  1861.  
  1862.  
  1863.       // hide the MDI client window to avoid multiple repaints
  1864.  
  1865.    bWasVisible = ShowWindow (hWndMDIClient, SW_HIDE);
  1866.  
  1867.  
  1868.       // As long as the MDI client has a child, destroy it
  1869.  
  1870.    while (hChild = GetWindow (hWndMDIClient, GW_CHILD))
  1871.       {
  1872.          // Skip the icon title windows (they have owners, MDI children
  1873.          //  don't -- child windows have parents, not owners; title
  1874.          //  bars' owners are the MDI child windows themselves).
  1875.  
  1876.       while (hChild && GetWindow (hChild, GW_OWNER))
  1877.          hChild = GetWindow (hChild, GW_HWNDNEXT);
  1878.  
  1879.       if (!hChild)
  1880.          break;
  1881.  
  1882.       SendMessage (hWndMDIClient, WM_MDIDESTROY, (WORD)hChild, 0L);
  1883.       }
  1884.  
  1885.  
  1886.       // Make the MDI Client visible again, if it was visible when
  1887.       //  we called ShowWindow(..., SW_HIDE).
  1888.  
  1889.    if (bWasVisible)
  1890.       ShowWindow(hWndMDIClient, SW_SHOWNORMAL);
  1891. }
  1892.  
  1893.  
  1894.  
  1895.  
  1896. //---------------------------------------------------------------------
  1897. //
  1898. // Function:   TrackMouse
  1899. //
  1900. // Purpose:    This routine is called when the left mouse button is
  1901. //             held down.  It will continuously draw a rectangle
  1902. //             showing where the user has selected for cutting to the
  1903. //             clipboard.  When this routine is called, lpClipRect's
  1904. //             top/left should point at the point in the client area
  1905. //             where the left mouse button was hit.  It will return the
  1906. //             full sized rectangle the user selected.  Never allow the
  1907. //             rubber band to extend beyond the DIB's margins.
  1908. //
  1909. //             Code was stolen almost verbatim from ShowDIB.
  1910. //
  1911. // Parms:      hWnd       == Handle to this MDI child window.
  1912. //             lpClipRect == Rectangle enclosed by tracking box.
  1913. //             cxDIB      == Width of DIB.  Won't allow tracking box
  1914. //                             to go beyond the width.
  1915. //             cyDIB      == Height of DIB.  Won't allow tracking box
  1916. //                             to go beyond the height.
  1917. //
  1918. // History:   Date      Reason
  1919. //             ????     Created
  1920. //             9/1/91   Added cxDIB/cyDIB to not allow garbage
  1921. //                        to be pasted to the clipboard.
  1922. //
  1923. //---------------------------------------------------------------------
  1924.  
  1925. void TrackMouse (HWND hWnd, LPRECT lpClipRect, int cxDIB, int cyDIB)
  1926. {
  1927.    HDC   hDC;
  1928.    MSG   msg;
  1929.    POINT ptOrigin, ptStart;
  1930.    RECT  rcClient;
  1931.  
  1932.    hDC = GetDC (hWnd);
  1933.    SetCapture (hWnd);
  1934.    GetClientRect (hWnd, &rcClient);
  1935.  
  1936.  
  1937.       // Get mouse coordinates relative to origin of DIB.  Then
  1938.       //  setup the clip rectangle accordingly (it already should
  1939.       //  contain the starting point in its top/left).
  1940.  
  1941.    ptOrigin.x         = GetScrollPos (hWnd, SB_HORZ);
  1942.    ptOrigin.y         = GetScrollPos (hWnd, SB_VERT);
  1943.    lpClipRect->top   += ptOrigin.x;
  1944.    lpClipRect->left  += ptOrigin.y;
  1945.    lpClipRect->right  = lpClipRect->left;
  1946.    lpClipRect->bottom = lpClipRect->top;
  1947.    ptStart.x          = lpClipRect->left;    // Need to remember the
  1948.    ptStart.y          = lpClipRect->top;     //  starting point.
  1949.  
  1950.  
  1951.  
  1952.       // Display the starting coordinates.
  1953.  
  1954.    SetWindowOrg (hDC, ptOrigin.x, ptOrigin.y);
  1955.    DrawSelect (hDC, *lpClipRect);
  1956.  
  1957.  
  1958.     // Eat mouse messages until a WM_LBUTTONUP is encountered. Meanwhile
  1959.     // continue to draw a rubberbanding rectangle and display it's dimensions
  1960.  
  1961.    for (;;)
  1962.       {
  1963.       WaitMessage ();
  1964.  
  1965.       if (PeekMessage (&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
  1966.          {
  1967.             // Erase the old.
  1968.  
  1969.          DrawSelect (hDC, *lpClipRect);
  1970.  
  1971.  
  1972.             // Determine new coordinates.
  1973.  
  1974.          lpClipRect->left   = ptStart.x;
  1975.          lpClipRect->top    = ptStart.y;
  1976.          lpClipRect->right  = LOWORD (msg.lParam) + ptOrigin.x;
  1977.          lpClipRect->bottom = HIWORD (msg.lParam) + ptOrigin.y;
  1978.          NormalizeRect (lpClipRect);
  1979.  
  1980.  
  1981.             // Keep the rectangle within the bounds of the DIB.
  1982.  
  1983.          lpClipRect->left   = MAX(lpClipRect->left,   0);
  1984.          lpClipRect->top    = MAX(lpClipRect->top,    0);
  1985.          lpClipRect->right  = MAX(lpClipRect->right,  0);
  1986.          lpClipRect->bottom = MAX(lpClipRect->bottom, 0);
  1987.  
  1988.          lpClipRect->left   = MIN(lpClipRect->left,   cxDIB);
  1989.          lpClipRect->top    = MIN(lpClipRect->top,    cyDIB);
  1990.          lpClipRect->right  = MIN(lpClipRect->right,  cxDIB);
  1991.          lpClipRect->bottom = MIN(lpClipRect->bottom, cyDIB);
  1992.  
  1993.  
  1994.  
  1995.             // Draw the new rectangle.
  1996.  
  1997.          DrawSelect (hDC, *lpClipRect);
  1998.  
  1999.  
  2000.             // If the button is released, quit.
  2001.  
  2002.          if (msg.message == WM_LBUTTONUP)
  2003.              break;
  2004.         }
  2005.       else
  2006.          continue;
  2007.       }
  2008.  
  2009.  
  2010.       // Clean up.
  2011.  
  2012.    ReleaseCapture ();
  2013.    ReleaseDC (hWnd, hDC);
  2014. }
  2015.  
  2016.  
  2017.  
  2018.  
  2019. //---------------------------------------------------------------------
  2020. //
  2021. // Function:   NormalizeRect
  2022. //
  2023. // Purpose:    Insure that the upper/left corner of the rectangle is
  2024. //             kept in rect.top/rect.right.  Swaps around coordinates
  2025. //             in the rectangle to make sure this is true.
  2026. //
  2027. //             Code was stolen verbatim from ShowDIB.
  2028. //
  2029. // Parms:      lpRect == Far pointer to RECT to normalize.
  2030. //
  2031. // History:   Date      Reason
  2032. //             ????     Created
  2033. //
  2034. //---------------------------------------------------------------------
  2035.  
  2036. void NormalizeRect (LPRECT lpRect)
  2037. {
  2038.     if (lpRect->right < lpRect->left)
  2039.         SWAP (lpRect->right,lpRect->left);
  2040.  
  2041.     if (lpRect->bottom < lpRect->top)
  2042.         SWAP (lpRect->bottom,lpRect->top);
  2043. }
  2044.  
  2045.  
  2046.  
  2047. //---------------------------------------------------------------------
  2048. //
  2049. // Function:   DrawSelect
  2050. //
  2051. // Purpose:    Draw the rubberbanding rectangle with the specified
  2052. //             dimensions on the specified DC.  Rectangle includes
  2053. //             a string with its dimensions centered within it.
  2054. //
  2055. //             Code was stolen almost verbatim from ShowDIB.
  2056. //
  2057. // Parms:      hDC    == DC to draw into.
  2058. //             rcClip == Rectangle to draw.
  2059. //
  2060. // History:   Date      Reason
  2061. //             ????     Created
  2062. //
  2063. //---------------------------------------------------------------------
  2064.  
  2065. void DrawSelect (HDC hDC, RECT rcClip)
  2066. {
  2067.    char    szStr[80];
  2068.    DWORD   dwExt;
  2069.    int     x, y, nLen, dx, dy;
  2070.    HDC     hDCBits;
  2071.    HBITMAP hBitmap;
  2072.  
  2073.  
  2074.       // Don't have anything to do if the rectangle is empty.
  2075.  
  2076.    if (IsRectEmpty (&rcClip))
  2077.       return;
  2078.  
  2079.  
  2080.       // Draw rectangular clip region
  2081.  
  2082.    PatBlt (hDC,
  2083.             rcClip.left,
  2084.             rcClip.top,
  2085.             rcClip.right - rcClip.left,
  2086.             1,
  2087.             DSTINVERT);
  2088.  
  2089.    PatBlt (hDC,
  2090.             rcClip.left,
  2091.             rcClip.bottom,
  2092.             1,
  2093.             -(rcClip.bottom - rcClip.top),
  2094.             DSTINVERT);
  2095.  
  2096.    PatBlt (hDC,
  2097.             rcClip.right - 1,
  2098.             rcClip.top,
  2099.             1,
  2100.             rcClip.bottom - rcClip.top,
  2101.             DSTINVERT);
  2102.  
  2103.    PatBlt (hDC,
  2104.             rcClip.right,
  2105.             rcClip.bottom - 1,
  2106.             -(rcClip.right - rcClip.left),
  2107.             1,
  2108.             DSTINVERT);
  2109.  
  2110.  
  2111.       // Format the dimensions string ...
  2112.  
  2113.    wsprintf (szStr,
  2114.                 "%dx%d",
  2115.                rcClip.right - rcClip.left,
  2116.                 rcClip.bottom - rcClip.top );
  2117.                nLen = lstrlen(szStr);
  2118.  
  2119.  
  2120.       // ... and center it in the rectangle
  2121.  
  2122.    dwExt   = GetTextExtent (hDC, szStr, nLen);
  2123.    dx      = LOWORD (dwExt);
  2124.    dy      = HIWORD (dwExt);
  2125.    x       = (rcClip.right  + rcClip.left - dx) / 2;
  2126.    y       = (rcClip.bottom + rcClip.top  - dy) / 2;
  2127.    hDCBits = CreateCompatibleDC (hDC);
  2128.  
  2129.  
  2130.       // Output the text to the DC
  2131.  
  2132.    SetTextColor (hDCBits, RGB (255, 255, 255));
  2133.    SetBkColor (hDCBits,   RGB (0,   0,   0));
  2134.  
  2135.    if (hBitmap = CreateBitmap (dx, dy, 1, 1, NULL))
  2136.       {
  2137.       hBitmap = SelectObject (hDCBits, hBitmap);
  2138.  
  2139.       ExtTextOut (hDCBits, 0, 0, 0, NULL, szStr, nLen, NULL);
  2140.       BitBlt (hDC, x, y, dx, dy, hDCBits, 0, 0, SRCINVERT);
  2141.       hBitmap = SelectObject (hDCBits, hBitmap);
  2142.  
  2143.       DeleteObject (hBitmap);
  2144.       }
  2145.  
  2146.    DeleteDC (hDCBits);
  2147. }
  2148.  
  2149.  
  2150.  
  2151. //---------------------------------------------------------------------
  2152. //
  2153. // Function:   GetCurrentClipRect
  2154. //
  2155. // Purpose:    Return the rectangular dimensions of the clipboard
  2156. //             rectangle for the specified window.
  2157. //
  2158. // Parms:      hWnd == Window to retrieve the clipboard rectangle info
  2159. //                     from.
  2160. //
  2161. // History:   Date      Reason
  2162. //             6/1/91   Created
  2163. //
  2164. //---------------------------------------------------------------------
  2165.  
  2166. RECT GetCurrentClipRect (HWND hWnd)
  2167. {
  2168.    RECT      rect = {0,0,0,0};
  2169.    HANDLE    hDIBInfo;
  2170.    LPDIBINFO lpDIBInfo;
  2171.  
  2172.    if (hWnd)
  2173.       {
  2174.       hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  2175.  
  2176.       if (hDIBInfo)
  2177.          {
  2178.          lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  2179.          rect      = lpDIBInfo->rcClip;
  2180.          GlobalUnlock (hDIBInfo);
  2181.          }
  2182.       }
  2183.  
  2184.    return rect;
  2185. }
  2186.  
  2187.  
  2188.  
  2189. //---------------------------------------------------------------------
  2190. //
  2191. // Function:   GetCurrentDIBSize
  2192. //
  2193. // Purpose:    Return the dimensions of the DIB for the given MDI child
  2194. //             window.  Dimensions are returned as a POINT.
  2195. //
  2196. //             If the DIB is stretched on the screen, return the client
  2197. //             area of the window.  Otherwise, retrieve the info from
  2198. //             the DIBINFO structure stored in the window's words.
  2199. //
  2200. // Parms:      hWnd == Window to retrieve the DIB dimensions from.
  2201. //
  2202. // History:   Date      Reason
  2203. //             6/1/91   Created
  2204. //
  2205. //---------------------------------------------------------------------
  2206.  
  2207. POINT GetCurrentDIBSize (HWND hWnd)
  2208. {
  2209.    HANDLE    hDIBInfo;
  2210.    LPDIBINFO lpDIBInfo;
  2211.    POINT     pt = {0,0};
  2212.    RECT      rcClient;
  2213.  
  2214.    if (hWnd)
  2215.       {
  2216.       hDIBInfo = GetWindowWord (hWnd, WW_DIB_HINFO);
  2217.  
  2218.       if (hDIBInfo)
  2219.          {
  2220.          lpDIBInfo = (LPDIBINFO) GlobalLock (hDIBInfo);
  2221.  
  2222.          if (lpDIBInfo->Options.bStretch)
  2223.             {
  2224.             GetClientRect (hWnd, &rcClient);
  2225.             pt.x = rcClient.right;
  2226.             pt.y = rcClient.bottom;
  2227.             }
  2228.          else
  2229.             {
  2230.             pt.x = lpDIBInfo->wDIBWidth;
  2231.             pt.y = lpDIBInfo->wDIBHeight;
  2232.             }
  2233.  
  2234.          GlobalUnlock (hDIBInfo);
  2235.          }
  2236.       }
  2237.  
  2238.    return pt;
  2239. }
  2240.