home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gbmos2pm.zip / gbmv2 / gbmv2.c < prev    next >
C/C++ Source or Header  |  1996-11-15  |  78KB  |  3,052 lines

  1. /*
  2.  
  3. gbmv2.c - Display a bitmap
  4.  
  5. */
  6.  
  7. /*...sincludes:0:*/
  8. #define    INCL_DOS
  9. #define    INCL_WIN
  10. #define    INCL_GPI
  11. #define    INCL_DEV
  12. #define    INCL_SPL
  13. #define    INCL_SPLDOSPRINT
  14. #define    INCL_BITMAPFILEFORMAT
  15. #include <os2.h>
  16. #include <stdio.h>
  17. #include <ctype.h>
  18. #include <stdlib.h>
  19. #include <stdarg.h>
  20. #include <string.h>
  21. #include <malloc.h>
  22. #include <memory.h>
  23. #include <process.h>
  24. #include "scroll.h"
  25. #include "gbm.h"
  26. #include "gbmdlg.h"
  27. #include "gbmdlgrc.h"
  28. #include "gbmv2.h"
  29. #include "gbmv2hlp.h"
  30. #include "model.h"
  31. #include "bmputils.h"
  32. #include "help.h"
  33.  
  34. /*...vscroll\46\h:0:*/
  35. /*...vgbm\46\h:0:*/
  36. /*...vgbmdlg\46\h:0:*/
  37. /*...vgbmdlgrc\46\h:0:*/
  38. /*...vgbmv2\46\h:0:*/
  39. /*...vgbmv2hlp\46\h:0:*/
  40. /*...vmodel\46\h:0:*/
  41. /*...vbmputils\46\h:0:*/
  42. /*...vhelp\46\h:0:*/
  43. /*...e*/
  44. /*...ssuppress warnings:0:*/
  45. /* When using VisualAge C++, it complains about the definition of
  46.    MPFROM2SHORT (well actually MAKELONG), so I've coded a replacement. */
  47.  
  48. #undef MPFROM2SHORT
  49. #define    MY_MAKELONG(l,h) ((ULONG)(((USHORT)(l)) | ( ((ULONG)((USHORT)(h))) << 16 ) ))
  50. #define    MPFROM2SHORT(l,h) ((MPARAM) MY_MAKELONG(l,h))
  51. /*...e*/
  52. /*...suseful:0:*/
  53. /* Windows has these 2 */
  54.  
  55. /*...sEnableMenuItem:0:*/
  56. static VOID EnableMenuItem(HWND hWndMenu, SHORT idMenuItem, BOOL fEnabled)
  57.     {
  58.     WinSendMsg(hWndMenu, MM_SETITEMATTR,
  59.            MPFROM2SHORT(idMenuItem, TRUE),
  60.            MPFROM2SHORT(MIA_DISABLED, ( fEnabled ) ? 0 : MIA_DISABLED));
  61.     }
  62. /*...e*/
  63. /*...sCheckMenuItem:0:*/
  64. static VOID CheckMenuItem(HWND hWndMenu, SHORT idMenuItem, BOOL fChecked)
  65.     {
  66.     WinSendMsg(hWndMenu, MM_SETITEMATTR,
  67.            MPFROM2SHORT(idMenuItem, TRUE),
  68.            MPFROM2SHORT(MIA_CHECKED, ( fChecked ) ? MIA_CHECKED : 0));
  69.     }
  70. /*...e*/
  71.  
  72. /* These are generally useful */
  73.  
  74. /*...sWarning:0:*/
  75. static VOID Warning(HWND hwnd, const CHAR *szFmt, ...)
  76.     {
  77.     va_list vars;
  78.     CHAR sz[256+1];
  79.  
  80.     va_start(vars, szFmt);
  81.     vsprintf(sz, szFmt, vars);
  82.     va_end(vars);
  83.     WinMessageBox(HWND_DESKTOP, hwnd, sz, NULL, 0, MB_OK | MB_WARNING | MB_MOVEABLE);
  84.     }
  85. /*...e*/
  86.  
  87. /*...sTidySysMenu:0:*/
  88. static VOID TidySysMenu(HWND hwndFrame)
  89.     {
  90.     HWND hwndSysMenu = WinWindowFromID(hwndFrame, FID_SYSMENU);
  91.     USHORT id        = SHORT1FROMMR(WinSendMsg(hwndSysMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(0), NULL));
  92.     MENUITEM mi;
  93.     HWND hwndSysSubMenu;
  94.     SHORT sInx, cItems;
  95.     
  96.     WinSendMsg(hwndSysMenu, MM_QUERYITEM, MPFROM2SHORT(id, FALSE), MPFROMP(&mi));
  97.     hwndSysSubMenu = mi.hwndSubMenu;
  98.     
  99.     cItems = SHORT1FROMMR(WinSendMsg(hwndSysSubMenu, MM_QUERYITEMCOUNT, NULL, NULL));
  100.     
  101.     for ( sInx = cItems - 1; sInx >= 0; sInx-- )
  102.         {
  103.         id = SHORT1FROMMR(WinSendMsg(hwndSysSubMenu, MM_ITEMIDFROMPOSITION, MPFROMSHORT(sInx), NULL));
  104.  
  105.         WinSendMsg(hwndSysSubMenu, MM_QUERYITEM,
  106.                MPFROM2SHORT(id, FALSE), MPFROMP(&mi));
  107.         if ( (mi.afStyle     & MIS_SEPARATOR) != 0 ||
  108.              (mi.afAttribute & MIA_DISABLED ) != 0 ||
  109.              id == (USHORT) SC_TASKMANAGER         )
  110.             WinSendMsg(hwndSysSubMenu, MM_DELETEITEM,
  111.                    MPFROM2SHORT(id, FALSE), NULL);
  112.         }
  113.     }    
  114. /*...e*/
  115. /*...sRestrictEntryfield:0:*/
  116. /*
  117. This is a function you call on an WC_ENTRYFIELD window that causes the window
  118. to only allow strings in one set to be used and also not allow strings in
  119. another set.
  120. */
  121.  
  122. typedef struct /* restr */
  123.     {
  124.     const CHAR *szIfIn;
  125.     const CHAR *szMapTo;
  126.     const CHAR *szAllowed;
  127.     const CHAR *szNotAllowed;
  128.     PFNWP pfnwpOld;
  129.     } RESTR;
  130.  
  131. /*...sRestrictSubProc:0:*/
  132. #define L_ENTRYMAX      500
  133.  
  134. /*...sMapChar:0:*/
  135. static CHAR MapChar(RESTR *prestr, CHAR ch)
  136.     {
  137.     CHAR *sz;
  138.  
  139.     if ( prestr -> szIfIn == NULL || prestr -> szMapTo == NULL )
  140.             return ch;
  141.  
  142.     if ( (sz = strchr(prestr -> szIfIn, ch)) == NULL )
  143.             return ch;
  144.  
  145.     return prestr -> szMapTo[sz - prestr -> szIfIn];
  146.     }
  147. /*...e*/
  148. /*...sProblemAllowed:0:*/
  149. static BOOL ProblemAllowed(RESTR *prestr, CHAR ch)
  150.     {
  151.     return prestr -> szAllowed != NULL && strchr(prestr -> szAllowed, ch) == NULL;
  152.     }                                                        /* More >>> */
  153. /*...e*/
  154. /*...sProblemNotAllowed:0:*/
  155. static BOOL ProblemNotAllowed(RESTR *prestr, CHAR ch)
  156.     {
  157.     return prestr -> szNotAllowed != NULL && strchr(prestr -> szNotAllowed, ch) != NULL;
  158.     }                                                        /* More >>> */
  159. /*...e*/
  160.  
  161. MRESULT _System RestrictSubProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  162.     {
  163.     RESTR *prestr = (RESTR *) WinQueryWindowULong(hwnd, QWL_USER);
  164.     PFNWP pfnwpOld = prestr -> pfnwpOld;
  165.  
  166.     switch ( (int) msg )
  167.             {
  168. /*...sWM_DESTROY \45\ clean up:16:*/
  169. case WM_DESTROY:
  170.     free(prestr);
  171.     break;
  172. /*...e*/
  173. /*...sWM_CHAR    \45\ map incoming chars:16:*/
  174. case WM_CHAR:
  175.     {
  176.     USHORT fs     = SHORT1FROMMP(mp1);
  177.     CHAR   ch     = (CHAR) SHORT1FROMMP(mp2);
  178.     USHORT vkey   = SHORT2FROMMP(mp2);
  179.  
  180.     if ( (fs & KC_VIRTUALKEY) != 0 && (fs & KC_ALT) != 0 &&
  181.          (vkey == VK_LEFT || vkey == VK_RIGHT || vkey == VK_UP || vkey == VK_DOWN) )
  182.         /* If we let the entryfield get these, it won't process them */
  183.         /* It will pass them to its owner, the dialog. */
  184.         /* The dialog will use them to change the focus! */
  185.         /* This is not desired, as Alt+NNN is being used */
  186.         return (MRESULT) 0;
  187.  
  188.     if ( (fs & KC_CHAR) != 0 && ch != '\b' && ch != '\t' )
  189.             {
  190.             ch = MapChar(prestr, ch);
  191.  
  192.             if ( ProblemAllowed(prestr, ch) ||
  193.                  ProblemNotAllowed(prestr, ch) )
  194.                     {
  195.                     WinAlarm(HWND_DESKTOP, WA_WARNING);
  196.                     return (MRESULT) 0;
  197.                     }
  198.  
  199.             mp2 = MPFROM2SHORT(ch, vkey);
  200.             }
  201.     }
  202.     break;
  203. /*...e*/
  204. /*...sEM_PASTE   \45\ paste then alter chars:16:*/
  205. case EM_PASTE:
  206.     {
  207.     MRESULT mr = (*pfnwpOld)(hwnd, msg, mp1, mp2);
  208.     MRESULT mrSel = WinSendMsg(hwnd, EM_QUERYSEL, NULL, NULL);
  209.     CHAR sz[L_ENTRYMAX+1], *szTmp;
  210.  
  211.     WinQueryWindowText(hwnd, L_ENTRYMAX, sz);
  212.     for ( szTmp = sz; *szTmp; szTmp++ )
  213.             {
  214.             *szTmp = MapChar(prestr, *szTmp);
  215.             if ( ProblemAllowed(prestr, *szTmp) ||
  216.                  ProblemNotAllowed(prestr, *szTmp) )
  217.                     {
  218.                     strcpy(szTmp, szTmp + 1);
  219.                     szTmp--;
  220.                     }
  221.             }
  222.     WinSetWindowText(hwnd, sz);
  223.     WinSendMsg(hwnd, EM_SETSEL, mrSel, NULL);
  224.  
  225.     return mr;
  226.     }
  227. /*...e*/
  228.             }
  229.     return (*pfnwpOld)(hwnd, msg, mp1, mp2);
  230.     }
  231. /*...e*/
  232.  
  233. static BOOL RestrictEntryfield(
  234.     HWND hwnd,
  235.     const CHAR *szIfIn,
  236.     const CHAR *szMapTo,
  237.     const CHAR *szAllowed,
  238.     const CHAR *szNotAllowed
  239.     )
  240.     {
  241.     RESTR *prestr;
  242.  
  243.     if ( (prestr = malloc(sizeof(RESTR))) == NULL )
  244.             return FALSE;
  245.  
  246.     prestr -> szIfIn       = szIfIn      ;
  247.     prestr -> szMapTo      = szMapTo     ;
  248.     prestr -> szAllowed    = szAllowed   ;
  249.     prestr -> szNotAllowed = szNotAllowed;
  250.     prestr -> pfnwpOld     = WinSubclassWindow(hwnd, RestrictSubProc);
  251.  
  252.     WinSetWindowULong(hwnd, QWL_USER, (LONG) prestr);
  253.  
  254.     return TRUE;
  255.     }
  256. /*...e*/
  257. /*...e*/
  258. /*...spriority:0:*/
  259. static VOID LowPri(VOID)
  260.     {
  261. #ifdef NEVER
  262.     DosSetPriority(PRTYS_THREAD, PRTYC_IDLETIME, 0, 0);
  263. #endif
  264.     DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MINIMUM, 0);
  265.     }
  266.  
  267. static VOID RegPri(VOID)
  268.     {
  269.     DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, 0, 0);
  270.     }
  271. /*...e*/
  272. /*...svars:0:*/
  273. /* Global single help instance */
  274. static HWND hwndHelp;
  275.  
  276. /* Application name */
  277. static CHAR szAppName[] = "GbmV2";
  278.  
  279. /* Whats the bitmap data called */
  280. #define    MAX_FILE_NAME 256
  281. static CHAR szFileName[MAX_FILE_NAME+1] = "";
  282. static CHAR *FileName(VOID) { return szFileName[0] != '\0' ? szFileName : "(untitled)"; }
  283.  
  284. /* The bitmap data itself */
  285. static BOOL fGotBitmap = FALSE;
  286. static BOOL fUnsavedChanges = FALSE;
  287. static MOD mod;
  288.  
  289. /* How we like to view the bitmaps */
  290. #define    VIEW_NULL    0
  291. #define    VIEW_HALFTONE    1
  292. #define    VIEW_ERRDIFF    2
  293. static BYTE bView = VIEW_NULL;
  294. static LONG lBitCountScreen;
  295.  
  296. /* General application vars */
  297. static BOOL fBusy = FALSE;
  298. static HWND hwndObject;
  299. static HWND hwndBitmap;
  300. static HWND hwndScroller;
  301.  
  302. /* Selections in the current bitmap */
  303. static BOOL fSelectionDefined = FALSE;
  304. static RECTL rclSelection;
  305. /*...e*/
  306. /*...sundo:0:*/
  307. /* Most replaced data gets stored here, so we can bring it back later */
  308.  
  309. static BOOL fCanUndo = FALSE;
  310. static MOD modUndo;
  311. static const CHAR *szWhatUndo;
  312.  
  313. /*...sDiscardUndo:0:*/
  314. static VOID DiscardUndo(VOID)
  315.     {
  316.     if ( fCanUndo )
  317.         {
  318.         ModDelete(&modUndo);
  319.         fCanUndo = FALSE;
  320.         }
  321.     }
  322. /*...e*/
  323. /*...sKeepForUndo:0:*/
  324. static VOID KeepForUndo(const MOD *mod, const CHAR *szWhat)
  325.     {
  326.     DiscardUndo();
  327.     if ( ModCopy(mod, &modUndo) != MOD_ERR_OK )
  328.         return; /* Silently fail to keep undo information */
  329.     szWhatUndo = szWhat;
  330.     fCanUndo = TRUE;
  331.     }
  332. /*...e*/
  333. /*...sUseUndoBuffer:0:*/
  334. static VOID UseUndoBuffer(
  335.     MOD *modToUndoTo,
  336.     const CHAR **pszWhat
  337.     )
  338.     {
  339.     ModMove(&modUndo, modToUndoTo);
  340.     *pszWhat = szWhatUndo;
  341.     fCanUndo = FALSE;
  342.     }
  343. /*...e*/
  344. /*...e*/
  345. /*...sCaption:0:*/
  346. static VOID Caption(HWND hwndClient, const CHAR *szFmt, ...)
  347.     {
  348.     va_list vars;
  349.     HWND hwndFrame = WinQueryWindow(hwndClient, QW_PARENT);
  350.     CHAR sz[50+MAX_FILE_NAME+50+1], *szAppend;
  351.  
  352.     strcpy(sz, szAppName);
  353.  
  354.     szAppend = sz + strlen(sz);
  355.  
  356.     va_start(vars, szFmt);
  357.     vsprintf(szAppend, szFmt, vars);
  358.     va_end(vars);
  359.  
  360.     WinSetWindowText(hwndFrame, sz);
  361.     }
  362. /*...e*/
  363. /*...sMakeVisual:0:*/
  364. static BOOL MakeVisual(
  365.     HWND hwnd,
  366.     MOD *mod,
  367.     BYTE bView,
  368.     HBITMAP *phbm, LONG *plColorBg, LONG *plColorFg
  369.     )
  370.     {
  371.     HAB hab = WinQueryAnchorBlock(hwnd);
  372.     MOD_ERR mrc = MOD_ERR_OK; MOD modTmp, *modVisual = mod;
  373.     BOOL fOk;
  374.  
  375.     switch ( bView )
  376.         {
  377. /*...sVIEW_HALFTONE:16:*/
  378. case VIEW_HALFTONE:
  379.     switch ( lBitCountScreen )
  380.         {
  381.         case 1:
  382.             /* Are there any 1bpp screens still in existence? */
  383.             mrc = ModBppMap(mod, CVT_BW, CVT_NEAREST, 8,8,8, 2, &modTmp);
  384.             modVisual = &modTmp;
  385.             break;
  386.         case 4:
  387.             mrc = ModBppMap(mod, CVT_VGA, CVT_HALFTONE, 8,8,8, 16, &modTmp);
  388.             modVisual = &modTmp;
  389.             break;
  390.         case 8:
  391.             mrc = ModBppMap(mod, CVT_784, CVT_HALFTONE, 8,8,8, 256, &modTmp);
  392.             modVisual = &modTmp;
  393.             break;
  394.         case 16:
  395.             mrc = ModBppMap(mod, CVT_RGB, CVT_HALFTONE, 5,6,5, 65536, &modTmp);
  396.             modVisual = &modTmp;
  397.             break;
  398.         }
  399.     break;
  400. /*...e*/
  401. /*...sVIEW_ERRDIFF:16:*/
  402. case VIEW_ERRDIFF:
  403.     switch ( lBitCountScreen )
  404.         {
  405.         case 1:
  406.             /* Are there any 1bpp screens still in existence? */
  407.             mrc = ModBppMap(mod, CVT_BW, CVT_ERRDIFF, 8,8,8, 2, &modTmp);
  408.             modVisual = &modTmp;
  409.             break;
  410.         case 4:
  411.             mrc = ModBppMap(mod, CVT_VGA, CVT_ERRDIFF, 8,8,8, 16, &modTmp);
  412.             modVisual = &modTmp;
  413.             break;
  414.         case 8:
  415.             mrc = ModBppMap(mod, CVT_784, CVT_ERRDIFF, 8,8,8, 256, &modTmp);
  416.             modVisual = &modTmp;
  417.             break;
  418.         case 16:
  419.             mrc = ModBppMap(mod, CVT_RGB, CVT_ERRDIFF, 5,6,5, 65536, &modTmp);
  420.             modVisual = &modTmp;
  421.             break;
  422.         }
  423.     break;
  424. /*...e*/
  425.         }
  426.  
  427.     if ( mrc != MOD_ERR_OK )
  428.         {
  429.         ModDelete(modVisual);
  430.         return FALSE; /* Unable to make improved quality bitmap */
  431.         }
  432.  
  433.     if ( modVisual->gbm.bpp == 1 )
  434. /*...sremember Bg and Fg colours:16:*/
  435. {
  436. *plColorBg = (modVisual->gbmrgb[0].r << 16) + (modVisual->gbmrgb[0].g << 8) + modVisual->gbmrgb[0].b;
  437. *plColorFg = (modVisual->gbmrgb[1].r << 16) + (modVisual->gbmrgb[1].g << 8) + modVisual->gbmrgb[1].b;
  438. }
  439. /*...e*/
  440.  
  441.     fOk = ( ModMakeHBITMAP(modVisual, hab, phbm) == MOD_ERR_OK );
  442.     if ( modVisual != mod )
  443.         ModDelete(modVisual);
  444.     return fOk;
  445.     }
  446. /*...e*/
  447. /*...sMakeVisualBg:0:*/
  448. static BOOL MakeVisualBg(
  449.     HWND hwnd,
  450.     MOD *mod,
  451.     BYTE bView,
  452.     HBITMAP *phbm, LONG *plColorBg, LONG *plColorFg,
  453.     CHAR *szFileName
  454.     )
  455.     {
  456.     LowPri();
  457.     /* Tentatively use new supplied filename, rather than current */
  458.     Caption(hwnd, " - rendering %s", szFileName);
  459.     if ( !MakeVisual(hwnd, mod, bView, phbm, plColorBg, plColorFg) )
  460.         {
  461.         /* Revert to previous filename */
  462.         Caption(hwnd, " - %s", FileName());
  463.         RegPri();
  464.         Warning(hwnd, "Error creating view bitmap");
  465.         return FALSE;
  466.         }
  467.     /* Leave with new supplied filename on display */
  468.     Caption(hwnd, " - %s", szFileName);
  469.     RegPri();
  470.     return TRUE;
  471.     }
  472. /*...e*/
  473. /*...sSaveChanges:0:*/
  474. /*
  475. Returns TRUE if changes saved ok.
  476. Returns FALSE if cancel selected.
  477. */
  478.  
  479. static BOOL SaveChanges(HWND hwnd)
  480.     {
  481.     CHAR sz[255];
  482.  
  483.     if ( !fGotBitmap )
  484.         return TRUE;
  485.  
  486.     if ( !fUnsavedChanges )
  487.         return TRUE;
  488.  
  489.     sprintf(sz, "Save current changes: %s", FileName());
  490.     switch ( WinMessageBox(HWND_DESKTOP, hwnd, sz, szAppName, 0, MB_YESNOCANCEL | MB_WARNING | MB_MOVEABLE) )
  491.         {
  492.         case MBID_YES:
  493.             {
  494.             GBMFILEDLG gbmfild;
  495.             MOD_ERR mrc;
  496.  
  497.             memset(&gbmfild.fild, 0, sizeof(FILEDLG));
  498.             gbmfild.fild.cbSize = sizeof(FILEDLG);
  499.             gbmfild.fild.fl = (FDS_CENTER|FDS_SAVEAS_DIALOG|FDS_HELPBUTTON);
  500.             strcpy(gbmfild.fild.szFullFile, szFileName);
  501.             strcpy(gbmfild.szOptions, "");    
  502.             while ( gbmfild.fild.szFullFile[0] == '\0' )
  503.                 /* Try to correct filename */
  504.                 {
  505.                 GbmFileDlg(HWND_DESKTOP, hwnd, &gbmfild);
  506.                 if ( gbmfild.fild.lReturn != DID_OK )
  507.                     return FALSE;
  508.                 }
  509.             if ( (mrc = ModWriteToFile(&mod, gbmfild.fild.szFullFile, gbmfild.szOptions)) != MOD_ERR_OK )
  510.                 {
  511.                 Warning(hwnd, "Error saving %s: %s", gbmfild.fild.szFullFile, ModErrorString(mrc));
  512.                 return FALSE;
  513.                 }
  514.  
  515.             strcpy(szFileName, gbmfild.fild.szFullFile);
  516.             fUnsavedChanges = FALSE;
  517.             return TRUE;
  518.             }
  519.         case MBID_NO:
  520.             return TRUE;
  521.         case MBID_CANCEL:
  522.             return FALSE;
  523.         }
  524.     /* NOT REACHED */
  525.     return FALSE; /* Keep fussy compiler happy */
  526.     }
  527. /*...e*/
  528. /*...sBitmapWndProc:0:*/
  529. #define    WC_BITMAP "GbmV2BitmapViewerClass"
  530.  
  531. static BOOL fTracking = FALSE;
  532.  
  533. /*
  534. This is a pretty simple window class. Its aim in life is to display the bitmap
  535. held by the global variable 'hbm'.
  536. */
  537.  
  538. static HBITMAP hbm = NULLHANDLE;
  539. static LONG lColorBg, lColorFg;
  540. static HMTX hmtxHbm;
  541.  
  542. static VOID RequestHbm(VOID)
  543.     {
  544.     DosRequestMutexSem(hmtxHbm, SEM_INDEFINITE_WAIT);
  545.     }
  546.  
  547. static VOID ReleaseHbm(VOID)
  548.     {
  549.     DosReleaseMutexSem(hmtxHbm);
  550.     }
  551.  
  552. MRESULT _System BitmapWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  553.     {
  554.     switch ( (int) msg )
  555.         {
  556. /*...sWM_PAINT       \45\ repaint client area:16:*/
  557. case WM_PAINT:
  558.     {
  559.     RECTL rclUpdate;
  560.     HPS hps = WinBeginPaint(hwnd, (HPS) NULL, &rclUpdate);
  561.  
  562.     RequestHbm();
  563.  
  564.     if ( hbm != (HBITMAP) NULL )
  565.         {
  566.         static SIZEL sizl = { 0, 0 };
  567.         HAB hab = WinQueryAnchorBlock(hwnd);
  568.         HDC hdcBmp = DevOpenDC(hab, OD_MEMORY, "*", 0L, (PDEVOPENDATA) NULL, (HDC) NULL);
  569.         HPS hpsBmp = GpiCreatePS(hab, hdcBmp, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC);
  570.         POINTL aptl[3];
  571.         BITMAPINFOHEADER bmp;
  572.  
  573.         GpiQueryBitmapParameters(hbm, &bmp);
  574.  
  575.         GpiSetBitmap(hpsBmp, hbm);
  576.  
  577.         GpiSetBackColor(hps, GpiQueryColorIndex(hps, 0, lColorBg));
  578.         GpiSetColor    (hps, GpiQueryColorIndex(hps, 0, lColorFg));
  579.  
  580.         aptl[0].x = 0;
  581.         aptl[0].y = 0;
  582.         aptl[1].x = bmp.cx;
  583.         aptl[1].y = bmp.cy;
  584.         aptl[2].x = 0;
  585.         aptl[2].y = 0;
  586.         GpiBitBlt(hps, hpsBmp, 3L, aptl, ROP_SRCCOPY, BBO_IGNORE);
  587.  
  588.         GpiSetBitmap(hpsBmp, (HBITMAP) NULL);
  589.         GpiDestroyPS(hpsBmp);
  590.         DevCloseDC(hdcBmp);
  591.  
  592.         if ( fSelectionDefined )
  593.             {
  594.             POINTL ptl;
  595.  
  596.             GpiSetMix(hps, FM_INVERT);
  597.             ptl.x = rclSelection.xLeft;
  598.             ptl.y = rclSelection.yBottom;
  599.             GpiMove(hps, &ptl);
  600.             ptl.x = rclSelection.xRight - 1L;
  601.             ptl.y = rclSelection.yTop   - 1L;
  602.             GpiBox(hps, DRO_OUTLINE, &ptl, 0L, 0L);
  603.             }
  604.         }
  605.  
  606.     ReleaseHbm();
  607.  
  608.     WinEndPaint(hps);
  609.     }
  610.     return (MRESULT) 0;
  611. /*...e*/
  612. /*...sWM_CHAR        \45\ got a character from the user:16:*/
  613. #define    KS(vkey,kc) ((vkey)+((kc&(KC_ALT|KC_SHIFT|KC_CTRL))<<16))
  614.  
  615. case WM_CHAR:
  616.     {
  617.     USHORT fs        = SHORT1FROMMP(mp1);
  618.     CHAR   ch        = (CHAR) SHORT1FROMMP(mp2);
  619.     USHORT vkey      = SHORT2FROMMP(mp2);
  620.     HWND hwndHscroll = WinWindowFromID(hwndScroller, SCID_HSCROLL);
  621.     HWND hwndVscroll = WinWindowFromID(hwndScroller, SCID_VSCROLL);
  622.  
  623.     if ( fTracking )
  624.         /* Swallow the key */
  625.         return (MRESULT) 0;
  626.  
  627.     if ( fs & KC_VIRTUALKEY )
  628.         {
  629.         switch ( KS(vkey,fs) )
  630.             {
  631.             case KS(VK_LEFT,0):
  632.             case KS(VK_RIGHT,0):
  633.                 return WinSendMsg(hwndHscroll, msg, mp1, mp2);
  634.             case KS(VK_LEFT,KC_SHIFT):
  635.                 fs &= ~KC_SHIFT;
  636.                 vkey = VK_PAGEUP;
  637.                 mp1 = MPFROMSHORT(fs);
  638.                 mp2 = MPFROM2SHORT(ch, vkey);
  639.                 return WinSendMsg(hwndHscroll, msg, mp1, mp2);
  640.             case KS(VK_RIGHT,KC_SHIFT):
  641.                 fs &= ~KC_SHIFT;
  642.                 vkey = VK_PAGEDOWN;
  643.                 mp1 = MPFROMSHORT(fs);
  644.                 mp2 = MPFROM2SHORT(ch, vkey);
  645.                 return WinSendMsg(hwndHscroll, msg, mp1, mp2);
  646.             case KS(VK_UP,0):
  647.             case KS(VK_DOWN,0):
  648.             case KS(VK_PAGEUP,0):
  649.             case KS(VK_PAGEDOWN,0):
  650.                 return WinSendMsg(hwndVscroll, msg, mp1, mp2);
  651.             case KS(VK_UP,KC_SHIFT):
  652.                 fs &= ~KC_SHIFT;
  653.                 vkey = VK_PAGEUP;
  654.                 mp1 = MPFROMSHORT(fs);
  655.                 mp2 = MPFROM2SHORT(ch, vkey);
  656.                 return WinSendMsg(hwndVscroll, msg, mp1, mp2);
  657.             case KS(VK_DOWN,KC_SHIFT):
  658.                 fs &= ~KC_SHIFT;
  659.                 vkey = VK_PAGEDOWN;
  660.                 mp1 = MPFROMSHORT(fs);
  661.                 mp2 = MPFROM2SHORT(ch, vkey);
  662.                 return WinSendMsg(hwndVscroll, msg, mp1, mp2);
  663.             }
  664.         }
  665.     }
  666.     break;
  667. /*...e*/
  668. /*...sWM_BUTTON2DOWN \45\ cancel current selection:16:*/
  669. case WM_BUTTON2DOWN:
  670.     fSelectionDefined = FALSE;
  671.     WinInvalidateRect(hwnd, NULL, TRUE);
  672.     WinUpdateWindow(hwnd);
  673.     break;
  674. /*...e*/
  675. /*...sWM_MOUSEMOVE   \45\ hourglass:16:*/
  676. case WM_MOUSEMOVE:
  677.     if ( fTracking )
  678.         return (MRESULT) 0;
  679.  
  680.     if ( fBusy )
  681.         {
  682.         WinSetPointer(HWND_DESKTOP,
  683.             WinQuerySysPointer(HWND_DESKTOP, SPTR_WAIT, FALSE));
  684.         return (MRESULT) 0;
  685.         }
  686.     break;
  687. /*...e*/
  688.         }
  689.     return WinDefWindowProc(hwnd, msg, mp1, mp2);
  690.     }
  691.  
  692. /*...sSetBitmap:0:*/
  693. /* Assured hbmNew != (HBITMAP) NULL on entry */
  694.  
  695. static VOID SetBitmap(HBITMAP hbmNew, LONG lColorBgNew, LONG lColorFgNew)
  696.     {
  697.     HBITMAP hbmOld = hbm;
  698.     BOOL fOld = ( hbmOld != (HBITMAP) NULL );
  699.     BITMAPINFOHEADER2 bmp;
  700.  
  701.     RequestHbm();
  702.  
  703.     if ( hbmNew == hbmOld )
  704.         {
  705.         ReleaseHbm();
  706.         return;
  707.         }
  708.  
  709.     hbm      = hbmNew;
  710.     lColorBg = lColorBgNew;
  711.     lColorFg = lColorFgNew;
  712.  
  713.     if ( fOld )
  714.         GpiDeleteBitmap(hbmOld);
  715.  
  716.     bmp.cbFix = sizeof(BITMAPINFOHEADER2);
  717.     GpiQueryBitmapInfoHeader(hbm, &bmp);
  718.     ReleaseHbm();
  719.     WinShowWindow(hwndBitmap, FALSE);        
  720.     WinSetWindowPos(hwndBitmap, (HWND) NULL, 0,0,bmp.cx,bmp.cy, SWP_SIZE);
  721.  
  722.     if ( fOld )
  723.         WinSendMsg(hwndScroller, SCM_SIZE, MPFROMHWND(hwndBitmap), NULL);
  724.     else
  725.         WinSendMsg(hwndScroller, SCM_CHILD, MPFROMHWND(hwndBitmap), NULL);
  726.  
  727.     WinShowWindow(hwndBitmap, TRUE);
  728.     WinInvalidateRect(hwndBitmap, NULL, TRUE);
  729.     WinUpdateWindow(hwndBitmap);
  730.     }
  731. /*...e*/
  732. /*...sSetNoBitmap:0:*/
  733. static VOID SetNoBitmap(VOID)
  734.     {
  735.     HBITMAP hbmOld = hbm;
  736.     BOOL fOld = ( hbmOld != (HBITMAP) NULL );
  737.  
  738.     RequestHbm();
  739.  
  740.     if ( hbmOld == (HBITMAP) NULL )
  741.         {
  742.         ReleaseHbm();
  743.         return;
  744.         }
  745.  
  746.     hbm = (HBITMAP) NULL;
  747.  
  748.     if ( fOld )
  749.         GpiDeleteBitmap(hbmOld);
  750.  
  751.     ReleaseHbm();
  752.     WinSetWindowPos(hwndBitmap, (HWND) NULL, 0,0,0,0, SWP_SIZE);
  753.  
  754.     if ( fOld )
  755.         WinSendMsg(hwndScroller, SCM_CHILD, MPFROMHWND((HWND) NULL), NULL);
  756.  
  757.     WinInvalidateRect(hwndScroller, NULL, TRUE);
  758.     WinUpdateWindow(hwndScroller);
  759.     }
  760. /*...e*/
  761. /*...e*/
  762. /*...sObjectWndProc:0:*/
  763. #define    WC_GBMV2_OBJECT    "GbmV2ObjectWindowClass"
  764.  
  765. /*...sUM_ user window messages:0:*/
  766. #define    UM_NEW             WM_USER
  767. #define    UM_OPEN            (WM_USER+ 1)
  768. #define    UM_SAVE            (WM_USER+ 2)
  769. #define    UM_SAVE_AS        (WM_USER+ 3)
  770. #define    UM_EXPORT_MET        (WM_USER+ 4)
  771. #define    UM_PRINT        (WM_USER+ 5)
  772. #define    UM_SNAPSHOT        (WM_USER+ 6)
  773. #define    UM_UNDO            (WM_USER+ 7)
  774. #define    UM_SELECT        (WM_USER+ 8)
  775. #define    UM_DESELECT        (WM_USER+ 9)
  776. #define    UM_COPY            (WM_USER+10)
  777. #define    UM_PASTE        (WM_USER+11)
  778. #define    UM_REF_HORZ        (WM_USER+12)
  779. #define    UM_REF_VERT        (WM_USER+13)
  780. #define    UM_ROT_90        (WM_USER+14)
  781. #define    UM_ROT_180        (WM_USER+15)
  782. #define    UM_ROT_270        (WM_USER+16)
  783. #define    UM_TRANSPOSE        (WM_USER+17)
  784. #define    UM_CROP            (WM_USER+18)
  785. #define    UM_COLOUR        (WM_USER+19)
  786. #define    UM_MAP            (WM_USER+20)
  787. #define    UM_RESIZE        (WM_USER+21)
  788. #define    UM_VIEW_NULL        (WM_USER+22)
  789. #define    UM_VIEW_HALFTONE    (WM_USER+23)
  790. #define    UM_VIEW_ERRDIFF        (WM_USER+24)
  791. #define    UM_ABOUT        (WM_USER+25)
  792.  
  793. #define    UM_DONE            (WM_USER+26)
  794. /*...e*/
  795.  
  796. /*...sTracking:0:*/
  797. static HWND hwndClientTracking;
  798. static TRACKINFO ti;
  799.  
  800. /*
  801. The documentation says that ti.rclTrack is updated to the current values before
  802. the hook functions are called. Under OS/2 2.1 GA (and perhaps other versions),
  803. this is not the case. So I will code this differently.
  804. */
  805.  
  806. /*...sTrackPosHook:0:*/
  807. static BOOL APIENTRY TrackPosHook(HAB hab, QMSG *pqmsg, ULONG msgf)
  808.     {
  809.     hab=hab; /* Suppress warning */
  810.     if ( msgf == MSGF_TRACK )
  811.         {
  812.         POINTL ptl = pqmsg->ptl;
  813.         WinMapWindowPoints(pqmsg->hwnd, hwndBitmap, &ptl, 1L);
  814.         if ( ptl.x < 0L )
  815.             ptl.x = 0L;
  816.         else if ( ptl.x > mod.gbm.w )
  817.             ptl.x = mod.gbm.w;
  818.         if ( ptl.y < 0L )
  819.             ptl.y = 0L;
  820.         else if ( ptl.y > mod.gbm.h )
  821.             ptl.y = mod.gbm.h;
  822.         Caption(hwndClientTracking, " - at %ldx%ld", ptl.x, ptl.y);
  823.         }
  824.     return FALSE; /* Pass on to next hook etc. / ie: don't swallow msg */
  825.     }
  826. /*...e*/
  827. /*...sTrackSizeHook:0:*/
  828. static BOOL APIENTRY TrackSizeHook(HAB hab, QMSG *pqmsg, ULONG msgf)
  829.     {
  830.     hab=hab; /* Suppress warning */
  831.     if ( msgf == MSGF_TRACK )
  832.         {
  833.         POINTL ptl = pqmsg->ptl;
  834.         SIZEL sizl;
  835.         WinMapWindowPoints(pqmsg->hwnd, hwndBitmap, &ptl, 1L);
  836.         sizl.cx = (ptl.x - ti.rclTrack.xLeft  );
  837.         sizl.cy = (ptl.y - ti.rclTrack.yBottom);
  838.         if ( sizl.cx < 0L )
  839.             sizl.cx = 0L;
  840.         else if ( sizl.cx > mod.gbm.w )
  841.             sizl.cx = mod.gbm.w;
  842.         if ( sizl.cy < 0L )
  843.             sizl.cy = 0L;
  844.         else if ( sizl.cy > mod.gbm.h )
  845.             sizl.cy = mod.gbm.h;
  846.         Caption(hwndClientTracking, " - at %ldx%ld, size %ldx%ld",
  847.             (long) ti.rclTrack.xLeft  ,
  848.             (long) ti.rclTrack.yBottom,
  849.             (long) sizl.cx,
  850.             (long) sizl.cy);
  851.         }
  852.     return FALSE; /* Pass on to next hook etc. / ie: don't swallow msg */
  853.     }
  854. /*...e*/
  855.  
  856. static BOOL Tracking(
  857.     HWND hwndClient,
  858.     HWND hwnd,
  859.     RECTL *prclTrack,
  860.     int x, int y, int cx, int cy,
  861.     HPOINTER hptr1, HPOINTER hptr2
  862.     )
  863.     {
  864.     HAB hab = WinQueryAnchorBlock(hwnd);
  865.     HMQ hmq = HMQ_CURRENT;
  866.     BOOL f;
  867.  
  868.     hwndClientTracking = hwndClient;
  869.  
  870.     ti.cxBorder   = 1;
  871.     ti.cyBorder   = 1;
  872.     ti.cxGrid     = 0;
  873.     ti.cyGrid     = 0;
  874.     ti.cxKeyboard = 4;
  875.     ti.cyKeyboard = 4;
  876.  
  877.     ti.rclBoundary.xLeft   = x;
  878.     ti.rclBoundary.xRight  = x + cx;
  879.     ti.rclBoundary.yBottom = y;
  880.     ti.rclBoundary.yTop    = y + cy;
  881.  
  882.     ti.ptlMinTrackSize.x = 1;
  883.     ti.ptlMinTrackSize.y = 1;
  884.  
  885.     ti.ptlMaxTrackSize.x = cx;
  886.     ti.ptlMaxTrackSize.y = cy;
  887.  
  888.     ti.rclTrack.xLeft   = x + cx/4;
  889.     ti.rclTrack.xRight  = x + cx/4;
  890.     ti.rclTrack.yBottom = y + cy/4;
  891.     ti.rclTrack.yTop    = y + cy/4;
  892.  
  893. /*...smove pointer:8:*/
  894. {
  895. POINTL ptl;
  896.  
  897. ptl.x = ti.rclTrack.xLeft;
  898. ptl.y = ti.rclTrack.yBottom;
  899. WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
  900. WinSetPointerPos(HWND_DESKTOP, ptl.x, ptl.y);
  901. }
  902. /*...e*/
  903.  
  904.     ti.fs = TF_MOVE | TF_STANDARD | TF_SETPOINTERPOS | TF_ALLINBOUNDARY;
  905.     WinSetPointer(HWND_DESKTOP, hptr1);
  906.     WinSetHook(hab, hmq, HK_MSGFILTER, (PFN) TrackPosHook, (HMODULE) NULL);
  907.     f = WinTrackRect(hwnd, (HPS) NULL, &ti);
  908.     WinReleaseHook(hab, hmq, HK_MSGFILTER, (PFN) TrackPosHook, (HMODULE) NULL);
  909.     Caption(hwndClient, " - %s", FileName());
  910.     if ( !f )
  911.         return FALSE;
  912.  
  913.     ti.fs = TF_RIGHT | TF_TOP | TF_STANDARD | TF_SETPOINTERPOS | TF_ALLINBOUNDARY;
  914.     WinSetPointer(HWND_DESKTOP, hptr2);
  915.     WinSetHook(hab, hmq, HK_MSGFILTER, (PFN) TrackSizeHook, (HMODULE) NULL);
  916.     f = WinTrackRect(hwnd, (HPS) NULL, &ti);
  917.     WinReleaseHook(hab, hmq, HK_MSGFILTER, (PFN) TrackSizeHook, (HMODULE) NULL);
  918.     Caption(hwndClient, " - %s", FileName());
  919.     if ( !f )
  920.         return FALSE;
  921.  
  922.     *prclTrack = ti.rclTrack;
  923.  
  924.     return TRUE;
  925.     }
  926. /*...e*/
  927. /*...sSelect:0:*/
  928. static BOOL Select(
  929.     HWND hwndClient,
  930.     HWND hwnd,
  931.     RECTL *prclTrack,
  932.     int x, int y, int cx, int cy
  933.     )
  934.     {
  935.     HPOINTER hptr1 = WinLoadPointer(HWND_DESKTOP, (HMODULE) NULL, RID_SELECT1);
  936.     HPOINTER hptr2 = WinLoadPointer(HWND_DESKTOP, (HMODULE) NULL, RID_SELECT2);
  937.     BOOL f;
  938.  
  939.     fTracking = TRUE;
  940.     f = Tracking(hwndClient, hwnd, prclTrack, x, y, cx, cy, hptr1, hptr2);
  941.     fTracking = FALSE;
  942.  
  943.     WinDestroyPointer(hptr1);
  944.     WinDestroyPointer(hptr2);
  945.  
  946.     return f;
  947.     }
  948. /*...e*/
  949. /*...sCopyToClipbrd:0:*/
  950. static VOID CopyToClipbrd(HWND hwndClient, RECTL rcl)
  951.     {
  952.     HAB hab = WinQueryAnchorBlock(hwndClient);
  953.     HPS hps = WinGetPS(hwndClient);
  954.     HBITMAP hbmClipbrd;
  955.     BITMAPINFOHEADER2 bmpClipbrd;
  956.     RECTL rclClipbrd;
  957.  
  958.     bmpClipbrd.cbFix = sizeof(BITMAPINFOHEADER2);
  959.     GpiQueryBitmapInfoHeader(hbm, &bmpClipbrd);
  960.     bmpClipbrd.cx = (ULONG) (rcl.xRight - rcl.xLeft);
  961.     bmpClipbrd.cy = (ULONG) (rcl.yTop - rcl.yBottom);
  962.     if ( (hbmClipbrd = GpiCreateBitmap(hps, &bmpClipbrd, 0L, NULL, NULL)) == (HBITMAP) NULL )
  963.         Warning(hwndClient, "Error copying to clipboard: can't create bitmap");
  964.     else
  965.         {
  966.         HMF hmf;
  967.  
  968.         rclClipbrd.xLeft   = 0; rclClipbrd.xRight = bmpClipbrd.cx;
  969.         rclClipbrd.yBottom = 0; rclClipbrd.yTop   = bmpClipbrd.cy;
  970.         BmpBlitter(hab, hbm, rcl, hbmClipbrd, rclClipbrd);
  971.         WinOpenClipbrd(hab);
  972.         WinEmptyClipbrd(hab);
  973.  
  974.         /* Try to also put CF_METAFILE data on clipboard too */
  975.         if ( ModMakeHMF(hbmClipbrd, hab, &hmf) == MOD_ERR_OK )
  976.             WinSetClipbrdData(hab, (ULONG) hmf, CF_METAFILE, CFI_HANDLE);
  977.  
  978.         WinSetClipbrdData(hab, (ULONG) hbmClipbrd, CF_BITMAP, CFI_HANDLE);
  979.         WinCloseClipbrd(hab);
  980.         }
  981.     WinReleasePS(hps);
  982.     }
  983. /*...e*/
  984. /*...sReflect:0:*/
  985. /*...sReflectAll:0:*/
  986. /* This routine is used to perform primitives affecting the whole bitmap */
  987.  
  988. static VOID ReflectAll(
  989.     HWND hwndClient,
  990.     MOD_ERR (*reflector)(const MOD *mod, MOD *modNew),
  991.     const CHAR *szWhat, const CHAR *szUndo
  992.     )
  993.     {
  994.     MOD_ERR mrc; MOD modNew;
  995.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  996.  
  997.     Caption(hwndClient, " - %s %s", szWhat, FileName());
  998.     mrc = (*reflector)(&mod, &modNew);
  999.     Caption(hwndClient, " - %s", FileName());
  1000.  
  1001.     if ( mrc != MOD_ERR_OK )
  1002.         {
  1003.         Warning(hwndClient, "Error %s %s: %s", szWhat, FileName(), ModErrorString(mrc));
  1004.         return;
  1005.         }
  1006.  
  1007.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  1008.         {
  1009.         ModDelete(&modNew);
  1010.         return;
  1011.         }
  1012.  
  1013.     KeepForUndo(&mod, szUndo);
  1014.  
  1015.     ModMove(&modNew, &mod);
  1016.     fUnsavedChanges = TRUE;
  1017.     fSelectionDefined = FALSE;
  1018.  
  1019.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  1020.     }
  1021. /*...e*/
  1022. /*...sReflectSelection:0:*/
  1023. /* This routine is called for primitives that just alter the selection */
  1024.  
  1025. static VOID ReflectSelection(
  1026.     HWND hwndClient,
  1027.     MOD_ERR (*reflector)(const MOD *mod, MOD *modNew),
  1028.     const CHAR *szWhat, const CHAR *szUndo
  1029.     )
  1030.     {
  1031.     MOD_ERR mrc; MOD modSelBefore, modSelAfter, modNew;
  1032.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  1033.  
  1034.     Caption(hwndClient, " - %s %s", szWhat, FileName());
  1035.  
  1036.     /* Step1: Extract the selection */
  1037.     mrc = ModExtractSubrectangle(&mod,
  1038.         rclSelection.xLeft,
  1039.         rclSelection.yBottom,
  1040.         rclSelection.xRight - rclSelection.xLeft,
  1041.         rclSelection.yTop - rclSelection.yBottom,
  1042.         &modSelBefore);
  1043.     if ( mrc != MOD_ERR_OK )
  1044.         {
  1045.         Caption(hwndClient, " - %s", FileName());
  1046.         Warning(hwndClient, "Error %s %s: %s", szWhat, FileName(), ModErrorString(mrc));
  1047.         return;
  1048.         }
  1049.  
  1050.     /* Step2: Try to perform the operation */
  1051.     mrc = (*reflector)(&modSelBefore, &modSelAfter);
  1052.     ModDelete(&modSelBefore);
  1053.     if ( mrc != MOD_ERR_OK )
  1054.         {
  1055.         Caption(hwndClient, " - %s", FileName());
  1056.         Warning(hwndClient, "Error %s %s: %s", szWhat, FileName(), ModErrorString(mrc));
  1057.         return;
  1058.         }
  1059.  
  1060.     /* Step3: Make a copy of the current bitmap to build composite result */
  1061.     mrc = ModCopy(&mod, &modNew);
  1062.     if ( mrc != MOD_ERR_OK )
  1063.         {
  1064.         ModDelete(&modSelAfter);
  1065.         Caption(hwndClient, " - %s", FileName());
  1066.         Warning(hwndClient, "Error %s %s: %s", szWhat, FileName(), ModErrorString(mrc));
  1067.         return;
  1068.         }
  1069.  
  1070.     /* Step4: Blit the new data over the old data */
  1071.     ModBlit(&modNew, rclSelection.xLeft, rclSelection.yBottom, /* Dst */
  1072.         &modSelAfter, 0, 0,                   /* Src */
  1073.             modSelAfter.gbm.w, modSelAfter.gbm.h);
  1074.  
  1075.     /* Step5: Discard after bitmap */
  1076.     ModDelete(&modSelAfter);
  1077.  
  1078.     /* From here onwards: Try to build new view bitmap */
  1079.  
  1080.     Caption(hwndClient, " - %s", FileName());
  1081.  
  1082.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  1083.         {
  1084.         ModDelete(&modNew);
  1085.         return;
  1086.         }
  1087.  
  1088.     KeepForUndo(&mod, szUndo);
  1089.  
  1090.     ModMove(&modNew, &mod);
  1091.     fUnsavedChanges = TRUE;
  1092.  
  1093.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  1094.     }
  1095. /*...e*/
  1096.  
  1097. static VOID Reflect(
  1098.     HWND hwndClient,
  1099.     MOD_ERR (*reflector)(const MOD *mod, MOD *modNew),
  1100.     const CHAR *szWhat, const CHAR *szUndo
  1101.     )
  1102.     {
  1103.     if ( fSelectionDefined )
  1104.         ReflectSelection(hwndClient, reflector, szWhat, szUndo);
  1105.     else
  1106.         ReflectAll(hwndClient, reflector, szWhat, szUndo);
  1107.     }
  1108. /*...e*/
  1109. /*...sNewView:0:*/
  1110. static VOID NewView(HWND hwndClient, BYTE bViewNew)
  1111.     {
  1112.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  1113.  
  1114.     if ( !MakeVisualBg(hwndClient, &mod, bViewNew, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  1115.         return;
  1116.  
  1117.     bView = bViewNew;
  1118.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  1119.     }
  1120. /*...e*/
  1121.  
  1122. /*...sAboutDlgProc:0:*/
  1123. MRESULT _System AboutDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1124.     {
  1125.     switch ( msg )
  1126.         {
  1127. /*...sWM_INITDLG:16:*/
  1128. case WM_INITDLG:
  1129.     {
  1130.     CHAR sz[80+1];
  1131.     sprintf(sz, "Version %4.2f, using GBM.DLL version %4.2f",
  1132.         (double) VERSION/100.0, (double) gbm_version()/100.0);
  1133.     WinSetDlgItemText(hwnd, DID_VERSION_TEXT, sz);
  1134.     TidySysMenu(hwnd);
  1135.     return (MRESULT) FALSE; /* We have not set the focus */
  1136.     }
  1137. /*...e*/
  1138. /*...sWM_COMMAND:16:*/
  1139. case WM_COMMAND:
  1140.     switch ( COMMANDMSG(&msg) -> cmd )
  1141.         {
  1142. /*...sDID_OK:32:*/
  1143. case DID_OK:
  1144.     WinDismissDlg(hwnd, TRUE);
  1145.     return (MRESULT) 0;
  1146. /*...e*/
  1147. /*...sDID_CANCEL:32:*/
  1148. case DID_CANCEL:
  1149.     WinDismissDlg(hwnd, FALSE);
  1150.     return (MRESULT) 0;
  1151. /*...e*/
  1152.         }
  1153.     break;
  1154. /*...e*/
  1155. /*...sWM_CLOSE:16:*/
  1156. case WM_CLOSE:
  1157.     WinDismissDlg(hwnd, FALSE);
  1158.     return (MRESULT) 0;
  1159. /*...e*/
  1160. /*...sWM_HELP:16:*/
  1161. case WM_HELP:
  1162.     /* Parent is HWND_DESKTOP */
  1163.     /* WinDefDlgProc() will pass this up to the parent */
  1164.     /* So redirect to the owner */
  1165.     return WinSendMsg(WinQueryWindow(hwnd, QW_OWNER), msg, mp1, mp2);
  1166. /*...e*/
  1167.         }
  1168.     return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1169.     }
  1170. /*...e*/
  1171. /*...sColourDlgProc:0:*/
  1172. static int map = CVT_I_TO_L;
  1173. static double gama = 2.1, shelf = 0.0;
  1174.  
  1175. MRESULT _System ColourDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1176.     {
  1177.     switch ( msg )
  1178.         {
  1179. /*...sWM_INITDLG:16:*/
  1180. case WM_INITDLG:
  1181.     {
  1182.     CHAR sz[50+1];
  1183.     SHORT id;
  1184.  
  1185.     switch ( map )
  1186.         {
  1187.         case CVT_I_TO_L:    id = DID_I_TO_L;    break;
  1188.         case CVT_I_TO_P:    id = DID_I_TO_P;    break;
  1189.         case CVT_L_TO_I:    id = DID_L_TO_I;    break;
  1190.         case CVT_L_TO_P:    id = DID_L_TO_P;    break;
  1191.         case CVT_P_TO_I:    id = DID_P_TO_I;    break;
  1192.         case CVT_P_TO_L:    id = DID_P_TO_L;    break;
  1193.         }
  1194.  
  1195.     WinSendDlgItemMsg(hwnd, id, BM_CLICK, MPFROMSHORT(TRUE), NULL);
  1196.     WinSetFocus(HWND_DESKTOP, WinWindowFromID(hwnd, id));
  1197.  
  1198.     sprintf(sz, "%1.1f", gama);
  1199.     WinSetDlgItemText(hwnd, DID_GAMMA, sz);
  1200.     RestrictEntryfield(WinWindowFromID(hwnd, DID_GAMMA), NULL, NULL, "0123456789.", NULL);
  1201.  
  1202.     sprintf(sz, "%1.1f", shelf);
  1203.     WinSetDlgItemText(hwnd, DID_SHELF, sz);
  1204.     RestrictEntryfield(WinWindowFromID(hwnd, DID_SHELF), NULL, NULL, "0123456789.", NULL);
  1205.  
  1206.     TidySysMenu(hwnd);
  1207.     return (MRESULT) TRUE; /* We have set the focus */
  1208.     }
  1209. /*...e*/
  1210. /*...sWM_COMMAND:16:*/
  1211. case WM_COMMAND:
  1212.     switch ( COMMANDMSG(&msg) -> cmd )
  1213.         {
  1214. /*...sDID_OK:32:*/
  1215. case DID_OK:
  1216.     {
  1217.     SHORT sInx = SHORT1FROMMR(WinSendDlgItemMsg(hwnd, DID_I_TO_L, BM_QUERYCHECKINDEX, NULL, NULL)) - 1;
  1218.     CHAR sz[50+1];
  1219.     int mapNew;
  1220.     double gamaNew, shelfNew;
  1221.  
  1222.     switch ( sInx )
  1223.         {
  1224.         case DID_I_TO_L - DID_I_TO_L:    mapNew = CVT_I_TO_L;    break;
  1225.         case DID_I_TO_P - DID_I_TO_L:    mapNew = CVT_I_TO_P;    break;
  1226.         case DID_L_TO_I - DID_I_TO_L:    mapNew = CVT_L_TO_I;    break;
  1227.         case DID_L_TO_P - DID_I_TO_L:    mapNew = CVT_L_TO_P;    break;
  1228.         case DID_P_TO_I - DID_I_TO_L:    mapNew = CVT_P_TO_I;    break;
  1229.         case DID_P_TO_L - DID_I_TO_L:    mapNew = CVT_P_TO_L;    break;
  1230.         }
  1231.     WinQueryDlgItemText(hwnd, DID_GAMMA, sizeof(sz), sz);
  1232.     sscanf(sz, "%lf", &gamaNew);
  1233.     WinQueryDlgItemText(hwnd, DID_SHELF, sizeof(sz), sz);
  1234.     sscanf(sz, "%lf", &shelfNew);
  1235.  
  1236.     if ( gamaNew < 0.1 || gamaNew > 10.0 )
  1237.         Warning(hwnd, "Gamma must be between 0.1 and 10.0");
  1238.     else if ( shelfNew < 0.0 || shelfNew > 1.0 )
  1239.         Warning(hwnd, "Shelf must be between 0.0 and 1.0");
  1240.     else
  1241.         {
  1242.         map   = mapNew;
  1243.         gama  = gamaNew;
  1244.         shelf = shelfNew;
  1245.         WinDismissDlg(hwnd, TRUE);
  1246.         return (MRESULT) 0;
  1247.         }
  1248.     }
  1249.     break;
  1250. /*...e*/
  1251. /*...sDID_CANCEL:32:*/
  1252. case DID_CANCEL:
  1253.     WinDismissDlg(hwnd, FALSE);
  1254.     return (MRESULT) 0;
  1255. /*...e*/
  1256.         }
  1257.     break;
  1258. /*...e*/
  1259. /*...sWM_CONTROL:16:*/
  1260. case WM_CONTROL:
  1261.     {
  1262.     SHORT id = SHORT1FROMMP(mp1);
  1263.     SHORT note = SHORT2FROMMP(mp1);
  1264.     HWND hwndG = WinWindowFromID(hwnd, DID_GAMMA_TEXT);
  1265.     HWND hwndS = WinWindowFromID(hwnd, DID_SHELF_TEXT);
  1266.  
  1267.     switch ( id )
  1268.         {
  1269.         case DID_I_TO_L:
  1270.         case DID_L_TO_I:
  1271.             if ( note == BN_CLICKED )
  1272.                 {
  1273.                 WinEnableWindow(hwndG, FALSE);
  1274.                 WinEnableWindow(hwndS, FALSE);
  1275.                 }
  1276.             break;
  1277.         case DID_I_TO_P:
  1278.         case DID_P_TO_I:
  1279.         case DID_P_TO_L:
  1280.         case DID_L_TO_P:
  1281.             if ( note == BN_CLICKED )
  1282.                 {
  1283.                 WinEnableWindow(hwndG, TRUE);
  1284.                 WinEnableWindow(hwndS, TRUE);
  1285.                 }
  1286.             break;
  1287.         }
  1288.     }
  1289.     break;
  1290. /*...e*/
  1291. /*...sWM_CLOSE:16:*/
  1292. case WM_CLOSE:
  1293.     WinDismissDlg(hwnd, FALSE);
  1294.     return (MRESULT) 0;
  1295. /*...e*/
  1296. /*...sWM_HELP:16:*/
  1297. case WM_HELP:
  1298.     /* Parent is HWND_DESKTOP */
  1299.     /* WinDefDlgProc() will pass this up to the parent */
  1300.     /* So redirect to the owner */
  1301.     return WinSendMsg(WinQueryWindow(hwnd, QW_OWNER), msg, mp1, mp2);
  1302. /*...e*/
  1303.         }
  1304.     return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1305.     }
  1306. /*...e*/
  1307. /*...sMapDlgProc:0:*/
  1308. static int iKeepRed = 8, iKeepGreen = 8, iKeepBlue = 8, nCols = 256;
  1309. static int iPal = CVT_784, iAlg = CVT_ERRDIFF;
  1310.  
  1311. MRESULT _System MapDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1312.     {
  1313.     switch ( msg )
  1314.         {
  1315. /*...sWM_INITDLG:16:*/
  1316. case WM_INITDLG:
  1317.     {
  1318.     CHAR sz[50+1];
  1319.     SHORT id;
  1320.  
  1321.     switch ( iPal )
  1322.         {
  1323.         case CVT_BW:        id = DID_MAP_BW;    break;
  1324.         case CVT_VGA:        id = DID_MAP_VGA;    break;
  1325.         case CVT_8:        id = DID_MAP_8;        break;
  1326.         case CVT_4G:        id = DID_MAP_4G;    break;
  1327.         case CVT_784:        id = DID_MAP_784;    break;
  1328.         case CVT_666:        id = DID_MAP_666;    break;
  1329.         case CVT_8G:        id = DID_MAP_8G;    break;
  1330.         case CVT_TRIPEL:    id = DID_MAP_TRIPEL;    break;
  1331.         case CVT_RGB:        id = DID_MAP_RGB;    break;
  1332.         case CVT_FREQ:        id = DID_MAP_FREQ;    break;
  1333.         case CVT_MCUT:        id = DID_MAP_MCUT;    break;
  1334.         }
  1335.  
  1336.     WinSendDlgItemMsg(hwnd, id, BM_CLICK, MPFROMSHORT(TRUE), NULL);
  1337.     WinSetFocus(HWND_DESKTOP, WinWindowFromID(hwnd, id));
  1338.  
  1339.     switch ( iAlg )
  1340.         {
  1341.         case CVT_NEAREST:    id = DID_NEAREST;    break;
  1342.         case CVT_HALFTONE:    id = DID_HALFTONE;    break;
  1343.         case CVT_ERRDIFF:    id = DID_ERRDIFF;    break;
  1344.         }
  1345.  
  1346.     WinSendDlgItemMsg(hwnd, id, BM_CLICK, MPFROMSHORT(TRUE), NULL);
  1347.  
  1348.     sprintf(sz, "%d", iKeepRed);
  1349.     WinSetDlgItemText(hwnd, DID_R, sz);
  1350.     RestrictEntryfield(WinWindowFromID(hwnd, DID_R), NULL, NULL, "0123456789", NULL);
  1351.  
  1352.     sprintf(sz, "%d", iKeepGreen);
  1353.     WinSetDlgItemText(hwnd, DID_G, sz);
  1354.     RestrictEntryfield(WinWindowFromID(hwnd, DID_G), NULL, NULL, "0123456789", NULL);
  1355.  
  1356.     sprintf(sz, "%d", iKeepBlue);
  1357.     WinSetDlgItemText(hwnd, DID_B, sz);
  1358.     RestrictEntryfield(WinWindowFromID(hwnd, DID_B), NULL, NULL, "0123456789", NULL);
  1359.  
  1360.     sprintf(sz, "%d", nCols);
  1361.     WinSetDlgItemText(hwnd, DID_N, sz);
  1362.     RestrictEntryfield(WinWindowFromID(hwnd, DID_N), NULL, NULL, "0123456789", NULL);
  1363.  
  1364.     TidySysMenu(hwnd);
  1365.     return (MRESULT) TRUE; /* We have set the focus */
  1366.     }
  1367. /*...e*/
  1368. /*...sWM_CONTROL:16:*/
  1369. case WM_CONTROL:
  1370.     {
  1371.     SHORT id = SHORT1FROMMP(mp1);
  1372.     SHORT note = SHORT2FROMMP(mp1);
  1373.     HWND hwndR = WinWindowFromID(hwnd, DID_R_TEXT);
  1374.     HWND hwndG = WinWindowFromID(hwnd, DID_G_TEXT);
  1375.     HWND hwndB = WinWindowFromID(hwnd, DID_B_TEXT);
  1376.     HWND hwndN = WinWindowFromID(hwnd, DID_N_TEXT);
  1377.     HWND hwndH = WinWindowFromID(hwnd, DID_HALFTONE);
  1378.     HWND hwndE = WinWindowFromID(hwnd, DID_ERRDIFF);
  1379.  
  1380.     switch ( id )
  1381.         {
  1382.         case DID_MAP_BW:
  1383.         case DID_MAP_8:
  1384.         case DID_MAP_VGA:
  1385.         case DID_MAP_4G:
  1386.         case DID_MAP_784:
  1387.         case DID_MAP_666:
  1388.         case DID_MAP_8G:
  1389.         case DID_MAP_TRIPEL:
  1390.             if ( note == BN_CLICKED )
  1391.                 {
  1392.                 WinEnableWindow(hwndR, FALSE);
  1393.                 WinEnableWindow(hwndG, FALSE);
  1394.                 WinEnableWindow(hwndB, FALSE);
  1395.                 WinEnableWindow(hwndN, FALSE);
  1396.                 }
  1397.             break;
  1398.         case DID_MAP_RGB:
  1399.             if ( note == BN_CLICKED )
  1400.                 {
  1401.                 WinEnableWindow(hwndR, TRUE);
  1402.                 WinEnableWindow(hwndG, TRUE);
  1403.                 WinEnableWindow(hwndB, TRUE);
  1404.                 WinEnableWindow(hwndN, FALSE);
  1405.                 }
  1406.             break;
  1407.         case DID_MAP_FREQ:
  1408.             if ( note == BN_CLICKED )
  1409.                 {
  1410.                 WinEnableWindow(hwndR, TRUE);
  1411.                 WinEnableWindow(hwndG, TRUE);
  1412.                 WinEnableWindow(hwndB, TRUE);
  1413.                 WinEnableWindow(hwndN, TRUE);
  1414.                 }
  1415.             break;
  1416.         case DID_MAP_MCUT:
  1417.             if ( note == BN_CLICKED )
  1418.                 {
  1419.                 WinEnableWindow(hwndR, FALSE);
  1420.                 WinEnableWindow(hwndG, FALSE);
  1421.                 WinEnableWindow(hwndB, FALSE);
  1422.                 WinEnableWindow(hwndN, TRUE);
  1423.                 }
  1424.             break;
  1425.         }
  1426.  
  1427.     switch ( id )
  1428.         {
  1429.         case DID_MAP_8G:
  1430.         case DID_MAP_TRIPEL:
  1431.         case DID_MAP_FREQ:
  1432.         case DID_MAP_MCUT:
  1433.             if ( note == BN_CLICKED )
  1434.                 {
  1435.                 WinSendDlgItemMsg(hwnd, DID_NEAREST, BM_CLICK, MPFROMSHORT(TRUE), NULL);
  1436.                 WinEnableWindow(hwndH, FALSE);
  1437.                 WinEnableWindow(hwndE, FALSE);
  1438.                 }
  1439.             break;
  1440.         case DID_MAP_BW:
  1441.         case DID_MAP_4G:
  1442.             if ( note == BN_CLICKED )
  1443.                 {
  1444.                 WinSendDlgItemMsg(hwnd, DID_NEAREST, BM_CLICK, MPFROMSHORT(TRUE), NULL);
  1445.                 WinEnableWindow(hwndH, FALSE);
  1446.                 WinEnableWindow(hwndE, TRUE);
  1447.                 }
  1448.             break;
  1449.         case DID_MAP_8:
  1450.         case DID_MAP_VGA:
  1451.         case DID_MAP_784:
  1452.         case DID_MAP_666:
  1453.         case DID_MAP_RGB:
  1454.             if ( note == BN_CLICKED )
  1455.                 {
  1456.                 WinEnableWindow(hwndH, TRUE);
  1457.                 WinEnableWindow(hwndE, TRUE);
  1458.                 }
  1459.             break;
  1460.         }
  1461.     }
  1462.     break;
  1463. /*...e*/
  1464. /*...sWM_COMMAND:16:*/
  1465. case WM_COMMAND:
  1466.     switch ( COMMANDMSG(&msg) -> cmd )
  1467.         {
  1468. /*...sDID_OK:32:*/
  1469. case DID_OK:
  1470.     {
  1471.     SHORT sInx;
  1472.     CHAR sz[50+1];
  1473.     int iPalNew, iAlgNew;
  1474.     int iKeepRedNew, iKeepGreenNew, iKeepBlueNew, nColsNew;
  1475.  
  1476.     sInx = SHORT1FROMMR(WinSendDlgItemMsg(hwnd, DID_MAP_BW, BM_QUERYCHECKINDEX, NULL, NULL)) - 1;
  1477.     switch ( sInx )
  1478.         {
  1479.         case DID_MAP_BW     - DID_MAP_BW:    iPalNew = CVT_BW;    break;
  1480.         case DID_MAP_VGA    - DID_MAP_BW:    iPalNew = CVT_VGA;    break;
  1481.         case DID_MAP_8      - DID_MAP_BW:    iPalNew = CVT_8;    break;
  1482.         case DID_MAP_4G     - DID_MAP_BW:    iPalNew = CVT_4G;    break;
  1483.         case DID_MAP_784    - DID_MAP_BW:    iPalNew = CVT_784;    break;
  1484.         case DID_MAP_666    - DID_MAP_BW:    iPalNew = CVT_666;    break;
  1485.         case DID_MAP_8G     - DID_MAP_BW:    iPalNew = CVT_8G;    break;
  1486.         case DID_MAP_TRIPEL - DID_MAP_BW:    iPalNew = CVT_TRIPEL;    break;
  1487.         case DID_MAP_RGB    - DID_MAP_BW:    iPalNew = CVT_RGB;    break;
  1488.         case DID_MAP_FREQ   - DID_MAP_BW:    iPalNew = CVT_FREQ;    break;
  1489.         case DID_MAP_MCUT   - DID_MAP_BW:    iPalNew = CVT_MCUT;    break;
  1490.         }
  1491.  
  1492.     WinEnableWindow(WinWindowFromID(hwnd, DID_NEAREST ), TRUE);
  1493.     WinEnableWindow(WinWindowFromID(hwnd, DID_HALFTONE), TRUE);
  1494.     WinEnableWindow(WinWindowFromID(hwnd, DID_ERRDIFF ), TRUE);
  1495.  
  1496.     sInx = SHORT1FROMMR(WinSendDlgItemMsg(hwnd, DID_NEAREST, BM_QUERYCHECKINDEX, NULL, NULL)) - 1;
  1497.     switch ( sInx )
  1498.         {
  1499.         case DID_NEAREST  - DID_NEAREST:    iAlgNew = CVT_NEAREST;    break;
  1500.         case DID_HALFTONE - DID_NEAREST:    iAlgNew = CVT_HALFTONE;    break;
  1501.         case DID_ERRDIFF  - DID_NEAREST:    iAlgNew = CVT_ERRDIFF;    break;
  1502.         }
  1503.  
  1504.     WinQueryDlgItemText(hwnd, DID_R, sizeof(sz), sz);
  1505.     sscanf(sz, "%d", &iKeepRedNew);
  1506.     WinQueryDlgItemText(hwnd, DID_G, sizeof(sz), sz);
  1507.     sscanf(sz, "%d", &iKeepGreenNew);
  1508.     WinQueryDlgItemText(hwnd, DID_B, sizeof(sz), sz);
  1509.     sscanf(sz, "%d", &iKeepBlueNew);
  1510.     WinQueryDlgItemText(hwnd, DID_N, sizeof(sz), sz);
  1511.     sscanf(sz, "%d", &nColsNew);
  1512.  
  1513.     if ( iKeepRedNew   < 0 || iKeepRedNew   > 8 )
  1514.         Warning(hwnd, "No. bits R must be between 0 and 8");
  1515.     else if ( iKeepGreenNew < 0 || iKeepGreenNew > 8 )
  1516.         Warning(hwnd, "No. bits G must be between 0 and 8");
  1517.     else if ( iKeepBlueNew  < 0 || iKeepBlueNew  > 8 )
  1518.         Warning(hwnd, "No. bits B must be between 0 and 8");
  1519.     else if ( nColsNew      < 1 || nColsNew      > 256 )
  1520.         Warning(hwnd, "No. bits B must be between 1 and 256");
  1521.     else
  1522.         {
  1523.         iPal       = iPalNew;
  1524.         iAlg       = iAlgNew;
  1525.         iKeepRed   = iKeepRedNew;
  1526.         iKeepGreen = iKeepGreenNew;
  1527.         iKeepBlue  = iKeepBlueNew;
  1528.         nCols      = nColsNew;
  1529.         WinDismissDlg(hwnd, TRUE);
  1530.         return (MRESULT) 0;
  1531.         }
  1532.     }
  1533.     return (MRESULT) 0;
  1534. /*...e*/
  1535. /*...sDID_CANCEL:32:*/
  1536. case DID_CANCEL:
  1537.     WinDismissDlg(hwnd, FALSE);
  1538.     return (MRESULT) 0;
  1539. /*...e*/
  1540.         }
  1541.     break;
  1542. /*...e*/
  1543. /*...sWM_CLOSE:16:*/
  1544. case WM_CLOSE:
  1545.     WinDismissDlg(hwnd, FALSE);
  1546.     return (MRESULT) 0;
  1547. /*...e*/
  1548. /*...sWM_HELP:16:*/
  1549. case WM_HELP:
  1550.     /* Parent is HWND_DESKTOP */
  1551.     /* WinDefDlgProc() will pass this up to the parent */
  1552.     /* So redirect to the owner */
  1553.     return WinSendMsg(WinQueryWindow(hwnd, QW_OWNER), msg, mp1, mp2);
  1554. /*...e*/
  1555.         }
  1556.     return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1557.     }
  1558. /*...e*/
  1559. /*...sResizeDlgProc:0:*/
  1560. static int cxNew = -1, cyNew = -1;
  1561.  
  1562. MRESULT _System ResizeDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1563.     {
  1564.     switch ( msg )
  1565.         {
  1566. /*...sWM_INITDLG:16:*/
  1567. case WM_INITDLG:
  1568.     {
  1569.     CHAR sz[100+1];
  1570.  
  1571.     sprintf(sz, "Bitmap is currently %dx%d at %dbpp",
  1572.         mod.gbm.w, mod.gbm.h, mod.gbm.bpp);
  1573.     WinSetDlgItemText(hwnd, DID_CURRENTLY, sz);
  1574.  
  1575.     WinSetFocus(HWND_DESKTOP, WinWindowFromID(hwnd, DID_WIDTH));
  1576.  
  1577.     if ( cxNew == -1 ) cxNew = mod.gbm.w;
  1578.     sprintf(sz, "%d", cxNew);
  1579.     WinSetDlgItemText(hwnd, DID_WIDTH, sz);
  1580.     RestrictEntryfield(WinWindowFromID(hwnd, DID_WIDTH ), NULL, NULL, "0123456789", NULL);
  1581.  
  1582.     if ( cyNew == -1 ) cyNew = mod.gbm.h;
  1583.     sprintf(sz, "%d", cyNew);
  1584.     WinSetDlgItemText(hwnd, DID_HEIGHT, sz);
  1585.     RestrictEntryfield(WinWindowFromID(hwnd, DID_HEIGHT), NULL, NULL, "0123456789", NULL);
  1586.  
  1587.     WinSetDlgItemText(hwnd, DID_MUL, "2");
  1588.     RestrictEntryfield(WinWindowFromID(hwnd, DID_MUL), NULL, NULL, "0123456789.", NULL);
  1589.  
  1590.     TidySysMenu(hwnd);
  1591.     return (MRESULT) TRUE; /* We have set the focus */
  1592.     }
  1593. /*...e*/
  1594. /*...sWM_COMMAND:16:*/
  1595. case WM_COMMAND:
  1596.     switch ( COMMANDMSG(&msg) -> cmd )
  1597.         {
  1598. /*...sDID_WIDTH_MUL:32:*/
  1599. case DID_WIDTH_MUL:
  1600.     {
  1601.     CHAR sz[40+1]; double w, n;
  1602.     WinQueryDlgItemText(hwnd, DID_WIDTH, sizeof(sz), sz);
  1603.     sscanf(sz, "%lf", &w);
  1604.     WinQueryDlgItemText(hwnd, DID_MUL, sizeof(sz), sz);
  1605.     sscanf(sz, "%lf", &n);
  1606.     w *= n;
  1607.     if ( w > 10000.0 )
  1608.         {
  1609.         Warning(hwnd, "Width of %6.2lf is too big", w);
  1610.         return (MRESULT) 0;
  1611.         }
  1612.     sprintf(sz, "%d", (int) w);
  1613.     WinSetDlgItemText(hwnd, DID_WIDTH, sz);
  1614.     return (MRESULT) 0;
  1615.     }
  1616. /*...e*/
  1617. /*...sDID_WIDTH_DIV:32:*/
  1618. case DID_WIDTH_DIV:
  1619.     {
  1620.     CHAR sz[40+1]; double w, n;
  1621.     WinQueryDlgItemText(hwnd, DID_WIDTH, sizeof(sz), sz);
  1622.     sscanf(sz, "%lf", &w);
  1623.     WinQueryDlgItemText(hwnd, DID_MUL, sizeof(sz), sz);
  1624.     sscanf(sz, "%lf", &n);
  1625.     if ( n < 1e-10 )
  1626.         {
  1627.         Warning(hwnd, "N is too small");
  1628.         return (MRESULT) 0;
  1629.         }
  1630.     w /= n;
  1631.     if ( w > 10000.0 )
  1632.         {
  1633.         Warning(hwnd, "Width of %6.2lf is too big", w);
  1634.         return (MRESULT) 0;
  1635.         }
  1636.     sprintf(sz, "%d", (int) w);
  1637.     WinSetDlgItemText(hwnd, DID_WIDTH, sz);
  1638.     return (MRESULT) 0;
  1639.     }
  1640. /*...e*/
  1641. /*...sDID_WIDTH_MATCH:32:*/
  1642. case DID_WIDTH_MATCH:
  1643.     {
  1644.     CHAR sz[40+1]; double w, h;
  1645.     WinQueryDlgItemText(hwnd, DID_HEIGHT, sizeof(sz), sz);
  1646.     sscanf(sz, "%lf", &h);
  1647.     w = ( h/mod.gbm.h ) * mod.gbm.w;
  1648.     if ( w > 10000.0 )
  1649.         {
  1650.         Warning(hwnd, "Width of %6.2lf is too big", w);
  1651.         return (MRESULT) 0;
  1652.         }
  1653.     sprintf(sz, "%d", (int) w);
  1654.     WinSetDlgItemText(hwnd, DID_WIDTH, sz);
  1655.     return (MRESULT) 0;
  1656.     }
  1657. /*...e*/
  1658. /*...sDID_HEIGHT_MUL:32:*/
  1659. case DID_HEIGHT_MUL:
  1660.     {
  1661.     CHAR sz[40+1]; double h, n;
  1662.     WinQueryDlgItemText(hwnd, DID_HEIGHT, sizeof(sz), sz);
  1663.     sscanf(sz, "%lf", &h);
  1664.     WinQueryDlgItemText(hwnd, DID_MUL, sizeof(sz), sz);
  1665.     sscanf(sz, "%lf", &n);
  1666.     h *= n;
  1667.     if ( h > 10000.0 )
  1668.         {
  1669.         Warning(hwnd, "Height of %6.2lf is too big", h);
  1670.         return (MRESULT) 0;
  1671.         }
  1672.     sprintf(sz, "%d", (int) h);
  1673.     WinSetDlgItemText(hwnd, DID_HEIGHT, sz);
  1674.     return (MRESULT) 0;
  1675.     }
  1676. /*...e*/
  1677. /*...sDID_HEIGHT_DIV:32:*/
  1678. case DID_HEIGHT_DIV:
  1679.     {
  1680.     CHAR sz[40+1]; double h, n;
  1681.     WinQueryDlgItemText(hwnd, DID_HEIGHT, sizeof(sz), sz);
  1682.     sscanf(sz, "%lf", &h);
  1683.     WinQueryDlgItemText(hwnd, DID_MUL, sizeof(sz), sz);
  1684.     sscanf(sz, "%lf", &n);
  1685.     if ( n < 1e-10 )
  1686.         {
  1687.         Warning(hwnd, "N is too small");
  1688.         return (MRESULT) 0;
  1689.         }
  1690.     h /= n;
  1691.     if ( h > 10000.0 )
  1692.         {
  1693.         Warning(hwnd, "Height of %6.2lf is too big", h);
  1694.         return (MRESULT) 0;
  1695.         }
  1696.     sprintf(sz, "%d", (int) h);
  1697.     WinSetDlgItemText(hwnd, DID_HEIGHT, sz);
  1698.     return (MRESULT) 0;
  1699.     }
  1700. /*...e*/
  1701. /*...sDID_HEIGHT_MATCH:32:*/
  1702. case DID_HEIGHT_MATCH:
  1703.     {
  1704.     CHAR sz[40+1]; double w, h;
  1705.     WinQueryDlgItemText(hwnd, DID_WIDTH, sizeof(sz), sz);
  1706.     sscanf(sz, "%lf", &w);
  1707.     h = ( w/mod.gbm.w ) * mod.gbm.h;
  1708.     if ( h > 10000.0 )
  1709.         {
  1710.         Warning(hwnd, "Height of %6.2lf is too big", h);
  1711.         return (MRESULT) 0;
  1712.         }
  1713.     sprintf(sz, "%d", (int) h);
  1714.     WinSetDlgItemText(hwnd, DID_HEIGHT, sz);
  1715.     return (MRESULT) 0;
  1716.     }
  1717. /*...e*/
  1718. /*...sDID_OK:32:*/
  1719. case DID_OK:
  1720.     {
  1721.     CHAR sz[50+1];
  1722.  
  1723.     WinQueryDlgItemText(hwnd, DID_WIDTH, sizeof(sz), sz);
  1724.     sscanf(sz, "%d", &cxNew);
  1725.     WinQueryDlgItemText(hwnd, DID_HEIGHT, sizeof(sz), sz);
  1726.     sscanf(sz, "%d", &cyNew);
  1727.  
  1728.     if ( cxNew < 1 || cyNew < 1 )
  1729.         Warning(hwnd, "Desired size is too small");
  1730.     else if ( cxNew > 10000 || cyNew > 10000 )
  1731.         Warning(hwnd, "Desired size is too big");
  1732.     else
  1733.         WinDismissDlg(hwnd, TRUE);
  1734.     return (MRESULT) 0;
  1735.     }
  1736. /*...e*/
  1737. /*...sDID_CANCEL:32:*/
  1738. case DID_CANCEL:
  1739.     WinDismissDlg(hwnd, FALSE);
  1740.     return (MRESULT) 0;
  1741. /*...e*/
  1742.         }
  1743.     break;
  1744. /*...e*/
  1745. /*...sWM_CLOSE:16:*/
  1746. case WM_CLOSE:
  1747.     WinDismissDlg(hwnd, FALSE);
  1748.     return (MRESULT) 0;
  1749. /*...e*/
  1750. /*...sWM_HELP:16:*/
  1751. case WM_HELP:
  1752.     /* Parent is HWND_DESKTOP */
  1753.     /* WinDefDlgProc() will pass this up to the parent */
  1754.     /* So redirect to the owner */
  1755.     return WinSendMsg(WinQueryWindow(hwnd, QW_OWNER), msg, mp1, mp2);
  1756. /*...e*/
  1757.         }
  1758.     return WinDefDlgProc(hwnd, msg, mp1, mp2);
  1759.     }
  1760. /*...e*/
  1761.  
  1762. /*...sRemoveExtension:0:*/
  1763. static VOID RemoveExtension(CHAR *sz)
  1764.     {
  1765.     CHAR *szDot;
  1766.  
  1767.     if ( (szDot = strrchr(sz, '.')) == NULL )
  1768.         return;
  1769.  
  1770.     if ( strchr(szDot+1, '\\') != NULL )
  1771.         return;
  1772.  
  1773.     *szDot = '\0';
  1774.     }
  1775. /*...e*/
  1776.  
  1777. MRESULT _System ObjectWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  1778.     {
  1779.     switch ( (int) msg )
  1780.         {
  1781. /*...sUM_NEW           \45\ clear out bitmap:16:*/
  1782. case UM_NEW:
  1783.     {
  1784.     HWND hwndClient = HWNDFROMMP(mp1);
  1785.  
  1786.     DiscardUndo();
  1787.  
  1788.     if ( fGotBitmap )
  1789.         if ( SaveChanges(hwndClient) )
  1790.             {
  1791.             SetNoBitmap();
  1792.  
  1793.             ModDelete(&mod);
  1794.             fGotBitmap        = FALSE;
  1795.             fUnsavedChanges   = FALSE;
  1796.             fSelectionDefined = FALSE;
  1797.             strcpy(szFileName, "");
  1798.  
  1799.             Caption(hwndClient, "");
  1800.             }
  1801.  
  1802.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1803.     return (MRESULT) 0;
  1804.     }
  1805. /*...e*/
  1806. /*...sUM_OPEN          \45\ read in a new bitmap:16:*/
  1807. case UM_OPEN:
  1808.     {
  1809.     HWND hwndClient = HWNDFROMMP(mp1);
  1810.     GBMFILEDLG gbmfild;
  1811.     MOD_ERR mrc; MOD modNew;
  1812.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  1813.  
  1814.     if ( !SaveChanges(hwndClient) )
  1815.         {
  1816.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1817.         return (MRESULT) 0;
  1818.         }
  1819.  
  1820.     memset(&(gbmfild.fild), 0, sizeof(FILEDLG));
  1821.     gbmfild.fild.cbSize = sizeof(FILEDLG);
  1822.     gbmfild.fild.fl = (FDS_CENTER|FDS_OPEN_DIALOG|FDS_HELPBUTTON);
  1823.     strcpy(gbmfild.fild.szFullFile, szFileName);
  1824.     strcpy(gbmfild.szOptions, "");    
  1825.     GbmFileDlg(HWND_DESKTOP, hwndClient, &gbmfild);
  1826.     if ( gbmfild.fild.lReturn != DID_OK )
  1827.         {
  1828.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1829.         return (MRESULT) 0;
  1830.         }
  1831.  
  1832.     SetNoBitmap();
  1833.  
  1834.     LowPri();
  1835.     Caption(hwndClient, " - reading %s", gbmfild.fild.szFullFile);
  1836.     mrc = ModCreateFromFile(gbmfild.fild.szFullFile, gbmfild.szOptions, &modNew);
  1837.     Caption(hwndClient, " - %s", FileName());
  1838.     RegPri();
  1839.     if ( mrc != MOD_ERR_OK )
  1840.         {
  1841.         Warning(hwndClient, "Error loading %s: %s", gbmfild.fild.szFullFile, ModErrorString(mrc));
  1842.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1843.         return (MRESULT) 0;
  1844.         }
  1845.         
  1846.     if ( !MakeVisualBg(hwndClient, &modNew, 
  1847.          bView, &hbmNew, &lColorBgNew, &lColorFgNew,
  1848.          gbmfild.fild.szFullFile) )
  1849.         {
  1850.         ModDelete(&modNew);
  1851.         Caption(hwndClient, "");
  1852.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1853.         return (MRESULT) 0;
  1854.         }
  1855.  
  1856.     DiscardUndo();
  1857.     if ( fGotBitmap )
  1858.         ModDelete(&mod);
  1859.  
  1860.     ModMove(&modNew, &mod);
  1861.     fGotBitmap = TRUE;
  1862.     fUnsavedChanges = FALSE;
  1863.     fSelectionDefined = FALSE;
  1864.     strcpy(szFileName, gbmfild.fild.szFullFile);
  1865.     Caption(hwndClient, " - %s", FileName());
  1866.  
  1867.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  1868.  
  1869.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1870.     return (MRESULT) 0;
  1871.     }
  1872. /*...e*/
  1873. /*...sUM_SAVE          \45\ save with old name:16:*/
  1874. case UM_SAVE:
  1875.     {
  1876.     HWND hwndClient = HWNDFROMMP(mp1);
  1877.     MOD_ERR mrc;
  1878.  
  1879.     LowPri();
  1880.     Caption(hwndClient, " - saving %s", FileName());
  1881.     mrc = ModWriteToFile(&mod, szFileName, "");
  1882.     Caption(hwndClient, "- %s", FileName());
  1883.     RegPri();
  1884.     if ( mrc != MOD_ERR_OK )
  1885.         {
  1886.         Warning(hwndClient, "Error saving %s: %s", FileName(), ModErrorString(mrc));
  1887.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1888.         return (MRESULT) 0;
  1889.         }
  1890.  
  1891.     fUnsavedChanges = FALSE;
  1892.  
  1893.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1894.     return (MRESULT) 0;
  1895.     }
  1896. /*...e*/
  1897. /*...sUM_SAVE_AS       \45\ save with new name:16:*/
  1898. case UM_SAVE_AS:
  1899.     {
  1900.     HWND hwndClient = HWNDFROMMP(mp1);
  1901.     GBMFILEDLG gbmfild;
  1902.     MOD_ERR mrc;
  1903.  
  1904.     memset(&gbmfild.fild, 0, sizeof(FILEDLG));
  1905.     gbmfild.fild.cbSize = sizeof(FILEDLG);
  1906.     gbmfild.fild.fl = (FDS_CENTER|FDS_SAVEAS_DIALOG|FDS_HELPBUTTON);
  1907.     strcpy(gbmfild.fild.szFullFile, szFileName);
  1908.     strcpy(gbmfild.szOptions, "");    
  1909.     GbmFileDlg(HWND_DESKTOP, hwndClient, &gbmfild);
  1910.     if ( gbmfild.fild.lReturn != DID_OK )
  1911.         {
  1912.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1913.         return (MRESULT) 0;
  1914.         }
  1915.  
  1916.     LowPri();
  1917.     Caption(hwndClient, " - saving as %s", gbmfild.fild.szFullFile);
  1918.     mrc = ModWriteToFile(&mod, gbmfild.fild.szFullFile, gbmfild.szOptions);
  1919.     Caption(hwndClient, " - %s", FileName());
  1920.     RegPri();
  1921.     if ( mrc != MOD_ERR_OK )
  1922.         {
  1923.         Warning(hwndClient, "Error saving as %s: %s", gbmfild.fild.szFullFile, ModErrorString(mrc));
  1924.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1925.         return (MRESULT) 0;
  1926.         }
  1927.  
  1928.     strcpy(szFileName, gbmfild.fild.szFullFile);
  1929.     Caption(hwndClient, " - %s", FileName());
  1930.     fUnsavedChanges = FALSE;
  1931.  
  1932.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1933.     return (MRESULT) 0;
  1934.     }
  1935. /*...e*/
  1936. /*...sUM_EXPORT_MET    \45\ export to Metafile:16:*/
  1937. case UM_EXPORT_MET:
  1938.     {
  1939.     HAB hab = WinQueryAnchorBlock(hwnd);
  1940.     HWND hwndClient = HWNDFROMMP(mp1);
  1941.     MOD_ERR mrc;
  1942.     BOOL fOk;
  1943.     HMF hmf;
  1944.     FILEDLG fild;
  1945.  
  1946.     memset(&fild, 0, sizeof(FILEDLG));
  1947.     fild.cbSize = sizeof(FILEDLG);
  1948.     fild.fl = (FDS_CENTER|FDS_SAVEAS_DIALOG);
  1949.     fild.pszTitle = "Export to MetaFile";
  1950.     if ( szFileName[0] != '\0' )
  1951.         {
  1952.         strcpy(fild.szFullFile, szFileName);
  1953.         RemoveExtension(fild.szFullFile);
  1954.         strcat(fild.szFullFile, ".MET");
  1955.         }
  1956.     else
  1957.         strcpy(fild.szFullFile, "EXPORT.MET");
  1958.     WinFileDlg(HWND_DESKTOP, hwndClient, &fild);
  1959.     if ( fild.lReturn != DID_OK )
  1960.         {
  1961.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1962.         return (MRESULT) 0;
  1963.         }
  1964.  
  1965.     LowPri();
  1966.     Caption(hwndClient, " - making metafile");
  1967.     mrc = ModMakeHMF(hbm, hab, &hmf);
  1968.     Caption(hwndClient, " - %s", FileName());
  1969.     RegPri();
  1970.     if ( mrc != MOD_ERR_OK )
  1971.         {
  1972.         Warning(hwndClient, "Error making metafile: %s", ModErrorString(mrc));
  1973.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1974.         return (MRESULT) 0;
  1975.         }
  1976.  
  1977.     LowPri();
  1978.     Caption(hwndClient, " - exporting to %s", fild.szFullFile);
  1979.     remove(fild.szFullFile);
  1980.     fOk = GpiSaveMetaFile(hmf, fild.szFullFile);
  1981.     Caption(hwndClient, " - %s", FileName());
  1982.     RegPri();
  1983.  
  1984.     GpiDeleteMetaFile(hmf);
  1985.  
  1986.     if ( !fOk )
  1987.         {
  1988.         Warning(hwndClient, "Error exporting to %s", fild.szFullFile);
  1989.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1990.         return (MRESULT) 0;
  1991.         }
  1992.  
  1993.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  1994.     return (MRESULT) 0;
  1995.     }
  1996. /*...e*/
  1997. /*...sUM_PRINT         \45\ print image:16:*/
  1998. case UM_PRINT:
  1999.     {
  2000.     HWND hwndClient = HWNDFROMMP(mp1);
  2001.     HAB hab = WinQueryAnchorBlock(hwndClient);
  2002.     HBITMAP hbmCopy;
  2003.     BMP_ERR brc;
  2004.     CHAR sz[255+1];
  2005.  
  2006.     Caption(hwndClient, " - printing %s", FileName());
  2007.     LowPri();
  2008.     if ( (brc = BmpCopy(hab, hbm, &hbmCopy)) != BMP_ERR_OK )
  2009.         {
  2010.         RegPri();
  2011.         Caption(hwndClient, " - %s", FileName());
  2012.         Warning(hwndClient, "Error preparing to print %s: %s", FileName(), BmpErrorMessage(brc, sz));
  2013.         }
  2014.     else if ( (brc = BmpPrint(hab, hbmCopy, lColorFg, lColorBg, FileName(), szAppName, NULL)) != BMP_ERR_OK )
  2015.         {
  2016.         RegPri();
  2017.         Caption(hwndClient, " - %s", FileName());
  2018.         GpiDeleteBitmap(hbmCopy);
  2019.         Warning(hwndClient, "Error printing: %s", BmpErrorMessage(brc, sz));
  2020.         }
  2021.     else
  2022.         {
  2023.         RegPri();
  2024.         Caption(hwndClient, " - %s", FileName());
  2025.         GpiDeleteBitmap(hbmCopy);
  2026.         }
  2027.  
  2028.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2029.     return (MRESULT) 0;
  2030.     }
  2031.  
  2032. /*...e*/
  2033. /*...sUM_SNAPSHOT      \45\ take snapshot of screen:16:*/
  2034. case UM_SNAPSHOT:
  2035.     {
  2036.     HAB hab = WinQueryAnchorBlock(hwnd);
  2037.     HWND hwndClient = HWNDFROMMP(mp1);
  2038.     int cxScreen = (int) WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
  2039.     int cyScreen = (int) WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
  2040.     MOD_ERR mrc; MOD modNew;
  2041.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2042.     HDC hdcMem; SIZEL sizl; HPS hpsMem; HBITMAP hbmMem;
  2043.     struct
  2044.         {
  2045.         BITMAPINFOHEADER2 bmp2;
  2046.         RGB2 argb2Color[0x100];
  2047.         } bm;
  2048.  
  2049.     if ( (hdcMem = DevOpenDC(hab, OD_MEMORY, "*", 0L, (PDEVOPENDATA) NULL, (HDC) NULL)) == (HDC) NULL )
  2050.         {
  2051.         Warning(hwndClient, "Error snapshotting screen: can't create PM HDC resource");
  2052.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2053.         return (MRESULT) 0;
  2054.         }
  2055.  
  2056.     sizl.cx = cxScreen;
  2057.     sizl.cy = cyScreen;
  2058.     if ( (hpsMem = GpiCreatePS(hab, hdcMem, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC)) == (HPS) NULL )
  2059.         {
  2060.         DevCloseDC(hdcMem);
  2061.         Warning(hwndClient, "Error snapshotting screen: can't create PM HPS resource");
  2062.         return (MRESULT) 0;
  2063.         }
  2064.  
  2065.     memset(&(bm.bmp2), 0, sizeof(BITMAPINFOHEADER2));
  2066.     bm.bmp2.cbFix     = 16;
  2067.     bm.bmp2.cx        = cxScreen;
  2068.     bm.bmp2.cy        = cyScreen;
  2069.     bm.bmp2.cBitCount = lBitCountScreen;
  2070.     bm.bmp2.cPlanes   = 1;
  2071.     if ( (hbmMem = GpiCreateBitmap(hpsMem, &(bm.bmp2), 0L, NULL, (BITMAPINFO2 *) &bm)) == (HBITMAP) NULL )
  2072.         {
  2073.         GpiDestroyPS(hpsMem);
  2074.         DevCloseDC(hdcMem);
  2075.         Warning(hwndClient, "Error snapshotting screen: can't create PM HBITMAP resource");
  2076.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2077.         return (MRESULT) 0;
  2078.         }
  2079.     GpiSetBitmap(hpsMem, hbmMem);
  2080.  
  2081.     LowPri();
  2082.     Caption(hwndClient, " - snapshotting (untitled)");
  2083. /*...sdo the actual copy from the screen to hpsMem:24:*/
  2084. {
  2085. HWND hwndFrame = WinQueryWindow(hwndClient, QW_PARENT);
  2086. POINTL aptl[3];
  2087. HPS hps;
  2088.  
  2089. WinShowWindow(hwndFrame, FALSE);
  2090. DosSleep(2000L); /* Give other windows a chance to repaint */
  2091. hps = WinGetScreenPS(HWND_DESKTOP);
  2092. aptl[0].x = 0;
  2093. aptl[0].y = 0;
  2094. aptl[1].x = cxScreen;
  2095. aptl[1].y = cyScreen;
  2096. aptl[2].x = 0;
  2097. aptl[2].y = 0;
  2098. GpiBitBlt(hpsMem, hps, 3L, aptl, ROP_SRCCOPY, BBO_IGNORE);
  2099. WinReleasePS(hps);
  2100. WinShowWindow(hwndFrame, TRUE);
  2101. }
  2102. /*...e*/
  2103.     mrc = ModCreateFromHPS(hpsMem ,cxScreen, cyScreen,
  2104.         ( lBitCountScreen > 8 ) ? 24 : lBitCountScreen,
  2105.         &modNew);
  2106.     Caption(hwndClient, " - %s", FileName());
  2107.     RegPri();
  2108.  
  2109.     GpiSetBitmap(hpsMem, (HBITMAP) NULL);
  2110.     GpiDeleteBitmap(hbmMem);
  2111.     GpiDestroyPS(hpsMem);
  2112.     DevCloseDC(hdcMem);
  2113.  
  2114.     if ( mrc != MOD_ERR_OK )
  2115.         {
  2116.         Warning(hwndClient, "Error snapshotting screen: %s", ModErrorString(mrc));
  2117.         WinSendMsg(hwndClient, UM_DONE, NULL, NULL);
  2118.         return (MRESULT) 0;
  2119.         }        
  2120.  
  2121.     if ( !MakeVisualBg(hwndClient, &modNew, VIEW_NULL, &hbmNew, &lColorBgNew, &lColorFgNew, "(untitled)") )
  2122.         {
  2123.         ModDelete(&modNew);
  2124.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2125.         return (MRESULT) 0;
  2126.         }
  2127.  
  2128.     bView = VIEW_NULL;
  2129.  
  2130.     DiscardUndo();
  2131.     if ( fGotBitmap )
  2132.         {
  2133.         SetNoBitmap();
  2134.         ModDelete(&mod);
  2135.         fGotBitmap = FALSE;
  2136.         fUnsavedChanges = FALSE;
  2137.         fSelectionDefined = FALSE;
  2138.         }
  2139.  
  2140.     ModMove(&modNew, &mod);
  2141.     fGotBitmap = TRUE;
  2142.     fUnsavedChanges = TRUE;
  2143.     fSelectionDefined = FALSE;
  2144.     strcpy(szFileName, ""); /* Become untitled */
  2145.     Caption(hwndClient, " - %s", FileName());
  2146.  
  2147.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2148.  
  2149.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2150.     return (MRESULT) 0;
  2151.     }
  2152. /*...e*/
  2153. /*...sUM_UNDO          \45\ undo previous operation:16:*/
  2154. case UM_UNDO:
  2155.     {
  2156.     HWND hwndClient = HWNDFROMMP(mp1);
  2157.     MOD modNew;
  2158.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2159.     const CHAR *szWhat;
  2160.  
  2161.     UseUndoBuffer(&modNew, &szWhat);
  2162.  
  2163.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  2164.         {
  2165.         ModDelete(&modNew);
  2166.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2167.         return (MRESULT) 0;
  2168.         }
  2169.  
  2170.     if ( fGotBitmap )
  2171.         ModDelete(&mod);
  2172.  
  2173.     ModMove(&modNew, &mod);
  2174.     fGotBitmap        = TRUE;
  2175.     fUnsavedChanges   = TRUE;
  2176.     fSelectionDefined = FALSE;
  2177.  
  2178.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2179.  
  2180.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2181.     return (MRESULT) 0;
  2182.     }
  2183. /*...e*/
  2184. /*...sUM_SELECT        \45\ select region of bitmap:16:*/
  2185. case UM_SELECT:
  2186.     {
  2187.     HWND hwndClient = HWNDFROMMP(mp1);
  2188.     BITMAPINFOHEADER2 bmp;
  2189.     SWP swpBitmap, swpScroller;
  2190.     int cxScrollbar, cyScrollbar, x, y, cx, cy;
  2191.  
  2192.     bmp.cbFix = sizeof(BITMAPINFOHEADER2);
  2193.     GpiQueryBitmapInfoHeader(hbm, &bmp);
  2194.     fSelectionDefined = FALSE;
  2195.     WinInvalidateRect(hwndBitmap, NULL, TRUE);
  2196.     WinUpdateWindow(hwndBitmap);
  2197.     WinQueryWindowPos(hwndBitmap  , &swpBitmap  );
  2198.     WinQueryWindowPos(hwndScroller, &swpScroller);
  2199.     cxScrollbar = (int) WinQuerySysValue(HWND_DESKTOP, SV_CXVSCROLL);
  2200.     cyScrollbar = (int) WinQuerySysValue(HWND_DESKTOP, SV_CYHSCROLL);
  2201.     x = - (int) swpBitmap.x              ; if ( x < 0 ) x = 0;
  2202.     y = - (int) swpBitmap.y + cyScrollbar; if ( y < 0 ) y = 0;
  2203.     cx = (int) swpScroller.cx - cxScrollbar; if ( cx > (int) bmp.cx ) cx = (int) bmp.cx;
  2204.     cy = (int) swpScroller.cy - cyScrollbar; if ( cy > (int) bmp.cy ) cy = (int) bmp.cy;
  2205.     fSelectionDefined = Select(hwndClient, hwndBitmap, &rclSelection, x, y, cx, cy);
  2206.     if ( fSelectionDefined )
  2207.         {
  2208.         if ( rclSelection.xLeft   != rclSelection.xRight &&
  2209.              rclSelection.yBottom != rclSelection.yTop   )
  2210.             {
  2211.             WinInvalidateRect(hwndBitmap, NULL, TRUE);
  2212.             WinUpdateWindow(hwndBitmap);
  2213.             }
  2214.         }
  2215.  
  2216.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2217.     return (MRESULT) 0;
  2218.     }
  2219. /*...e*/
  2220. /*...sUM_DESELECT      \45\ de\45\select any selcted area:16:*/
  2221. case UM_DESELECT:
  2222.     if ( fSelectionDefined )
  2223.         {
  2224.         fSelectionDefined = FALSE;
  2225.         WinInvalidateRect(hwndBitmap, NULL, TRUE);
  2226.         WinUpdateWindow(hwndBitmap);
  2227.         }
  2228.  
  2229.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2230.     return (MRESULT) 0;
  2231. /*...e*/
  2232. /*...sUM_COPY          \45\ copy bitmap to clipboard:16:*/
  2233. case UM_COPY:
  2234.     {
  2235.     HWND hwndClient = HWNDFROMMP(mp1);
  2236.     if ( fSelectionDefined )
  2237.         CopyToClipbrd(hwndClient, rclSelection);
  2238.     else
  2239.         {
  2240.         RECTL rclAll;
  2241.         rclAll.xLeft = 0; rclAll.xRight = mod.gbm.w;
  2242.         rclAll.yBottom = 0; rclAll.yTop = mod.gbm.h;
  2243.         CopyToClipbrd(hwndClient, rclAll);
  2244.         }
  2245.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2246.     return (MRESULT) 0;
  2247.     }
  2248. /*...e*/
  2249. /*...sUM_PASTE         \45\ paste from clipboard:16:*/
  2250. case UM_PASTE:
  2251.     {
  2252.     HWND hwndClient = HWNDFROMMP(mp1);
  2253.     HAB hab = WinQueryAnchorBlock(hwndClient);
  2254.     HDC hdc; SIZEL sizl; HPS hps; HBITMAP hbmClipbrd;
  2255.     MOD_ERR mrc; MOD modNew;
  2256.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2257.     struct
  2258.         {
  2259.         BITMAPINFOHEADER2 bmp2;
  2260.         RGB2 argb2Color[0x100];
  2261.         } bm;
  2262.  
  2263.     WinOpenClipbrd(hab);
  2264.     if ( (hbmClipbrd = (HBITMAP) WinQueryClipbrdData(hab, CF_BITMAP)) == (HBITMAP) NULL )
  2265.         /* Not able to get bitmap from the clipboard */
  2266.         {
  2267.         WinCloseClipbrd(hab);
  2268.         Warning(hwndClient, "Error pasting: can't access clipboard data");
  2269.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2270.         return (MRESULT) 0;
  2271.         }
  2272.  
  2273.     memset(&(bm.bmp2), 0, sizeof(bm.bmp2));
  2274.     bm.bmp2.cbFix = 16;
  2275.     GpiQueryBitmapInfoHeader(hbmClipbrd, &bm.bmp2);
  2276.  
  2277.     if ( lBitCountScreen < bm.bmp2.cBitCount )
  2278.         /* Data only actually stored in clipboard quality */
  2279.         bm.bmp2.cBitCount = lBitCountScreen;
  2280.  
  2281.     if ( bm.bmp2.cBitCount == 16 )
  2282.         bm.bmp2.cBitCount = 24;
  2283.  
  2284.     if ( bm.bmp2.cPlanes != 1 )
  2285.         {
  2286.         WinCloseClipbrd(hab);
  2287.         Warning(hwndClient, "Error pasting: don't know how to handle cPlanes != 1");
  2288.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2289.         return (MRESULT) 0;
  2290.         }
  2291.  
  2292.     if ( (hdc = DevOpenDC(hab, OD_MEMORY, "*", 0L, (PDEVOPENDATA) NULL, (HDC) NULL)) == (HDC) NULL )
  2293.         {
  2294.         WinCloseClipbrd(hab);
  2295.         Warning(hwnd, "Error pasting: can't create PM HDC resource");
  2296.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2297.         return (MRESULT) 0;
  2298.         }
  2299.  
  2300.     sizl.cx = bm.bmp2.cx;
  2301.     sizl.cy = bm.bmp2.cy;
  2302.     if ( (hps = GpiCreatePS(hab, hdc, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC)) == (HPS) NULL )
  2303.         {
  2304.         DevCloseDC(hdc);
  2305.         WinCloseClipbrd(hab);
  2306.         Warning(hwnd, "Error pasting: can't create PM HPS resource");
  2307.         return (MRESULT) 0;
  2308.         }
  2309.  
  2310.     LowPri();
  2311.     Caption(hwndClient, " - pasting %s", FileName());
  2312.     GpiSetBitmap(hps, hbmClipbrd);
  2313.     mrc = ModCreateFromHPS(hps, bm.bmp2.cx, bm.bmp2.cy,
  2314.         ( bm.bmp2.cBitCount > 8 ) ? 24 : bm.bmp2.cBitCount,
  2315.         &modNew);
  2316.     GpiSetBitmap(hps, (HBITMAP) NULL);
  2317.     Caption(hwndClient, " - %s", FileName());
  2318.     RegPri();
  2319.  
  2320.     GpiDestroyPS(hps);
  2321.     DevCloseDC(hdc);
  2322.     WinCloseClipbrd(hab);
  2323.  
  2324.     if ( mrc != MOD_ERR_OK )
  2325.         {
  2326.         Warning(hwndClient, "Error pasting: can't query bitmap bits from clipboard");
  2327.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2328.         return (MRESULT) 0;
  2329.         }
  2330.  
  2331.     /* Now modNew contains a bitmap from the clipboard */
  2332.  
  2333.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, "(untitled)") )
  2334.         {
  2335.         ModDelete(&modNew);
  2336.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2337.         return (MRESULT) 0;
  2338.         }
  2339.  
  2340.     if ( fGotBitmap )
  2341.         {
  2342.         fGotBitmap        = FALSE;
  2343.         fUnsavedChanges   = FALSE;
  2344.         fSelectionDefined = FALSE;
  2345.         KeepForUndo(&mod, "paste");
  2346.         SetNoBitmap();
  2347.         }
  2348.     else        
  2349.         DiscardUndo();
  2350.  
  2351.     ModMove(&modNew, &mod);
  2352.     fGotBitmap        = TRUE;
  2353.     fUnsavedChanges   = TRUE;
  2354.     fSelectionDefined = FALSE;
  2355.     Caption(hwndClient, " - %s", FileName());
  2356.  
  2357.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2358.  
  2359.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2360.     return (MRESULT) 0;
  2361.     }
  2362. /*...e*/
  2363. /*...sUM_REF_HORZ      \45\ reflect horizontally:16:*/
  2364. case UM_REF_HORZ:
  2365.     {
  2366.     HWND hwndClient = HWNDFROMMP(mp1);
  2367.     Reflect(hwndClient, ModReflectHorz, "reflecting horizontally", "horizontal reflection");
  2368.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2369.     return (MRESULT) 0;
  2370.     }
  2371. /*...e*/
  2372. /*...sUM_REF_VERT      \45\ reflect vertically:16:*/
  2373. case UM_REF_VERT:
  2374.     {
  2375.     HWND hwndClient = HWNDFROMMP(mp1);
  2376.     Reflect(hwndClient, ModReflectVert, "reflecting vertically", "vertical reflection");
  2377.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2378.     return (MRESULT) 0;
  2379.     }
  2380. /*...e*/
  2381. /*...sUM_ROT_90        \45\ rotate 90 degrees:16:*/
  2382. case UM_ROT_90:
  2383.     {
  2384.     HWND hwndClient = HWNDFROMMP(mp1);
  2385.     Reflect(hwndClient, ModRotate90, "rotating by 90 degrees", "90 degree rotation");
  2386.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2387.     return (MRESULT) 0;
  2388.     }
  2389. /*...e*/
  2390. /*...sUM_ROT_180       \45\ rotate 180 degrees:16:*/
  2391. case UM_ROT_180:
  2392.     {
  2393.     HWND hwndClient = HWNDFROMMP(mp1);
  2394.     Reflect(hwndClient, ModRotate180, "rotating by 180 degrees", "180 degree rotation");
  2395.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2396.     return (MRESULT) 0;
  2397.     }
  2398. /*...e*/
  2399. /*...sUM_ROT_270       \45\ rotate 270 degrees:16:*/
  2400. case UM_ROT_270:
  2401.     {
  2402.     HWND hwndClient = HWNDFROMMP(mp1);
  2403.     Reflect(hwndClient, ModRotate270, "rotating by 270 degrees", "270 degree rotation");
  2404.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2405.     return (MRESULT) 0;
  2406.     }
  2407. /*...e*/
  2408. /*...sUM_TRANSPOSE     \45\ transpose x for y:16:*/
  2409. case UM_TRANSPOSE:
  2410.     {
  2411.     HWND hwndClient = HWNDFROMMP(mp1);
  2412.     Reflect(hwndClient, ModTranspose, "transposing", "transposition");
  2413.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2414.     return (MRESULT) 0;
  2415.     }
  2416. /*...e*/
  2417. /*...sUM_CROP          \45\ crop to selection:16:*/
  2418. case UM_CROP:
  2419.     {
  2420.     HWND hwndClient = HWNDFROMMP(mp1);
  2421.     MOD_ERR mrc; MOD modNew;
  2422.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2423.  
  2424.     LowPri();
  2425.     Caption(hwndClient, " - cropping %s", FileName());
  2426.     mrc = ModExtractSubrectangle(&mod,
  2427.         rclSelection.xLeft,
  2428.         rclSelection.yBottom,
  2429.         rclSelection.xRight - rclSelection.xLeft,
  2430.         rclSelection.yTop - rclSelection.yBottom,
  2431.         &modNew);
  2432.     Caption(hwndClient, " - %s", FileName());
  2433.     RegPri();
  2434.     if ( mrc != MOD_ERR_OK )
  2435.         {
  2436.         Warning(hwndClient, "Error cropping: %s", ModErrorString(mrc));
  2437.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2438.         return (MRESULT) 0;
  2439.         }
  2440.  
  2441.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  2442.         {
  2443.         ModDelete(&modNew);
  2444.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2445.         return (MRESULT) 0;
  2446.         }
  2447.  
  2448.     KeepForUndo(&mod, "cropping");
  2449.  
  2450.     ModMove(&modNew, &mod);
  2451.     fUnsavedChanges = TRUE;
  2452.     fSelectionDefined = FALSE;
  2453.  
  2454.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2455.  
  2456.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2457.     return (MRESULT) 0;
  2458.     }
  2459. /*...e*/
  2460. /*...sUM_COLOUR        \45\ colour space:16:*/
  2461. case UM_COLOUR:
  2462.     {
  2463.     HWND hwndClient = HWNDFROMMP(mp1);
  2464.     MOD_ERR mrc; MOD modNew;
  2465.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2466.  
  2467.     if ( !WinDlgBox(HWND_DESKTOP, hwndClient, ColourDlgProc, (HMODULE) NULL, RID_DLG_COLOUR, NULL) )
  2468.         {
  2469.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2470.         return (MRESULT) 0;
  2471.         }
  2472.     
  2473.     LowPri();
  2474.     Caption(hwndClient, " - colour space mapping %s", FileName());
  2475.     mrc = ModColourAdjust(&mod, map, gama, shelf, &modNew);
  2476.     Caption(hwndClient, " - %s", FileName());
  2477.     RegPri();
  2478.     if ( mrc != MOD_ERR_OK )
  2479.         {
  2480.         Warning(hwndClient, "Error colour space mapping %s: %s", FileName(), ModErrorString(mrc));
  2481.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2482.         return (MRESULT) 0;
  2483.         }
  2484.  
  2485.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  2486.         {
  2487.         ModDelete(&modNew);
  2488.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2489.         return (MRESULT) 0;
  2490.         }
  2491.  
  2492.     KeepForUndo(&mod, "mapping");
  2493.  
  2494.     ModMove(&modNew, &mod);
  2495.     fUnsavedChanges = TRUE;
  2496.  
  2497.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2498.  
  2499.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2500.     return (MRESULT) 0;
  2501.     }
  2502. /*...e*/
  2503. /*...sUM_MAP           \45\ map:16:*/
  2504. case UM_MAP:
  2505.     {
  2506.     HWND hwndClient = HWNDFROMMP(mp1);
  2507.     MOD_ERR mrc; MOD modNew;
  2508.     BYTE bViewFastSuggested, bViewFast;
  2509.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2510.  
  2511.     if ( !WinDlgBox(HWND_DESKTOP, hwndClient, MapDlgProc, (HMODULE) NULL, RID_DLG_MAP, NULL) )
  2512.         {
  2513.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2514.         return (MRESULT) 0;
  2515.         }
  2516.  
  2517. /*...sspeedup rendering later:24:*/
  2518. {
  2519. CHAR *szFast =    "You have just selected a mapping to a palette which has all "
  2520.         "of its colours in the screen palette. Therefore halftoning "
  2521.         "or error diffusion will cause no improvement - change View "
  2522.         "setting to Raw PM mapping?";
  2523. bViewFastSuggested = bViewFast = bView;
  2524.  
  2525. switch ( iPal )
  2526.     {
  2527.     case CVT_BW:
  2528.         bViewFastSuggested = VIEW_NULL;
  2529.         break;
  2530.     case CVT_8:
  2531.     case CVT_VGA:
  2532.         if ( lBitCountScreen == 4 )
  2533.             bViewFastSuggested = VIEW_NULL;
  2534.         break;
  2535.     case CVT_784:
  2536.         if ( lBitCountScreen == 8 )
  2537.             bViewFastSuggested = VIEW_NULL;
  2538.         break;
  2539.     case CVT_FREQ:
  2540.     case CVT_RGB:
  2541.         if ( lBitCountScreen == 16 &&
  2542.              iKeepRed   <= 5 &&
  2543.              iKeepGreen <= 6 &&
  2544.              iKeepBlue  <= 5 )
  2545.             bViewFastSuggested = VIEW_NULL;
  2546.         break;
  2547.     }
  2548.  
  2549. if ( bViewFastSuggested != bView )
  2550.     switch ( WinMessageBox(HWND_DESKTOP, hwndClient, szFast, szAppName, 0, MB_YESNOCANCEL | MB_WARNING | MB_MOVEABLE) )
  2551.         {
  2552.         case MBID_YES:
  2553.             bViewFast = bViewFastSuggested;
  2554.             break;
  2555.         case MBID_NO:
  2556.             break;
  2557.         case MBID_CANCEL:
  2558.             WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2559.             return (MRESULT) 0;
  2560.         }
  2561. }
  2562. /*...e*/
  2563.     
  2564.     LowPri();
  2565.     Caption(hwndClient, " - mapping %s", FileName());
  2566.     mrc = ModBppMap(&mod, iPal, iAlg, iKeepRed, iKeepGreen, iKeepBlue, nCols, &modNew);
  2567.     Caption(hwndClient, " - %s", FileName());
  2568.     RegPri();
  2569.     if ( mrc != MOD_ERR_OK )
  2570.         {
  2571.         Warning(hwndClient, "Error mapping %s: %s", FileName(), ModErrorString(mrc));
  2572.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2573.         return (MRESULT) 0;
  2574.         }
  2575.  
  2576.     if ( !MakeVisualBg(hwndClient, &modNew, bViewFast, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  2577.         {
  2578.         ModDelete(&modNew);
  2579.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2580.         return (MRESULT) 0;
  2581.         }
  2582.  
  2583.     bView = bViewFast;
  2584.  
  2585.     KeepForUndo(&mod, "mapping");
  2586.  
  2587.     ModMove(&modNew, &mod);
  2588.     fUnsavedChanges = TRUE;
  2589.  
  2590.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2591.  
  2592.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2593.     return (MRESULT) 0;
  2594.     }
  2595. /*...e*/
  2596. /*...sUM_RESIZE        \45\ resize:16:*/
  2597. case UM_RESIZE:
  2598.     {
  2599.     HWND hwndClient = HWNDFROMMP(mp1);
  2600.     MOD_ERR mrc; MOD modNew;
  2601.     HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2602.  
  2603.     if ( !WinDlgBox(HWND_DESKTOP, hwndClient, ResizeDlgProc, (HMODULE) NULL, RID_DLG_RESIZE, NULL) )
  2604.         {
  2605.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2606.         return (MRESULT) 0;
  2607.         }
  2608.     
  2609.     LowPri();
  2610.     Caption(hwndClient, " - resizing %s", FileName());
  2611.     mrc = ModResize(&mod, cxNew, cyNew, &modNew);
  2612.     Caption(hwndClient, " - %s", FileName());
  2613.     RegPri();
  2614.     if ( mrc != MOD_ERR_OK )
  2615.         {
  2616.         Warning(hwndClient, "Error resizing %s: %s", FileName(), ModErrorString(mrc));
  2617.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2618.         return (MRESULT) 0;
  2619.         }
  2620.  
  2621.     if ( !MakeVisualBg(hwndClient, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew, FileName()) )
  2622.         {
  2623.         ModDelete(&modNew);
  2624.         WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2625.         return (MRESULT) 0;
  2626.         }
  2627.  
  2628.     KeepForUndo(&mod, "resizing");
  2629.  
  2630.     ModMove(&modNew, &mod);
  2631.     fSelectionDefined = FALSE;
  2632.     fUnsavedChanges   = TRUE;
  2633.  
  2634.     SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  2635.  
  2636.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2637.     return (MRESULT) 0;
  2638.     }
  2639. /*...e*/
  2640. /*...sUM_VIEW_NULL     \45\ view with no transform:16:*/
  2641. case UM_VIEW_NULL:
  2642.     {
  2643.     HWND hwndClient = HWNDFROMMP(mp1);
  2644.     NewView(hwndClient, VIEW_NULL);
  2645.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2646.     return (MRESULT) 0;
  2647.     }
  2648. /*...e*/
  2649. /*...sUM_VIEW_HALFTONE \45\ view with error diffusion:16:*/
  2650. case UM_VIEW_HALFTONE:
  2651.     {
  2652.     HWND hwndClient = HWNDFROMMP(mp1);
  2653.     NewView(hwndClient, VIEW_HALFTONE);
  2654.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2655.     return (MRESULT) 0;
  2656.     }
  2657. /*...e*/
  2658. /*...sUM_VIEW_ERRDIFF  \45\ view with halftoning:16:*/
  2659. case UM_VIEW_ERRDIFF:
  2660.     {
  2661.     HWND hwndClient = HWNDFROMMP(mp1);
  2662.     NewView(hwndClient, VIEW_ERRDIFF);
  2663.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2664.     return (MRESULT) 0;
  2665.     }
  2666. /*...e*/
  2667. /*...sUM_ABOUT         \45\ about box:16:*/
  2668. case UM_ABOUT:
  2669.     {
  2670.     HWND hwndClient = HWNDFROMMP(mp1);
  2671.     WinDlgBox(HWND_DESKTOP, hwndClient, AboutDlgProc, (HMODULE) NULL, RID_DLG_ABOUT, NULL);
  2672.     WinSendMsg(hwnd, UM_DONE, NULL, NULL);
  2673.     return (MRESULT) 0;
  2674.     }
  2675. /*...e*/
  2676. /*...sUM_DONE          \45\ done\44\ and set pointer back:16:*/
  2677. case UM_DONE:
  2678.     {
  2679.     POINTL ptl;
  2680.     fBusy = FALSE;
  2681.     WinQueryPointerPos(HWND_DESKTOP, &ptl);
  2682.     WinSetPointerPos(HWND_DESKTOP, ptl.x, ptl.y);
  2683.     return (MRESULT) 0;
  2684.     }
  2685. /*...e*/
  2686.         }
  2687.     return WinDefWindowProc(hwnd, msg, mp1, mp2);
  2688.     }
  2689. /*...e*/
  2690. /*...sObjectThread:0:*/
  2691. #define    CB_STACK_OBJECT    0xf000
  2692.  
  2693. static VOID _Optlink ObjectThread(VOID *pvParams)
  2694.     {
  2695.     HAB hab = WinInitialize(0);
  2696.     HMQ hmq = WinCreateMsgQueue(hab, 0);
  2697.     QMSG qmsg;
  2698.  
  2699.     pvParams=pvParams; /* Suppress 'unref' compiler warning */
  2700.  
  2701.     WinRegisterClass(hab, WC_GBMV2_OBJECT, ObjectWndProc, 0L, 0);
  2702.  
  2703.     hwndObject = WinCreateWindow(HWND_OBJECT, WC_GBMV2_OBJECT, NULL, 0L, 0,0,0,0,
  2704.             HWND_DESKTOP, HWND_BOTTOM, 0, NULL, NULL);
  2705.  
  2706.     while ( WinGetMsg(hab, &qmsg, (HWND) NULL, 0, 0) )
  2707.         WinDispatchMsg(hab, &qmsg);
  2708.  
  2709.     WinDestroyWindow(hwndObject);
  2710.  
  2711.     WinDestroyMsgQueue(hmq);
  2712.     WinTerminate(hab);
  2713.     }
  2714. /*...e*/
  2715. /*...sGbmV2WndProc:0:*/
  2716. #define    WC_GBMV2 "GbmV2Class"
  2717.  
  2718. /*...smap MID_ to UM_:0:*/
  2719. typedef struct { int mid, um; } MIDUM;
  2720.  
  2721. static MIDUM mid_um[] =
  2722.     {
  2723.     MID_NEW,        UM_NEW,
  2724.     MID_OPEN,        UM_OPEN,
  2725.     MID_SAVE,        UM_SAVE,
  2726.     MID_SAVE_AS,        UM_SAVE_AS,
  2727.     MID_EXPORT_MET,        UM_EXPORT_MET,
  2728.     MID_PRINT,        UM_PRINT,
  2729.     MID_SNAPSHOT,        UM_SNAPSHOT,
  2730.     MID_UNDO,        UM_UNDO,
  2731.     MID_SELECT,        UM_SELECT,
  2732.     MID_DESELECT,        UM_DESELECT,
  2733.     MID_COPY,        UM_COPY,
  2734.     MID_PASTE,        UM_PASTE,
  2735.     MID_REF_HORZ,        UM_REF_HORZ,
  2736.     MID_REF_VERT,        UM_REF_VERT,
  2737.     MID_ROT_90,        UM_ROT_90,
  2738.     MID_ROT_180,        UM_ROT_180,
  2739.     MID_ROT_270,        UM_ROT_270,
  2740.     MID_TRANSPOSE,        UM_TRANSPOSE,
  2741.     MID_CROP,        UM_CROP,
  2742.     MID_COLOUR,        UM_COLOUR,
  2743.     MID_MAP,        UM_MAP,
  2744.     MID_RESIZE,        UM_RESIZE,
  2745.     MID_VIEW_NULL,        UM_VIEW_NULL,
  2746.     MID_VIEW_HALFTONE,    UM_VIEW_HALFTONE,
  2747.     MID_VIEW_ERRDIFF,    UM_VIEW_ERRDIFF,
  2748.     MID_ABOUT,        UM_ABOUT,
  2749.     };
  2750.  
  2751. #define    N_MID_UM (sizeof(mid_um)/sizeof(mid_um[0]))
  2752. /*...e*/
  2753.  
  2754. MRESULT _System GbmV2WndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  2755.     {
  2756.     switch ( (int) msg )
  2757.         {
  2758. /*...sWM_PAINT     \45\ repaint client area:16:*/
  2759. /*
  2760. Dead simple, as this window is usually completely covered by the scroller
  2761. window.
  2762. */
  2763.  
  2764. case WM_PAINT:
  2765.     {
  2766.     HPS hps = WinBeginPaint(hwnd, (HPS) NULL, (RECTL *) NULL);
  2767.  
  2768.     GpiSetBackColor(hps, CLR_RED);
  2769.  
  2770.     GpiErase(hps);
  2771.  
  2772.     WinEndPaint(hps);
  2773.     }
  2774.     return (MRESULT) 0;
  2775. /*...e*/
  2776. /*...sWM_SIZE      \45\ user has resized window:16:*/
  2777. case WM_SIZE:
  2778.     {
  2779.     SWP swp;
  2780.  
  2781.     WinQueryWindowPos(hwnd, &swp);
  2782.     WinSetWindowPos(hwndScroller, (HWND) NULL, 0, 0, swp.cx, swp.cy, SWP_SIZE);
  2783.     }
  2784.     break;
  2785. /*...e*/
  2786. /*...sWM_INITMENU  \45\ enable right menuitems:16:*/
  2787. case WM_INITMENU:
  2788.     {
  2789.     HWND hwndFrame = WinQueryWindow(hwnd, QW_PARENT);
  2790.     HWND hwndMenu = WinWindowFromID(hwndFrame, FID_MENU);
  2791.  
  2792.     switch ( SHORT1FROMMP(mp1) )
  2793.         {
  2794. /*...sMID_FILE:32:*/
  2795. case MID_FILE:
  2796.     EnableMenuItem(hwndMenu, MID_NEW       , !fBusy && fGotBitmap);
  2797.     EnableMenuItem(hwndMenu, MID_OPEN      , !fBusy              );
  2798.     EnableMenuItem(hwndMenu, MID_SAVE      , !fBusy && fGotBitmap && (szFileName[0] != '\0') );
  2799.     EnableMenuItem(hwndMenu, MID_SAVE_AS   , !fBusy && fGotBitmap);
  2800.     EnableMenuItem(hwndMenu, MID_EXPORT_MET, !fBusy && fGotBitmap);
  2801.     EnableMenuItem(hwndMenu, MID_PRINT     , !fBusy && fGotBitmap);
  2802.     EnableMenuItem(hwndMenu, MID_SNAPSHOT  , !fBusy              );
  2803.     return (MRESULT) 0;
  2804. /*...e*/
  2805. /*...sMID_EDIT:32:*/
  2806. case MID_EDIT:
  2807.     {
  2808.     CHAR szUndo[100+1];
  2809.     ULONG ulInfo;
  2810.     HAB hab = WinQueryAnchorBlock(hwnd);
  2811.     BOOL fClipbrd = WinQueryClipbrdFmtInfo(hab, CF_BITMAP, &ulInfo);
  2812.  
  2813.     EnableMenuItem(hwndMenu, MID_UNDO      , !fBusy && fCanUndo);
  2814.     if ( fCanUndo )
  2815.         sprintf(szUndo, "~Undo %s", szWhatUndo);
  2816.     else
  2817.         sprintf(szUndo, "~Undo");
  2818.     WinSendMsg(hwndMenu, MM_SETITEMTEXT, MPFROMSHORT(MID_UNDO), MPFROMP(szUndo));
  2819.     EnableMenuItem(hwndMenu, MID_SELECT    , !fBusy && fGotBitmap);
  2820.     EnableMenuItem(hwndMenu, MID_DESELECT  , !fBusy && fSelectionDefined);
  2821.     EnableMenuItem(hwndMenu, MID_COPY      , !fBusy && fGotBitmap);
  2822.     EnableMenuItem(hwndMenu, MID_PASTE     , !fBusy && fClipbrd);
  2823.     return (MRESULT) 0;
  2824.     }
  2825. /*...e*/
  2826. /*...sMID_BITMAP:32:*/
  2827. case MID_BITMAP:
  2828.     {
  2829.     BOOL fSquare = !fSelectionDefined ||
  2830.         ( rclSelection.xRight - rclSelection.xLeft ==
  2831.           rclSelection.yTop - rclSelection.yBottom );
  2832.     EnableMenuItem(hwndMenu, MID_REF_HORZ , !fBusy && fGotBitmap);
  2833.     EnableMenuItem(hwndMenu, MID_REF_VERT , !fBusy && fGotBitmap);
  2834.     EnableMenuItem(hwndMenu, MID_ROT_90   , !fBusy && fGotBitmap && fSquare);
  2835.     EnableMenuItem(hwndMenu, MID_ROT_180  , !fBusy && fGotBitmap);
  2836.     EnableMenuItem(hwndMenu, MID_ROT_270  , !fBusy && fGotBitmap && fSquare);
  2837.     EnableMenuItem(hwndMenu, MID_TRANSPOSE, !fBusy && fGotBitmap && fSquare);
  2838.     EnableMenuItem(hwndMenu, MID_CROP     , !fBusy && fGotBitmap && fSelectionDefined);
  2839.     EnableMenuItem(hwndMenu, MID_COLOUR   , !fBusy && fGotBitmap);
  2840.     EnableMenuItem(hwndMenu, MID_MAP      , !fBusy && fGotBitmap);
  2841.     EnableMenuItem(hwndMenu, MID_RESIZE   , !fBusy && fGotBitmap);
  2842.     return (MRESULT) 0;
  2843.     }
  2844. /*...e*/
  2845. /*...sMID_VIEW:32:*/
  2846. case MID_VIEW:
  2847.     {
  2848.     BOOL fHalftone = ( lBitCountScreen == 4 || lBitCountScreen == 8 || lBitCountScreen == 16 );
  2849.     BOOL fErrDiff  = ( lBitCountScreen == 4 || lBitCountScreen == 8 || lBitCountScreen == 16 );
  2850.  
  2851.     EnableMenuItem(hwndMenu, MID_VIEW_NULL    , !fBusy && fGotBitmap && bView != VIEW_NULL                 );
  2852.     EnableMenuItem(hwndMenu, MID_VIEW_HALFTONE, !fBusy && fGotBitmap && bView != VIEW_HALFTONE && fHalftone);
  2853.     EnableMenuItem(hwndMenu, MID_VIEW_ERRDIFF , !fBusy && fGotBitmap && bView != VIEW_ERRDIFF  && fErrDiff );
  2854.     CheckMenuItem(hwndMenu, MID_VIEW_NULL    , bView == VIEW_NULL    );
  2855.     CheckMenuItem(hwndMenu, MID_VIEW_HALFTONE, bView == VIEW_HALFTONE);
  2856.     CheckMenuItem(hwndMenu, MID_VIEW_ERRDIFF , bView == VIEW_ERRDIFF );
  2857.     return (MRESULT) 0;
  2858.     }
  2859. /*...e*/
  2860.         }
  2861.     }
  2862.     break;
  2863. /*...e*/
  2864. /*...sWM_COMMAND   \45\ menu command:16:*/
  2865. case WM_COMMAND:
  2866.     {
  2867.     USHORT cmd = COMMANDMSG(&msg)->cmd;
  2868.     int i;
  2869.  
  2870.     /* Try to handle majority case */
  2871.     for ( i = 0; i < N_MID_UM; i++ )
  2872.         if ( mid_um[i].mid == cmd )
  2873.             {
  2874.             fBusy = TRUE;
  2875.             WinPostMsg(hwndObject, mid_um[i].um, MPFROMHWND(hwnd), NULL);
  2876.             return (MRESULT) 0;
  2877.             }
  2878.  
  2879.     /* Now try infrequent cases */
  2880.     switch ( cmd )
  2881.         {
  2882. /*...sMID_HELP_FOR_HELP \45\ bring up help for help:32:*/
  2883. case MID_HELP_FOR_HELP:
  2884.     HlpHelpForHelp(hwndHelp);
  2885.     break;
  2886. /*...e*/
  2887. /*...sMID_EXIT          \45\ initiate shutdown of this app:32:*/
  2888. case MID_EXIT:
  2889.     WinPostMsg(hwnd, WM_QUIT, 0L, 0L);
  2890.     break;
  2891. /*...e*/
  2892.         }
  2893.  
  2894.     return (MRESULT) 0;
  2895.     }
  2896. /*...e*/
  2897. /*...sWM_CLOSE     \45\ close window:16:*/
  2898. case WM_CLOSE:
  2899.     WinPostMsg(hwnd, WM_QUIT, 0L, 0L);
  2900.     return (MRESULT) 0;
  2901. /*...e*/
  2902. /*...sWM_CONTROL   \45\ override scrolling amount:16:*/
  2903. case WM_CONTROL:
  2904.     if ( SHORT1FROMMP(mp1) == WID_SCROLL )
  2905.         switch ( SHORT2FROMMP(mp1) )
  2906.             {
  2907.             case SCN_HPAGE:
  2908.             case SCN_VPAGE:
  2909.                 return (MRESULT) SHORT2FROMMP(mp2);
  2910.             }
  2911.     break;
  2912. /*...e*/
  2913. /*...sWM_ACTIVATE  \45\ ensure help instance is ours:16:*/
  2914. case WM_ACTIVATE:
  2915.     {
  2916.     HWND hwndFrame = WinQueryWindow(hwnd, QW_PARENT);
  2917.  
  2918.     if ( mp1 && hwndHelp != (HWND) NULL )
  2919.         HlpActivate(hwndHelp, hwndFrame);
  2920.     }
  2921.     break;
  2922. /*...e*/
  2923. /*...sHM_\42\         \45\ redirect help support:16:*/
  2924. case HM_ERROR:
  2925. case HM_INFORM:
  2926. case HM_QUERY_KEYS_HELP:
  2927. case HM_EXT_HELP_UNDEFINED:
  2928. case HM_HELPSUBITEM_NOT_FOUND:
  2929.     return HlpWndProc(hwnd, msg, mp1, mp2);
  2930. /*...e*/
  2931.         }
  2932.     return WinDefWindowProc(hwnd, msg, mp1, mp2);
  2933.     }
  2934. /*...e*/
  2935. /*...smain:0:*/
  2936. int main(int argc, CHAR *argv[])
  2937.     {
  2938.     HAB hab = WinInitialize(0);
  2939.     HMQ hmq = WinCreateMsgQueue(hab, 0);
  2940.     QMSG qmsg;
  2941.     HWND hwndFrame, hwndClient;
  2942.     SWP swp;
  2943.     static ULONG flFrameFlags = FCF_TITLEBAR      | FCF_SYSMENU    |
  2944.                     FCF_SIZEBORDER    | FCF_MINMAX     |
  2945.                     FCF_MENU          | FCF_ICON       |
  2946.                     FCF_SHELLPOSITION | FCF_TASKLIST   |
  2947.                     FCF_ACCELTABLE    ;
  2948.  
  2949.     gbm_init();
  2950.  
  2951. /*...sdetermine lBitCountScreen:8:*/
  2952. {
  2953. HPS hps = WinGetPS(HWND_DESKTOP);
  2954. HDC hdc = GpiQueryDevice(hps);
  2955. DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1L, &lBitCountScreen);
  2956. WinReleasePS(hps);
  2957. }
  2958. /*...e*/
  2959.  
  2960.     DosCreateMutexSem(NULL, &hmtxHbm, 0 /*=Private*/, FALSE /*=unowned*/ );
  2961.  
  2962.     RegisterScrollClass(hab);
  2963.     WinRegisterClass(hab, WC_GBMV2, GbmV2WndProc, CS_CLIPCHILDREN|CS_SIZEREDRAW, 0);
  2964.     WinRegisterClass(hab, WC_BITMAP, BitmapWndProc, 0L, 0);
  2965.  
  2966.     hwndFrame = WinCreateStdWindow(
  2967.         HWND_DESKTOP,        /* Parent window handle              */
  2968.         WS_VISIBLE,        /* Style of frame window             */
  2969.         &flFrameFlags,        /* Pointer to control data           */
  2970.         WC_GBMV2,        /* Client window class name          */
  2971.         NULL,            /* Title bar text                    */
  2972.         0L,            /* Style of client window            */
  2973.         (HMODULE) NULL,        /* Module handle for resources       */
  2974.         RID_GBMV2,        /* ID of resources                   */
  2975.         &hwndClient);        /* Pointer to client window handle   */
  2976.  
  2977.     WinSetWindowText(hwndFrame, szAppName);
  2978.  
  2979.     WinQueryWindowPos(hwndClient, &swp);
  2980.  
  2981.     hwndScroller = WinCreateWindow(hwndClient, WC_SCROLL, "", WS_VISIBLE|SCS_HSCROLL|SCS_VSCROLL|SCS_HCENTRE|SCS_VCENTRE|SCS_HPAGE|SCS_VPAGE,
  2982.          0,0,swp.cx,swp.cy, hwndClient, HWND_BOTTOM, WID_SCROLL, NULL, NULL);
  2983.     hwndBitmap = WinCreateWindow(hwndScroller, WC_BITMAP, "", WS_VISIBLE|WS_CLIPSIBLINGS,
  2984.          0,0,0,0, hwndScroller, HWND_BOTTOM, WID_BITMAP, NULL, NULL);
  2985.     WinSetFocus(HWND_DESKTOP, hwndBitmap);
  2986.  
  2987.     _beginthread(ObjectThread, NULL, CB_STACK_OBJECT, NULL);
  2988.  
  2989.     if ( argc >= 2 )
  2990.         {
  2991.         CHAR *szOpt;
  2992.         MOD_ERR mrc; MOD modNew;
  2993.         HBITMAP hbmNew; LONG lColorBgNew, lColorFgNew;
  2994.  
  2995.         if ( (szOpt = strchr(argv[1], ',')) != NULL )
  2996.             *szOpt++ = '\0';
  2997.         else
  2998.             szOpt = "";
  2999.  
  3000.         if ( (mrc = ModCreateFromFile(argv[1], szOpt, &modNew)) != MOD_ERR_OK )
  3001.             Warning(HWND_DESKTOP, "Error loading %s: %s", argv[1], ModErrorString(mrc));
  3002.         else if ( !MakeVisual(HWND_DESKTOP, &modNew, bView, &hbmNew, &lColorBgNew, &lColorFgNew) )
  3003.             {
  3004.             ModDelete(&modNew);
  3005.             Warning(HWND_DESKTOP, "Error loading %s: can't create view bitmap", argv[1]);
  3006.             }
  3007.         else
  3008.             {
  3009.             ModMove(&modNew, &mod);
  3010.             fGotBitmap = TRUE;
  3011.             strcpy(szFileName, argv[1]);
  3012.             Caption(hwndClient, " - %s", szFileName);
  3013.             SetBitmap(hbmNew, lColorBgNew, lColorFgNew);
  3014.             }
  3015.         }
  3016.  
  3017.     if ( (hwndHelp = HlpInit(hwndClient,
  3018.                (HMODULE) NULL, RID_HELP_TABLE,
  3019.                "GBMV2.HLP GBMDLG.HLP", szAppName)) != (HWND) NULL )
  3020.         HlpActivate(hwndHelp, hwndFrame);
  3021.  
  3022.     do
  3023.         for ( ;; )
  3024.             {
  3025.             while ( WinGetMsg(hab, &qmsg, (HWND) NULL, 0, 0) )
  3026.                 WinDispatchMsg(hab, &qmsg);
  3027.             if ( !fBusy )
  3028.                 break;
  3029.             Warning(hwndFrame, "Cannot Close yet, %s is busy, please wait and retry", szAppName);
  3030.             }
  3031.     while ( !SaveChanges(hwndFrame) );
  3032.  
  3033.     if ( hwndHelp != (HWND) NULL )
  3034.         HlpDeinit(hwndHelp);
  3035.  
  3036.     WinPostMsg(hwndObject, WM_QUIT, NULL, NULL);
  3037.  
  3038.     WinDestroyWindow(hwndBitmap);
  3039.     WinDestroyWindow(hwndScroller);
  3040.     WinDestroyWindow(hwndFrame);
  3041.  
  3042.     DosCloseMutexSem(hmtxHbm);
  3043.  
  3044.     gbm_deinit();
  3045.  
  3046.     WinDestroyMsgQueue(hmq);
  3047.     WinTerminate(hab);
  3048.  
  3049.     return 0;
  3050.     }
  3051. /*...e*/
  3052.