home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwplascr.zip / XWPL0208.ZIP / tools / ximgview / ximgview.c < prev    next >
C/C++ Source or Header  |  2002-04-13  |  38KB  |  1,193 lines

  1.  
  2. /*
  3.  * ximgview.c:
  4.  *      primitive image viewer. Uses MMPM/2 to understand all its
  5.  *      image file formats.
  6.  *
  7.  *      This is also a simple example of how to multithread a PM
  8.  *      application. Loading the bitmap is passed to a second
  9.  *      transient thread.
  10.  *
  11.  *      Copyright (C) 2000 Ulrich Möller.
  12.  *      This program is free software; you can redistribute it and/or modify
  13.  *      it under the terms of the GNU General Public License as published by
  14.  *      the Free Software Foundation, in version 2 as it comes in the COPYING
  15.  *      file of the XWorkplace main distribution.
  16.  *      This program is distributed in the hope that it will be useful,
  17.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  *      GNU General Public License for more details.
  20.  */
  21.  
  22. #define INCL_WIN
  23. #define INCL_WINWORKPLACE
  24. #define INCL_DOS
  25. #define INCL_DOSERRORS
  26. #define INCL_GPI                // required for INCL_MMIO_CODEC
  27. #define INCL_GPIBITMAPS         // required for INCL_MMIO_CODEC
  28. #include <os2.h>
  29.  
  30. // multimedia includes
  31. #define INCL_MCIOS2
  32. #define INCL_MMIOOS2
  33. #define INCL_MMIO_CODEC
  34. #include <os2me.h>
  35.  
  36. #include <stdio.h>
  37. #include <setjmp.h>
  38.  
  39. #include "setup.h"
  40.  
  41. #include "helpers\cnrh.h"
  42. #include "helpers\eah.h"
  43. #include "helpers\dosh.h"
  44. #include "helpers\gpih.h"
  45. #include "helpers\except.h"
  46. #include "helpers\stringh.h"
  47. #include "helpers\winh.h"
  48. #include "helpers\threads.h"
  49.  
  50. #include "bldlevel.h"
  51. #include "dlgids.h"
  52.  
  53. #include "ximgview.h"
  54.  
  55. /* ******************************************************************
  56.  *
  57.  *   Global variables for all threads
  58.  *
  59.  ********************************************************************/
  60.  
  61. CHAR        G_szFilename[CCHMAXPATH] = "";
  62.  
  63. /* ******************************************************************
  64.  *
  65.  *   Image handling
  66.  *
  67.  ********************************************************************/
  68.  
  69. #define LOADBMP_SUCCESS                 0
  70. // #define LOADBMP_UNKNOWN_ERROR           1
  71. #define LOADBMP_GOT_DOS_IOPROC          2
  72. #define LOADBMP_NOT_IMAGE_FILE          3
  73. #define LOADBMP_OPEN_FAILED             4
  74. #define LOADBMP_INCOMPATIBLE_HEADER     5
  75. #define LOADBMP_HEADER_UNAVAILABLE      6
  76. #define LOADBMP_GPIGREATEPS_FAILED      7
  77. #define LOADBMP_GPICREATEBITMAP_FAILED  8
  78. #define LOADBMP_GPISETBITMAPBITS_FAILED 9
  79. #define LOADBMP_OUT_OF_MEMORY           10
  80. #define LOADBMP_CRASHED                 11
  81. #define LOADBMP_MMIOREAD_FAILED         12
  82.  
  83. /*
  84.  *@@ QueryErrorDescription:
  85.  *
  86.  */
  87.  
  88. PSZ QueryErrorDescription(ULONG ulError)
  89. {
  90.     switch (ulError)
  91.     {
  92.         // case LOADBMP_UNKNOWN_ERROR:
  93.         case LOADBMP_GOT_DOS_IOPROC:
  94.             return "Image file format not supported.";
  95.  
  96.         case LOADBMP_NOT_IMAGE_FILE:
  97.             return "File is non-image multimedia file.";
  98.  
  99.         case LOADBMP_OPEN_FAILED:
  100.             return "Open failed.";
  101.  
  102.         case LOADBMP_INCOMPATIBLE_HEADER:
  103.             return "Incompatible bitmap header size.";
  104.  
  105.         case LOADBMP_HEADER_UNAVAILABLE:
  106.             return "Bitmap header unavailable.";
  107.  
  108.         case LOADBMP_GPIGREATEPS_FAILED:
  109.             return "Error creating memory presentation space (GpiCreatePS failed).";
  110.  
  111.         case LOADBMP_GPICREATEBITMAP_FAILED:
  112.             return "Error creating PM bitmap (GpiCreateBitmap failed).";
  113.  
  114.         case LOADBMP_GPISETBITMAPBITS_FAILED:
  115.             return "GpiSetBitmapBits failed.";
  116.  
  117.         case LOADBMP_OUT_OF_MEMORY:
  118.             return "Out of memory.";
  119.  
  120.         case LOADBMP_CRASHED:
  121.             return "Crashed reading bitmap.";
  122.  
  123.         case LOADBMP_MMIOREAD_FAILED:
  124.             return "Error reading bitmap data.";
  125.     }
  126.  
  127.     return ("Unknown error.");
  128. }
  129.  
  130. /*
  131.  *@@ CreateBitmapFromFile:
  132.  *      this actually loads the bitmap file
  133.  *      specified by hmmio (which must be a
  134.  *      bitmap file handle!) and creates a
  135.  *      regular PM bitmap from it.
  136.  *
  137.  *      The bitmap in stored *phbmOut. It
  138.  *      is not selected into any HPS.
  139.  *
  140.  *      Returns 0 (LOADBMP_SUCCESS) on success.
  141.  *      On error, returns one of the following:
  142.  *
  143.  *      -- LOADBMP_HEADER_UNAVAILABLE: mmioGetHeader failed.
  144.  *      -- LOADBMP_OUT_OF_MEMORY: DosAllocMem failed.
  145.  *      -- LOADBMP_GPIGREATEPS_FAILED: GpiCreatePS failed.
  146.  *      -- LOADBMP_GPICREATEBITMAP_FAILED: GpiCreateBitmap failed.
  147.  *      -- LOADBMP_MMIOREAD_FAILED: mmioRead failed.
  148.  *      -- LOADBMP_GPISETBITMAPBITS_FAILED: GpiSetBitmapBits failed.
  149.  */
  150.  
  151. ULONG CreateBitmapFromFile(HAB hab,             // in: anchor block
  152.                            HDC hdcMem,          // in: memory device context
  153.                            HMMIO hmmio,         // in: MMIO file handle
  154.                            HBITMAP *phbmOut)    // out: bitmap created
  155. {
  156.     ULONG   ulReturn = LOADBMP_SUCCESS,
  157.             ulrc;
  158.  
  159.     MMIMAGEHEADER mmImgHdr;
  160.     ULONG   ulBytesRead;
  161.  
  162.     ulrc = mmioGetHeader(hmmio,
  163.                          &mmImgHdr,
  164.                          sizeof(MMIMAGEHEADER),
  165.                          (PLONG)&ulBytesRead,
  166.                          0L,
  167.                          0L);
  168.  
  169.     if (ulrc != MMIO_SUCCESS)
  170.         // header unavailable
  171.         ulReturn = LOADBMP_HEADER_UNAVAILABLE;
  172.     else
  173.     {
  174.         /*
  175.          *  Determine the number of bytes required, per row.
  176.          *      PLANES MUST ALWAYS BE = 1
  177.          */
  178.  
  179.         ULONG dwHeight = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cy;
  180.         ULONG dwWidth = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cx;
  181.         SHORT wBitCount = mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;
  182.         ULONG dwRowBits = dwWidth * mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;
  183.         ULONG dwNumRowBytes = dwRowBits >> 3;
  184.  
  185.         ULONG dwPadBytes;
  186.         SIZEL ImageSize;
  187.         PBYTE pRowBuffer;
  188.  
  189.         /*
  190.          *  Account for odd bits used in 1bpp or 4bpp images that are
  191.          *  NOT on byte boundaries.
  192.          */
  193.  
  194.         if (dwRowBits % 8)
  195.         {
  196.              dwNumRowBytes++;
  197.         }
  198.  
  199.         /*
  200.          *  Ensure the row length in bytes accounts for byte padding.
  201.          *  All bitmap data rows must are aligned on LONG/4-BYTE boundaries.
  202.          *  The data FROM an IOProc should always appear in this form.
  203.          */
  204.  
  205.         dwPadBytes = (dwNumRowBytes % 4);
  206.  
  207.         if (dwPadBytes)
  208.             dwNumRowBytes += 4 - dwPadBytes;
  209.  
  210.         // allocate space for ONE row of pels
  211.         if (DosAllocMem((PPVOID)&pRowBuffer,
  212.                         dwNumRowBytes,
  213.                         fALLOC))
  214.             ulReturn = LOADBMP_OUT_OF_MEMORY;
  215.         else
  216.         {
  217.             // create a memory presentation space with the
  218.             // size of the bitmap
  219.             HPS hpsMem;
  220.  
  221.             ImageSize.cx = dwWidth;
  222.             ImageSize.cy = dwHeight;
  223.  
  224.             hpsMem = GpiCreatePS(hab,
  225.                                  hdcMem,
  226.                                  &ImageSize,
  227.                                  PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC);
  228.  
  229.             if (!hpsMem)
  230.                 ulReturn = LOADBMP_GPIGREATEPS_FAILED;
  231.             else
  232.             {
  233.                 // create an uninitialized bitmap -- this is where we
  234.                 // will put all of the bits once we read them in
  235.                 *phbmOut = GpiCreateBitmap(hpsMem,
  236.                                            &mmImgHdr.mmXDIBHeader.BMPInfoHeader2,
  237.                                            0L,
  238.                                            NULL,
  239.                                            NULL);
  240.                 if (!*phbmOut)
  241.                     ulReturn = LOADBMP_GPICREATEBITMAP_FAILED;
  242.                 else
  243.                 {
  244.                     // bitmap created:
  245.  
  246.                     if (GpiSetBitmap(hpsMem,
  247.                                      *phbmOut)
  248.                             == HBM_ERROR)
  249.                         ulReturn = LOADBMP_GPICREATEBITMAP_FAILED;
  250.                     else
  251.                     {
  252.                         ULONG dwRowCount;
  253.  
  254.                         // load the bitmap from the file,
  255.                         // one line at a time, starting from the BOTTOM
  256.                         for (dwRowCount = 0;
  257.                              dwRowCount < dwHeight;
  258.                              dwRowCount++)
  259.                         {
  260.                             LONG lrc;
  261.                             ulBytesRead = (ULONG)mmioRead(hmmio,
  262.                                                           pRowBuffer,
  263.                                                           dwNumRowBytes);
  264.  
  265.                             if (ulBytesRead == 0)
  266.                                 // done:
  267.                                 break;
  268.                             else if (ulBytesRead == MMIO_ERROR)
  269.                             {
  270.                                 ulReturn = LOADBMP_MMIOREAD_FAILED;
  271.                                 break;
  272.                             }
  273.  
  274.                             // allow context switching while previewing.. Couldn't get
  275.                             // it to work. Perhaps will get to it when time is available...
  276.  
  277.                             lrc = GpiSetBitmapBits(hpsMem,
  278.                                                    (LONG)dwRowCount,
  279.                                                    (LONG)1,
  280.                                                    (PBYTE)pRowBuffer,
  281.                                                    (PBITMAPINFO2)&mmImgHdr.mmXDIBHeader.BMPInfoHeader2);
  282.                             if (lrc == GPI_ALTERROR)
  283.                             {
  284.                                 ulReturn = LOADBMP_GPISETBITMAPBITS_FAILED;
  285.                                 break;
  286.                             }
  287.                         }
  288.  
  289.                         // unset bitmap im mem PS
  290.                         GpiSetBitmap(hpsMem,
  291.                                      NULLHANDLE);
  292.                     } // end if (GpiSetBitmap(hpsMem
  293.  
  294.                     if (ulReturn != LOADBMP_SUCCESS)
  295.                     {
  296.                         // error:
  297.                         GpiDeleteBitmap(*phbmOut);
  298.                         *phbmOut = NULLHANDLE;
  299.                     }
  300.                 }
  301.  
  302.                 GpiDestroyPS(hpsMem);
  303.             } // end if GpiCreatePS
  304.  
  305.             DosFreeMem(pRowBuffer);
  306.         } // end if (DosAllocMem((PPVOID)&pRowBuffer,
  307.     }
  308.  
  309.     return (ulReturn);
  310. }
  311.  
  312. /*
  313.  *@@ LoadBitmap:
  314.  *      one-shot function for loading an image
  315.  *      from a file. Understands any image file
  316.  *      format supported by MMPM/2.
  317.  *
  318.  *      Returns 0 (LOADBMP_SUCCESS) on success.
  319.  *      On error, returns one of the following:
  320.  *
  321.  *      -- LOADBMP_GOT_DOS_IOPROC: file format not understood
  322.  *         by MMPM/2.
  323.  *      -- LOADBMP_NOT_IMAGE_FILE: file format understood, but
  324.  *         is not image file or cannot be translated.
  325.  *      -- LOADBMP_OPEN_FAILED: mmioOpen failed.
  326.  *      -- LOADBMP_INCOMPATIBLE_HEADER: ioproc returned invalid
  327.  *         header.
  328.  *      -- LOADBMP_CRASHED: exception occured.
  329.  *
  330.  *      plus the error codes from CreateBitmapFromFile().
  331.  */
  332.  
  333. ULONG LoadBitmap(HAB hab,           // in: anchor block
  334.                  HDC hdcMem,        // in: memory device context
  335.                  HBITMAP *phbmOut,  // out: bitmap created
  336.                  PSZ pszFileName)   // in: filename
  337. {
  338.     ULONG           ulReturn = LOADBMP_SUCCESS;
  339.  
  340.     TRY_LOUD(excpt1)
  341.     {
  342.         ULONG           ulrc = 0;
  343.  
  344.         MMFORMATINFO    mmFormatInfo;
  345.         FOURCC          fccStorageSystem;
  346.  
  347.         // find the IOProc which can understand this file
  348.         ulrc = mmioIdentifyFile(pszFileName,
  349.                                 NULL,       // needed for RIFF only
  350.                                 &mmFormatInfo, // out: format info (FOURCC)
  351.                                 &fccStorageSystem, // out: FOURCC of storage IOProc
  352.                                 0L,         // reserved
  353.                                 0L);        // can be:
  354.                                 /*  MMIO_FORCE_IDENTIFY_SS
  355.                                         Forces the identification of a storage
  356.                                         system by ignoring the file
  357.                                         name and actually checking the MMIO Manager's
  358.                                         I/O procedure list.
  359.                                     MMIO_FORCE_IDENTIFY_FF
  360.                                         Forces the identification  of a file
  361.                                         format by ignoring the file name
  362.                                         and actually checking the MMIO Manager's
  363.                                         I/O procedure list.
  364.                                 */
  365.  
  366.         if (ulrc == MMIO_SUCCESS)
  367.         {
  368.             // if mmioIdentifyFile did not find a custom-written IO proc which
  369.             // can understand the image file, then it will return the DOS IO Proc
  370.             // info because the image file IS a DOS file.
  371.  
  372.             if (mmFormatInfo.fccIOProc == FOURCC_DOS)
  373.                 ulReturn = LOADBMP_GOT_DOS_IOPROC;
  374.             else
  375.             {
  376.                 // ensure this is an IMAGE IOproc, and that it can read
  377.                 // translated data
  378.                 if (   (mmFormatInfo.ulMediaType != MMIO_MEDIATYPE_IMAGE)
  379.                      || ((mmFormatInfo.ulFlags & MMIO_CANREADTRANSLATED) == 0)
  380.                    )
  381.                 {
  382.                     ulReturn = LOADBMP_NOT_IMAGE_FILE;
  383.                 }
  384.                 else
  385.                 {
  386.                     // remember fourcc of IOProc
  387.                     FOURCC      fccIOProc = mmFormatInfo.fccIOProc;
  388.  
  389.                     // load file
  390.                     MMIOINFO      mmioinfo;
  391.                     HMMIO         hmmio;
  392.  
  393.                     memset(&mmioinfo, 0, sizeof (MMIOINFO));
  394.                     mmioinfo.fccIOProc = fccIOProc;
  395.                     mmioinfo.ulTranslate = MMIO_TRANSLATEHEADER | MMIO_TRANSLATEDATA;
  396.                     hmmio = mmioOpen((PSZ)pszFileName,
  397.                                      &mmioinfo,
  398.                                      MMIO_READ | MMIO_DENYWRITE | MMIO_NOIDENTIFY);
  399.  
  400.                     if (!hmmio)
  401.                     {
  402.                         ulReturn = LOADBMP_OPEN_FAILED;
  403.                     }
  404.                     else
  405.                     {
  406.                         ULONG ulImageHeaderLength;
  407.                         ULONG dwReturnCode = mmioQueryHeaderLength(hmmio,
  408.                                                                    (PLONG)&ulImageHeaderLength,
  409.                                                                    0L,
  410.                                                                    0L);
  411.  
  412.                         if (ulImageHeaderLength != sizeof (MMIMAGEHEADER))
  413.                             ulReturn = LOADBMP_INCOMPATIBLE_HEADER;
  414.                         else
  415.                         {
  416.                             ulReturn = CreateBitmapFromFile(hab,
  417.                                                             hdcMem,
  418.                                                             hmmio,
  419.                                                             phbmOut);
  420.                         }
  421.  
  422.                         ulrc = mmioClose(hmmio, 0L);
  423.                     } // end if hmmio
  424.                 }
  425.             }
  426.         }
  427.     }
  428.     CATCH(excpt1)
  429.     {
  430.         ulReturn = LOADBMP_CRASHED;
  431.     } END_CATCH();
  432.  
  433.     return (ulReturn);
  434. }
  435.  
  436. /*
  437.  *@@ fntLoadBitmap:
  438.  *      transient thread created by WMCommand_FileOpen
  439.  *      to load the bitmap file.
  440.  *
  441.  *      Posts WM_DONELOADINGBMP to the client when done.
  442.  */
  443.  
  444. void _Optlink fntLoadBitmap(PTHREADINFO pti)
  445. {
  446.     // create a memory PS
  447.     HWND        hwndClient = (HWND)(pti->ulData);
  448.     HBITMAP     hbmNew;
  449.  
  450.     HDC hdcMem = DevOpenDC(pti->hab,
  451.                            OD_MEMORY,
  452.                            "*",
  453.                            0L,
  454.                            NULL,
  455.                            0);
  456.  
  457.     ULONG ulrc = LoadBitmap(pti->hab,
  458.                             hdcMem,
  459.                             &hbmNew,
  460.                             G_szFilename);
  461.     WinPostMsg(hwndClient,
  462.                WM_DONELOADINGBMP,
  463.                (MPARAM)ulrc,
  464.                (MPARAM)hbmNew);
  465.  
  466.     DevCloseDC(hdcMem);
  467. }
  468.  
  469. /* ******************************************************************
  470.  *
  471.  *   Global variables for main thread
  472.  *
  473.  ********************************************************************/
  474.  
  475. HAB         G_habMain = NULLHANDLE;
  476. HMQ         G_hmqMain = NULLHANDLE;
  477. HWND        G_hwndMain = NULLHANDLE;
  478. HBITMAP     G_hbmLoaded = NULLHANDLE;
  479.  
  480. PFNWP       G_pfnwpFrameOrig = NULL;
  481.  
  482. BOOL        G_fStartingUp = FALSE;
  483. BOOL        G_fUseLastOpenDir = FALSE;
  484.  
  485. // load-bitmap thread
  486. BOOL        G_fLoadingBitmap = FALSE;
  487. THREADINFO  G_tiLoadBitmap = {0};
  488.  
  489. // scroll bar data
  490. BITMAPINFOHEADER G_bmihBitmapLoaded;
  491. ULONG       G_ulVertScrollOfs,
  492.             G_ulHorzScrollOfs;
  493.  
  494. #define VERT_SCROLL_UNIT 10
  495. #define HORZ_SCROLL_UNIT 10
  496.  
  497. /*
  498.  *@@ G_GlobalSettings:
  499.  *      global settings.
  500.  */
  501.  
  502. struct
  503. {
  504.     BOOL    fResizeAfterLoad,
  505.             fConstrain2Screen,
  506.             fScale2WinSize;
  507. } G_GlobalSettings;
  508.  
  509. /* ******************************************************************
  510.  *
  511.  *   Command handler
  512.  *
  513.  ********************************************************************/
  514.  
  515. VOID UpdateTitle(VOID)
  516. {
  517.     CHAR szTitle[500] = "ximgview";
  518.  
  519.     if (G_hbmLoaded)
  520.     {
  521.         CHAR szName[CCHMAXPATH] = "",
  522.              szExt[CCHMAXPATH] = "";
  523.         _splitpath(G_szFilename, NULL, NULL, szName, szExt);
  524.         sprintf(szTitle + strlen(szTitle),
  525.                 " - %s%s (%dx%d, %d bpp)",
  526.                 szName, szExt,
  527.                 G_bmihBitmapLoaded.cx,
  528.                 G_bmihBitmapLoaded.cy,
  529.                 G_bmihBitmapLoaded.cBitCount);
  530.  
  531.     }
  532.  
  533.     WinSetWindowText(G_hwndMain, szTitle);
  534. }
  535.  
  536. /*
  537.  *@@ CalcViewportSize:
  538.  *
  539.  */
  540.  
  541. VOID CalcViewportSize(HWND hwndClient,
  542.                       SIZEL *pszl)
  543. {
  544.     SWP swpClient;
  545.     WinQueryWindowPos(hwndClient, &swpClient);
  546.             // this already has the scroll bars subtracted;
  547.             // apparently, these are frame controls...
  548.     pszl->cx = swpClient.cx; //  - WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL);
  549.     pszl->cy = swpClient.cy; //  - WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
  550. }
  551.  
  552. /*
  553.  *@@ UpdateScrollBars:
  554.  *
  555.  */
  556.  
  557. VOID UpdateScrollBars(HWND hwndClient)
  558. {
  559.     HWND hwndVScroll = WinWindowFromID(G_hwndMain, FID_VERTSCROLL);
  560.     HWND hwndHScroll = WinWindowFromID(G_hwndMain, FID_HORZSCROLL);
  561.  
  562.     if (G_GlobalSettings.fScale2WinSize)
  563.     {
  564.         WinEnableWindow(hwndVScroll, FALSE);
  565.         WinEnableWindow(hwndHScroll, FALSE);
  566.     }
  567.     else
  568.     {
  569.         SIZEL szlViewport;
  570.         CalcViewportSize(hwndClient, &szlViewport);
  571.  
  572.         _Pmpf(("bitmap cx: %d", G_bmihBitmapLoaded.cx));
  573.         _Pmpf(("viewport cx: %d", szlViewport.cx));
  574.  
  575.         // vertical (height)
  576.         winhUpdateScrollBar(hwndVScroll,
  577.                             szlViewport.cy,
  578.                             G_bmihBitmapLoaded.cy,
  579.                             G_ulVertScrollOfs,
  580.                             FALSE);      // auto-hide
  581.         // horizontal (width)
  582.         winhUpdateScrollBar(hwndHScroll,
  583.                             szlViewport.cx,
  584.                             G_bmihBitmapLoaded.cx,
  585.                             G_ulHorzScrollOfs,
  586.                             FALSE);      // auto-hide
  587.     }
  588. }
  589.  
  590. /*
  591.  *@@ UpdateMenuItems:
  592.  *
  593.  */
  594.  
  595. VOID UpdateMenuItems(VOID)
  596. {
  597.     HWND    hmenuMain = WinWindowFromID(G_hwndMain, FID_MENU);
  598.  
  599.     // file menu
  600.     WinEnableMenuItem(hmenuMain, IDM_FILE,
  601.                       // disable file menu if load-bitmap thread is running
  602.                       !G_fLoadingBitmap);
  603.  
  604.     // view menu
  605.     WinEnableMenuItem(hmenuMain, IDM_VIEW,
  606.                       // disable view menu if load-bitmap thread is running
  607.                       // or if we have no bitmap yet
  608.                       (     (!G_fLoadingBitmap)
  609.                         && (G_hbmLoaded != NULLHANDLE)
  610.                       ));
  611.     // options menu
  612.     WinCheckMenuItem(hmenuMain, IDMI_OPT_RESIZEAFTERLOAD,
  613.                      G_GlobalSettings.fResizeAfterLoad);
  614.     WinCheckMenuItem(hmenuMain, IDMI_OPT_CONSTRAIN2SCREEEN,
  615.                      G_GlobalSettings.fConstrain2Screen);
  616.     WinCheckMenuItem(hmenuMain, IDMI_OPT_SCALE2WINSIZE,
  617.                      G_GlobalSettings.fScale2WinSize);
  618. }
  619.  
  620. /*
  621.  *@@ SetWinSize2BmpSize:
  622.  *
  623.  */
  624.  
  625. VOID SetWinSize2BmpSize(VOID)
  626. {
  627.     SWP swpOld;
  628.     RECTL rcl;
  629.  
  630.     LONG   lNewCX,
  631.            lNewCY,
  632.            lNewY;
  633.  
  634.     // calculate new frame size needed:
  635.     // WinCalcFrameRect really wants screen
  636.     // coordinates, but we don't care here
  637.     rcl.xLeft = 0;
  638.     rcl.xRight = rcl.xLeft + G_bmihBitmapLoaded.cx;
  639.     rcl.yBottom = 0;
  640.     rcl.yTop = rcl.yBottom + G_bmihBitmapLoaded.cy;
  641.     WinCalcFrameRect(G_hwndMain,
  642.                      &rcl,
  643.                      FALSE);    // calc frame from client
  644.  
  645.     // new frame window size:
  646.     WinQueryWindowPos(G_hwndMain,
  647.                       &swpOld);
  648.     lNewCX = rcl.xRight - rcl.xLeft;
  649.     lNewCY = rcl.yTop - rcl.yBottom;
  650.  
  651.     lNewY = swpOld.y
  652.                   // calculate difference between old
  653.                   // and new cy:
  654.                   // if the new bitmap is larger, we
  655.                   // need to move y DOWN
  656.                   + swpOld.cy
  657.                   - lNewCY;
  658.  
  659.     // limit window size if off screen
  660.     if (G_GlobalSettings.fConstrain2Screen)
  661.     {
  662.         if ((swpOld.x + lNewCX) > winhQueryScreenCX())
  663.             lNewCX = winhQueryScreenCX() - swpOld.x;
  664.         if (lNewY < 0)
  665.         {
  666.             lNewY = 0;
  667.             lNewCY = swpOld.y + swpOld.cy;
  668.         }
  669.     }
  670.  
  671.     // now move/resize window:
  672.     // make the top left corner constant
  673.     WinSetWindowPos(G_hwndMain,
  674.                     HWND_TOP,
  675.                     // pos
  676.                     swpOld.x,         // don't change
  677.                     lNewY,
  678.                     // width
  679.                     lNewCX,
  680.                     lNewCY,
  681.                     SWP_MOVE | SWP_SIZE
  682.                         // in case the bitmap is loaded
  683.                         // as a startup parameter:
  684.                         | SWP_SHOW | SWP_ACTIVATE);
  685. }
  686.  
  687. /*
  688.  *@@ WMCommand_FileOpen:
  689.  *
  690.  */
  691.  
  692. BOOL WMCommand_FileOpen(HWND hwndClient)
  693. {
  694.     BOOL        brc = FALSE;
  695.  
  696.     CHAR        szFile[CCHMAXPATH] = "*";
  697.  
  698.     ULONG       flFlags = WINH_FOD_INISAVEDIR;
  699.  
  700.     if (G_fUseLastOpenDir)
  701.         // this is FALSE if we were loaded with a
  702.         // image file on the cmd line; in that case,
  703.         // use the startup directory
  704.         flFlags |= WINH_FOD_INILOADDIR;
  705.  
  706.     if (winhFileDlg(hwndClient,
  707.                     szFile,       // in: file mask; out: fully q'd filename
  708.                     flFlags,
  709.                     HINI_USER,
  710.                     INIAPP,
  711.                     INIKEY_LASTDIR))
  712.     {
  713.         strcpy(G_szFilename, szFile);
  714.         thrCreate(&G_tiLoadBitmap,
  715.                   fntLoadBitmap,
  716.                   &G_fLoadingBitmap,        // running flag
  717.                   "LoadBitmap",
  718.                   THRF_PMMSGQUEUE | THRF_WAIT,
  719.                   hwndClient);      // user param
  720.         UpdateMenuItems();
  721.  
  722.         // for subsequent opens, load from INI
  723.         G_fUseLastOpenDir = TRUE;
  724.     }
  725.  
  726.     return (brc);
  727. }
  728.  
  729. /*
  730.  *@@ WMDoneLoadingBitmap:
  731.  *      handler for WM_DONELOADINGBITMAP when
  732.  *      load-bitmap thread is done. ulError
  733.  *      has the error code (0 if none).
  734.  */
  735.  
  736. VOID WMDoneLoadingBitmap(HWND hwndClient,
  737.                          ULONG ulError,
  738.                          HBITMAP hbmNew)
  739. {
  740.     if (ulError)
  741.     {
  742.         CHAR szMsg[1000];
  743.         sprintf(szMsg,
  744.                 "Error %d loading \"%s\": %s",
  745.                 ulError,
  746.                 G_szFilename,
  747.                 QueryErrorDescription(ulError));
  748.  
  749.         if (G_fStartingUp)
  750.             // first call:
  751.             WinSetWindowPos(G_hwndMain, 0, 0,0,0,0, SWP_SHOW | SWP_ACTIVATE);
  752.         winhDebugBox(hwndClient, "Error", szMsg);
  753.     }
  754.     else
  755.     {
  756.         // free old bitmap
  757.         if (G_hbmLoaded)
  758.             GpiDeleteBitmap(G_hbmLoaded);
  759.  
  760.         G_hbmLoaded = hbmNew;
  761.  
  762.         GpiQueryBitmapParameters(G_hbmLoaded,
  763.                                  &G_bmihBitmapLoaded);
  764.         // reset scroller
  765.         G_ulVertScrollOfs = 0;
  766.         G_ulHorzScrollOfs = 0;
  767.  
  768.         if (G_GlobalSettings.fResizeAfterLoad)
  769.             SetWinSize2BmpSize();
  770.  
  771.         UpdateScrollBars(hwndClient);
  772.         WinInvalidateRect(hwndClient, NULL, FALSE);
  773.     }
  774.  
  775.     // wait till load-bitmap thread has really exited
  776.     thrWait(&G_tiLoadBitmap);
  777.  
  778.     UpdateTitle();
  779.     UpdateMenuItems();
  780.  
  781.     G_fStartingUp = FALSE;
  782. }
  783.  
  784. /*
  785.  *@@ WMClose_CanClose:
  786.  *      if this returns TRUE, the main window is
  787.  *      closed and the application is terminated.
  788.  *      Prompt for saving data here.
  789.  */
  790.  
  791. BOOL WMClose_CanClose(HWND hwndClient)
  792. {
  793.     return (TRUE);
  794. }
  795.  
  796. /* ******************************************************************
  797.  *
  798.  *   Main window
  799.  *
  800.  ********************************************************************/
  801.  
  802. /*
  803.  *@@ PaintClient:
  804.  *
  805.  */
  806.  
  807. VOID PaintClient(HWND hwndClient,
  808.                  HPS hps,
  809.                  PRECTL prclPaint)
  810. {
  811.     BOOL fFillBackground = TRUE;
  812.  
  813.     if (G_hbmLoaded)
  814.     {
  815.         POINTL ptlDest = {0, 0};
  816.         RECTL rclDest;
  817.         SIZEL szlViewport;
  818.         CalcViewportSize(hwndClient, &szlViewport);
  819.  
  820.         if (G_GlobalSettings.fScale2WinSize)
  821.         {
  822.             // "scale-to-window" mode: that's easy
  823.             RECTL rclClient;
  824.             WinQueryWindowRect(hwndClient, &rclClient);
  825.             WinDrawBitmap(hps,
  826.                           G_hbmLoaded,
  827.                           NULL,     // all
  828.                           (PPOINTL)&rclClient, // in stretch mode, this is
  829.                                                // used for the rectangle
  830.                           CLR_BLACK,    // in case we have a b/w bmp
  831.                           CLR_WHITE,    // in case we have a b/w bmp
  832.                           DBM_NORMAL | DBM_STRETCH);
  833.             fFillBackground = FALSE;
  834.         }
  835.         else
  836.         {
  837.             // original size mode:
  838.             if (G_bmihBitmapLoaded.cy < szlViewport.cy)
  839.                 // center vertically:
  840.                 ptlDest.y = (szlViewport.cy - G_bmihBitmapLoaded.cy) / 2;
  841.             else
  842.             {
  843.                 // use scroller offset:
  844.                 // calc leftover space
  845.                 ULONG ulClipped = G_bmihBitmapLoaded.cy - szlViewport.cy;
  846.                 // this is a positive value if the scroller is
  847.                 // down from the top or 0 if its at the top.
  848.                 // So if it's zero, we must use -ulClipped.
  849.                 ptlDest.y -= ulClipped;
  850.                 // if it's above zero, we must add to y.
  851.                 ptlDest.y += G_ulVertScrollOfs;
  852.             }
  853.  
  854.             if (G_bmihBitmapLoaded.cx < szlViewport.cx)
  855.                 // center horizontally:
  856.                 ptlDest.x = (szlViewport.cx - G_bmihBitmapLoaded.cx) / 2;
  857.             else
  858.             {
  859.                 // use scroller offset:
  860.                 // calc leftover space
  861.                 ULONG ulClipped = G_bmihBitmapLoaded.cx - szlViewport.cx;
  862.                 // this is a positive value if the scroller is
  863.                 // right from the left or 0 if its at the left.
  864.                 // So if it's zero, we must use -ulClipped.
  865.                 // ptlDest.x -= ulClipped;
  866.                 // if it's above zero, we must further subtract from x.
  867.                 ptlDest.x -= G_ulHorzScrollOfs;
  868.             }
  869.  
  870.             WinDrawBitmap(hps,
  871.                           G_hbmLoaded,
  872.                           NULL,        // subrectangle to be drawn
  873.                           &ptlDest,
  874.                           0, 0,
  875.                           DBM_NORMAL);
  876.  
  877.             // cut out the rectangle of the bitmap we just
  878.             // painted from the clipping region so that
  879.             // WinFillRect below cannot overwrite it
  880.             rclDest.xLeft = ptlDest.x;
  881.             rclDest.yBottom = ptlDest.y;
  882.             rclDest.xRight = ptlDest.x + G_bmihBitmapLoaded.cx;
  883.             rclDest.yTop = ptlDest.y + G_bmihBitmapLoaded.cy;
  884.             GpiExcludeClipRectangle(hps,
  885.                                     &rclDest);
  886.         }
  887.     }
  888.  
  889.     if (fFillBackground)
  890.         // fill the remainder white
  891.         WinFillRect(hps, prclPaint, CLR_WHITE);
  892. }
  893.  
  894. /*
  895.  *@@ fnwpMainClient:
  896.  *
  897.  */
  898.  
  899. MRESULT EXPENTRY fnwpMainClient(HWND hwndClient, ULONG msg, MPARAM mp1, MPARAM mp2)
  900. {
  901.     MRESULT mrc = 0;
  902.  
  903.     switch (msg)
  904.     {
  905.         /*
  906.          * WM_COMMAND:
  907.          *
  908.          */
  909.  
  910.         case WM_COMMAND:
  911.         {
  912.             USHORT usCmd = SHORT1FROMMP(mp1);
  913.             switch (usCmd)
  914.             {
  915.                 case IDMI_FILE_OPEN:
  916.                     WMCommand_FileOpen(hwndClient);
  917.                 break;
  918.  
  919.                 case IDMI_FILE_EXIT:
  920.                     WinPostMsg(G_hwndMain, WM_CLOSE, 0, 0);
  921.                 break;
  922.  
  923.                 case IDMI_VIEW_SIZEORIG:
  924.                     SetWinSize2BmpSize();
  925.                 break;
  926.  
  927.                 case IDMI_OPT_RESIZEAFTERLOAD:
  928.                     G_GlobalSettings.fResizeAfterLoad = !G_GlobalSettings.fResizeAfterLoad;
  929.                     UpdateMenuItems();
  930.                 break;
  931.  
  932.                 case IDMI_OPT_CONSTRAIN2SCREEEN:
  933.                     G_GlobalSettings.fConstrain2Screen = !G_GlobalSettings.fConstrain2Screen;
  934.                     UpdateMenuItems();
  935.                 break;
  936.  
  937.                 case IDMI_OPT_SCALE2WINSIZE:
  938.                     G_GlobalSettings.fScale2WinSize = !G_GlobalSettings.fScale2WinSize;
  939.                     UpdateMenuItems();
  940.                     UpdateScrollBars(hwndClient);
  941.                     WinInvalidateRect(hwndClient, NULL, FALSE);
  942.                 break;
  943.             }
  944.         break; }
  945.  
  946.         /*
  947.          *@@ WM_DONELOADINGBMP:
  948.          *      load-bitmap thread is done.
  949.          *      Parameters:
  950.          *      -- ULONG mp1: error code.
  951.          *      -- HBITMAP mp2: new bitmap handle.
  952.          */
  953.  
  954.         case WM_DONELOADINGBMP:
  955.             WMDoneLoadingBitmap(hwndClient,
  956.                                 (ULONG)mp1,
  957.                                 (HBITMAP)mp2);
  958.         break;
  959.  
  960.         /*
  961.          * WM_SIZE:
  962.          *
  963.          */
  964.  
  965.         case WM_SIZE:
  966.             UpdateScrollBars(hwndClient);
  967.             WinInvalidateRect(hwndClient, NULL, FALSE);
  968.         break;
  969.  
  970.         /*
  971.          * WM_PAINT:
  972.          *
  973.          */
  974.  
  975.         case WM_PAINT:
  976.         {
  977.             RECTL rclPaint;
  978.             HPS hps = WinBeginPaint(hwndClient,
  979.                                     NULLHANDLE,
  980.                                     &rclPaint);
  981.             PaintClient(hwndClient,
  982.                         hps,
  983.                         &rclPaint);
  984.             WinEndPaint(hps);
  985.         break; }
  986.  
  987.         case WM_VSCROLL:
  988.         {
  989.             RECTL rcl;
  990.             WinQueryWindowRect(hwndClient, &rcl);
  991.             winhHandleScrollMsg(hwndClient,
  992.                                 WinWindowFromID(G_hwndMain, FID_VERTSCROLL),
  993.                                 &G_ulVertScrollOfs,
  994.                                 &rcl,
  995.                                 G_bmihBitmapLoaded.cy,
  996.                                 10,
  997.                                 msg,
  998.                                 mp2);
  999.         break; }
  1000.  
  1001.         case WM_HSCROLL:
  1002.         {
  1003.             RECTL rcl;
  1004.             WinQueryWindowRect(hwndClient, &rcl);
  1005.             _Pmpf(("WM_HSCROLL"));
  1006.             winhHandleScrollMsg(hwndClient,
  1007.                                 WinWindowFromID(G_hwndMain, FID_HORZSCROLL),
  1008.                                 &G_ulHorzScrollOfs,
  1009.                                 &rcl,
  1010.                                 G_bmihBitmapLoaded.cx,
  1011.                                 10,
  1012.                                 msg,
  1013.                                 mp2);
  1014.             _Pmpf(("End of WM_HSCROLL"));
  1015.         break; }
  1016.  
  1017.         case WM_CLOSE:
  1018.             if (WMClose_CanClose(hwndClient))
  1019.                 winhSaveWindowPos(G_hwndMain,
  1020.                                   HINI_USER,
  1021.                                   INIAPP,
  1022.                                   INIKEY_MAINWINPOS);
  1023.             WinPostMsg(hwndClient, WM_QUIT, 0, 0);
  1024.         break;
  1025.  
  1026.         case WM_CHAR:
  1027.             mrc = (MRESULT)winhProcessScrollChars(hwndClient,
  1028.                                                   WinWindowFromID(G_hwndMain, FID_VERTSCROLL),
  1029.                                                   WinWindowFromID(G_hwndMain, FID_HORZSCROLL),
  1030.                                                   mp1,
  1031.                                                   mp2,
  1032.                                                   G_bmihBitmapLoaded.cy,
  1033.                                                   G_bmihBitmapLoaded.cx);
  1034.         break;
  1035.  
  1036.         default:
  1037.             mrc = WinDefWindowProc(hwndClient, msg, mp1, mp2);
  1038.     }
  1039.     return (mrc);
  1040. }
  1041.  
  1042. /*
  1043.  *@@ fnwpMainFrame:
  1044.  *
  1045.  */
  1046.  
  1047. MRESULT EXPENTRY fnwpMainFrame(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
  1048. {
  1049.     MRESULT mrc = 0;
  1050.  
  1051.     switch (msg)
  1052.     {
  1053.         case WM_QUERYTRACKINFO:
  1054.         {
  1055.             PTRACKINFO pti = (PTRACKINFO)mp2;
  1056.  
  1057.             G_pfnwpFrameOrig(hwndFrame, msg, mp1, mp2);
  1058.  
  1059.             if (pti->ptlMinTrackSize.x < 100)
  1060.                 pti->ptlMinTrackSize.x = 100;
  1061.             if (pti->ptlMinTrackSize.y < 100)
  1062.                 pti->ptlMinTrackSize.y = 100;
  1063.             mrc = (MRESULT)TRUE;
  1064.         break; }
  1065.  
  1066.         default:
  1067.             mrc = G_pfnwpFrameOrig(hwndFrame, msg, mp1, mp2);
  1068.     }
  1069.  
  1070.     return (mrc);
  1071. }
  1072.  
  1073. /*
  1074.  * main:
  1075.  *      program entry point.
  1076.  */
  1077.  
  1078. int main(int argc,
  1079.          char *argv[])
  1080. {
  1081.     QMSG        qmsg;
  1082.     HWND        hwndClient;
  1083.     ULONG       cbGlobalSettings = sizeof(G_GlobalSettings);
  1084.     ULONG       flSwpFlags;
  1085.  
  1086.     G_fStartingUp = TRUE;
  1087.  
  1088.     if (!(G_habMain = WinInitialize(0)))
  1089.         return FALSE;
  1090.  
  1091.     if (!(G_hmqMain = WinCreateMsgQueue(G_habMain, 0)))
  1092.         return FALSE;
  1093.  
  1094.     // initialize global settings
  1095.     memset(&G_GlobalSettings, 0, sizeof(G_GlobalSettings));
  1096.     PrfQueryProfileData(HINI_USER,
  1097.                         INIAPP,
  1098.                         INIKEY_SETTINGS,
  1099.                         &G_GlobalSettings,
  1100.                         &cbGlobalSettings);
  1101.  
  1102.     WinRegisterClass(G_habMain,
  1103.                      WC_MY_CLIENT_CLASS,
  1104.                      fnwpMainClient,
  1105.                      CS_SIZEREDRAW,
  1106.                      sizeof(ULONG));
  1107.  
  1108.     G_hwndMain = winhCreateStdWindow(HWND_DESKTOP,
  1109.                                      0,
  1110.                                      FCF_TITLEBAR
  1111.                                         | FCF_SYSMENU
  1112.                                         | FCF_MINMAX
  1113.                                         | FCF_VERTSCROLL
  1114.                                         | FCF_HORZSCROLL
  1115.                                         | FCF_SIZEBORDER
  1116.                                         | FCF_ICON
  1117.                                         | FCF_MENU
  1118.                                         | FCF_TASKLIST,
  1119.                                      0, // WS_VISIBLE,
  1120.                                      "Title",
  1121.                                      1,     // icon resource
  1122.                                      WC_MY_CLIENT_CLASS,
  1123.                                      WS_VISIBLE,
  1124.                                      0,
  1125.                                      NULL,
  1126.                                      &hwndClient);
  1127.  
  1128.     G_pfnwpFrameOrig = WinSubclassWindow(G_hwndMain,
  1129.                                          fnwpMainFrame);
  1130.  
  1131.     // standard SWP flags
  1132.     flSwpFlags = SWP_MOVE | SWP_SIZE;
  1133.  
  1134.     // parse args
  1135.     if (argc > 1)
  1136.     {
  1137.         HDC hdcMem;
  1138.         HBITMAP hbmNew;
  1139.         HWND hwndShape;
  1140.         ULONG ulrc;
  1141.         strcpy(G_szFilename, argv[1]);
  1142.         thrCreate(&G_tiLoadBitmap,
  1143.                   fntLoadBitmap,
  1144.                   &G_fLoadingBitmap,        // running flag
  1145.                   "LoadBitmap",
  1146.                   THRF_PMMSGQUEUE | THRF_WAIT,
  1147.                   hwndClient);      // user param
  1148.     }
  1149.     else
  1150.     {
  1151.         // no parameter:
  1152.         // show window initially
  1153.         flSwpFlags |= SWP_SHOW | SWP_ACTIVATE;
  1154.         // and retrieve last open dir from OS2.INI
  1155.         G_fUseLastOpenDir = TRUE;
  1156.     }
  1157.  
  1158.     UpdateTitle();
  1159.     UpdateMenuItems();
  1160.  
  1161.     if (!winhRestoreWindowPos(G_hwndMain,
  1162.                               HINI_USER,
  1163.                               INIAPP,
  1164.                               INIKEY_MAINWINPOS,
  1165.                               flSwpFlags))
  1166.         // standard pos
  1167.         WinSetWindowPos(G_hwndMain,
  1168.                         HWND_TOP,
  1169.                         10, 10, 500, 500,
  1170.                         flSwpFlags);
  1171.  
  1172.     //  standard PM message loop
  1173.     while (WinGetMsg(G_habMain, &qmsg, NULLHANDLE, 0, 0))
  1174.     {
  1175.         WinDispatchMsg(G_habMain, &qmsg);
  1176.     }
  1177.  
  1178.     // initialize global settings
  1179.     PrfWriteProfileData(HINI_USER,
  1180.                         INIAPP,
  1181.                         INIKEY_SETTINGS,
  1182.                         &G_GlobalSettings,
  1183.                         sizeof(G_GlobalSettings));
  1184.  
  1185.     // clean up on the way out
  1186.     WinDestroyMsgQueue(G_hmqMain);
  1187.     WinTerminate(G_habMain);
  1188.  
  1189.     return TRUE;
  1190. }
  1191.  
  1192.  
  1193.