home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwplascr.zip / XWPL0208.ZIP / tools / xmlview / xmlview.cpp < prev    next >
C/C++ Source or Header  |  2002-07-21  |  41KB  |  1,343 lines

  1.  
  2. /*
  3.  *@@sourcefile xmlview.cpp:
  4.  *      XML viewer, which displays the elements,
  5.  *      attributes, and DTD of any XML document
  6.  *      in a PM container tree view.
  7.  *
  8.  *@@header "xmlview.h"
  9.  *@@added V0.9.5 (2000-08-13) [umoeller]
  10.  */
  11.  
  12. /*
  13.  *      Copyright (C) 2000 Ulrich Möller.
  14.  *      This program is free software; you can redistribute it and/or modify
  15.  *      it under the terms of the GNU General Public License as published by
  16.  *      the Free Software Foundation, in version 2 as it comes in the COPYING
  17.  *      file of the XFolder main distribution.
  18.  *      This program is distributed in the hope that it will be useful,
  19.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.  *      GNU General Public License for more details.
  22.  */
  23.  
  24. #define OS2EMX_PLAIN_CHAR
  25.  
  26. #define INCL_DOS
  27. #define INCL_DOSERRORS
  28. #define INCL_WIN
  29. #include <os2.h>
  30.  
  31. // C library headers
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <stdio.h>
  35. #include <setjmp.h>
  36.  
  37. // generic headers
  38. #include "setup.h"
  39.  
  40. #include "xwpapi.h"
  41.  
  42. #include "expat\expat.h"
  43.  
  44. #include "helpers\cnrh.h"
  45. #include "helpers\datetime.h"           // date/time helper routines
  46. #include "helpers\dialog.h"
  47. #include "helpers\dosh.h"
  48. #include "helpers\except.h"
  49. #include "helpers\linklist.h"
  50. #include "helpers\standards.h"
  51. #include "helpers\stringh.h"
  52. #include "helpers\threads.h"
  53. #include "helpers\tree.h"
  54. #include "helpers\winh.h"
  55. #include "helpers\xstring.h"
  56. #include "helpers\xml.h"
  57.  
  58. #include "encodings\base.h"
  59.  
  60. #include "xmlview.h"
  61.  
  62. #pragma hdrstop
  63.  
  64. /* ******************************************************************
  65.  *
  66.  *   Global variables
  67.  *
  68.  ********************************************************************/
  69.  
  70. HWND        G_hwndMain = NULLHANDLE;
  71. HWND        G_hwndStatusBar = NULLHANDLE;
  72. HWND        G_hwndCnr = NULLHANDLE;
  73.  
  74. PFNWP       G_pfnwpCnrOrig = NULL,
  75.             G_fnwpFrameOrig = NULL;
  76.  
  77. const char  *INIAPP              = "XWorkplace:XMLView";
  78. const char  *INIKEY_MAINWINPOS   = "HandleFixWinPos";
  79. const char  *INIKEY_OPENDLG      = "FileOpenDlgPath";
  80.  
  81. const char  *APPTITLE            = "xmlview";
  82.  
  83. /*
  84.  *@@ NODERECORD:
  85.  *
  86.  */
  87.  
  88. typedef struct _NODERECORD
  89. {
  90.     RECORDCORE recc;
  91.  
  92. } NODERECORD, *PNODERECORD;
  93.  
  94. /*
  95.  *@@ XMLFILE:
  96.  *
  97.  *@@added V0.9.9 (2001-02-10) [umoeller]
  98.  */
  99.  
  100. typedef struct _XMLFILE
  101. {
  102.     XSTRING         strContents;
  103.                         // contents of the XML document
  104.  
  105.     PXMLDOM         pDom;
  106.  
  107. } XMLFILE, *PXMLFILE;
  108.  
  109. PXMLFILE     G_pLoadedFile = NULL;
  110.  
  111. /* ******************************************************************
  112.  *
  113.  *   XML load
  114.  *
  115.  ********************************************************************/
  116.  
  117. /*
  118.  *@@ winhSetWindowText:
  119.  *      sets a window's text to a variable text. This is
  120.  *      just like WinSetWindowText, but supports printf()
  121.  *      formatting.
  122.  *
  123.  *      This is limited to 2000 characters.
  124.  *
  125.  *@@added V0.9.9 (2001-02-10) [umoeller]
  126.  */
  127.  
  128. VOID winhSetVarWindowText(HWND hwnd,
  129.                           const char *pcszFormat,     // in: format string (as with printf)
  130.                           ...)                        // in: additional stuff (as with printf)
  131. {
  132.     va_list     args;
  133.     CHAR        sz[2000];
  134.  
  135.     va_start(args, pcszFormat);
  136.     vsprintf(sz, pcszFormat, args);
  137.     va_end(args);
  138.  
  139.     WinSetWindowText(hwnd, sz);
  140. }
  141.  
  142. /*
  143.  *@@ SuperFileDlg:
  144.  *
  145.  */
  146.  
  147. BOOL SuperFileDlg(HWND hwndOwner,    // in: owner for file dlg
  148.                   PSZ pszFile,       // in: file mask; out: fully q'd filename
  149.                                      //    (should be CCHMAXPATH in size)
  150.                   ULONG flFlags,     // in: any combination of the following:
  151.                                      // -- WINH_FOD_SAVEDLG: save dlg; else open dlg
  152.                                      // -- WINH_FOD_INILOADDIR: load FOD path from INI
  153.                                      // -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
  154.                   HINI hini,         // in: INI file to load/store last path from (can be HINI_USER)
  155.                   const char *pcszApplication, // in: INI application to load/store last path from
  156.                   const char *pcszKey)        // in: INI key to load/store last path from
  157. {
  158.     FILEDLG fd;
  159.     memset(&fd, 0, sizeof(FILEDLG));
  160.     fd.cbSize = sizeof(FILEDLG);
  161.     fd.fl = FDS_CENTER;
  162.  
  163.     if (flFlags & WINH_FOD_SAVEDLG)
  164.         fd.fl |= FDS_SAVEAS_DIALOG;
  165.     else
  166.         fd.fl |= FDS_OPEN_DIALOG;
  167.  
  168.     // default: copy pszFile
  169.     strcpy(fd.szFullFile, pszFile);
  170.  
  171.     if ( (hini) && (flFlags & WINH_FOD_INILOADDIR) )
  172.     {
  173.         // overwrite with initial directory for FOD from OS2.INI
  174.         if (PrfQueryProfileString(hini,
  175.                                   pcszApplication,
  176.                                   pcszKey,
  177.                                   "",      // default string V0.9.9 (2001-02-10) [umoeller]
  178.                                   fd.szFullFile,
  179.                                   sizeof(fd.szFullFile)-10)
  180.                     >= 2)
  181.         {
  182.             // found: append "\*"
  183.             strcat(fd.szFullFile, "\\");
  184.             strcat(fd.szFullFile, pszFile);
  185.         }
  186.     }
  187.  
  188.     if (    WinFileDlg(HWND_DESKTOP,    // parent
  189.                        hwndOwner, // owner
  190.                        &fd)
  191.         && (fd.lReturn == DID_OK)
  192.        )
  193.     {
  194.         // save path back?
  195.         if (    (hini)
  196.              && (flFlags & WINH_FOD_INISAVEDIR)
  197.            )
  198.         {
  199.             // get the directory that was used
  200.             PSZ p = strrchr(fd.szFullFile, '\\');
  201.             if (p)
  202.             {
  203.                 // contains directory:
  204.                 // copy to OS2.INI
  205.                 PSZ pszDir = strhSubstr(fd.szFullFile, p);
  206.                 if (pszDir)
  207.                 {
  208.                     PrfWriteProfileString(hini,
  209.                                           pcszApplication,
  210.                                           pcszKey,
  211.                                           pszDir);
  212.                     free(pszDir);
  213.                 }
  214.             }
  215.         }
  216.  
  217.         strcpy(pszFile, fd.szFullFile);
  218.  
  219.         return (TRUE);
  220.     }
  221.  
  222.     return (FALSE);
  223. }
  224.  
  225. /*
  226.  *@@ GetCPData:
  227.  *      DOM callback for filling in codepage data
  228.  *      for unicode conversion.
  229.  *
  230.  *@@added V0.9.14 (2001-08-09) [umoeller]
  231.  */
  232.  
  233. int APIENTRY GetCPData(PXMLDOM pDom,
  234.                        ULONG ulCP,
  235.                        int *piMap)
  236. {
  237.     ULONG ul;
  238.  
  239.     ENCID id;
  240.  
  241.     XWPENCODINGMAP *pMap = NULL;
  242.     ULONG cEntries = 0;
  243.  
  244.     if (    (id == encFindIdForCodepage(ulCP,
  245.                                         NULL,
  246.                                         NULL))
  247.  
  248.          && (encGetTable(id,
  249.                          &pMap,
  250.                          &cEntries))
  251.        )
  252.     {
  253.         for (ul = 0;
  254.              ul < cEntries;
  255.              ul++)
  256.         {
  257.             USHORT usCP = pMap[ul].usCP;        // cp value
  258.             if (usCP < 256)
  259.                 piMap[usCP] = (ULONG)pMap[ul].usUni;     // unicode value
  260.         }
  261.  
  262.         // success
  263.         return (1);
  264.     }
  265.  
  266.     return (0);
  267. }
  268.  
  269. /*
  270.  *@@ ParseExternal:
  271.  *
  272.  *@@added V0.9.14 (2001-08-09) [umoeller]
  273.  */
  274.  
  275. APIRET APIENTRY ParseExternal(PXMLDOM pDom,
  276.                               XML_Parser pSubParser,
  277.                               const char *pcszSystemID,
  278.                               const char *pcszPublicID)
  279. {
  280.     APIRET arc = ERROR_FILE_NOT_FOUND;
  281.  
  282.     if (pcszSystemID)
  283.     {
  284.         PSZ pszContents = NULL;
  285.         if (!(arc = doshLoadTextFile(pcszSystemID,
  286.                                      &pszContents,
  287.                                      NULL)))
  288.         {
  289.             if (!XML_Parse(pSubParser,
  290.                            pszContents,
  291.                            strlen(pszContents),
  292.                            TRUE))
  293.                 arc = -1;
  294.  
  295.             free(pszContents);
  296.         }
  297.     }
  298.  
  299.     return (arc);
  300. }
  301.  
  302. /*
  303.  *@@ LoadXMLFile:
  304.  *      loads and parses an XML file into an XMLFILE
  305.  *      structure, which is created.
  306.  *
  307.  *      If this returns ERROR_DOM_PARSING or
  308.  *      ERROR_DOM_VALIDITY, ppszError
  309.  *      receives an error description, which the
  310.  *      caller must free.
  311.  *
  312.  *@@added V0.9.9 (2001-02-10) [umoeller]
  313.  */
  314.  
  315. APIRET LoadXMLFile(const char *pcszFilename,    // in: file:/K:\...
  316.                    PXMLFILE *ppXMLFile,         // out: loaded file
  317.                    PSZ *ppszError)     // out: error string
  318. {
  319.     APIRET  arc = NO_ERROR;
  320.  
  321.     PSZ     pszContents = NULL;
  322.  
  323.     arc = doshLoadTextFile(pcszFilename,
  324.                            &pszContents,
  325.                            NULL);
  326.  
  327.     if (arc == NO_ERROR)
  328.     {
  329.         PXMLFILE pFile = (PXMLFILE)malloc(sizeof(XMLFILE));
  330.         if (!pFile)
  331.             arc = ERROR_NOT_ENOUGH_MEMORY;
  332.         else
  333.         {
  334.             memset(pFile, 0, sizeof(*pFile));
  335.  
  336.             xstrInitSet(&pFile->strContents, pszContents);
  337.  
  338.             xstrConvertLineFormat(&pFile->strContents,
  339.                                   CRLF2LF);
  340.  
  341.             arc = xmlCreateDOM(DF_PARSEDTD | DF_DROP_WHITESPACE,
  342.                                NULL,  // V0.9.20 (2002-07-17) [pr]
  343.                                0,
  344.                                GetCPData,
  345.                                ParseExternal,
  346.                                NULL,
  347.                                &pFile->pDom);
  348.             if (arc == NO_ERROR)
  349.             {
  350.                 arc = xmlParse(pFile->pDom,
  351.                                pFile->strContents.psz,
  352.                                pFile->strContents.ulLength,
  353.                                TRUE);       // last
  354.  
  355.                 if (arc == ERROR_DOM_PARSING)
  356.                 {
  357.                     CHAR    sz[1000];
  358.                     sprintf(sz,
  359.                             "Parsing error: %s (line %d, column %d)",
  360.                             pFile->pDom->pcszErrorDescription,
  361.                             pFile->pDom->ulErrorLine,
  362.                             pFile->pDom->ulErrorColumn);
  363.  
  364.                     if (pFile->pDom->pxstrFailingNode)
  365.                         sprintf(sz + strlen(sz),
  366.                                 " (%s)",
  367.                                 pFile->pDom->pxstrFailingNode->psz);
  368.  
  369.                     *ppszError = strdup(sz);
  370.                 }
  371.                 else if (arc == ERROR_DOM_VALIDITY)
  372.                 {
  373.                     CHAR    sz[1000];
  374.                     sprintf(sz,
  375.                             "Validation error: %s (line %d, column %d)",
  376.                             pFile->pDom->pcszErrorDescription,
  377.                             pFile->pDom->ulErrorLine,
  378.                             pFile->pDom->ulErrorColumn);
  379.  
  380.                     if (pFile->pDom->pxstrFailingNode)
  381.                         sprintf(sz + strlen(sz),
  382.                                 " (%s)",
  383.                                 pFile->pDom->pxstrFailingNode->psz);
  384.  
  385.                     *ppszError = strdup(sz);
  386.                 }
  387.             }
  388.  
  389.             if (arc == NO_ERROR)
  390.                 *ppXMLFile = pFile;
  391.             else
  392.                 // error:
  393.                 free(pFile);
  394.         }
  395.     }
  396.  
  397.     return (arc);
  398. }
  399.  
  400. /*
  401.  *@@ FreeXMLFile:
  402.  *
  403.  */
  404.  
  405. VOID FreeXMLFile(PXMLFILE pFile)
  406. {
  407.     xmlFreeDOM(pFile->pDom);
  408. }
  409.  
  410. /* ******************************************************************
  411.  *
  412.  *   XML view
  413.  *
  414.  ********************************************************************/
  415.  
  416. /*
  417.  *@@ INSERTSTACK:
  418.  *
  419.  */
  420.  
  421. typedef struct _INSERTSTACK
  422. {
  423.     PNODERECORD     pParentRecord;
  424.     PXMLDOM         pDom;
  425. } INSERTSTACK, *PINSERTSTACK;
  426.  
  427. /*
  428.  *@@ AppendUTF8:
  429.  *
  430.  *@@added V0.9.14 (2001-08-09) [umoeller]
  431.  */
  432.  
  433. VOID AppendUTF8(PCONVERSION pCodec,
  434.                 PXSTRING pstrTarget,
  435.                 PXSTRING pstrUTF8)
  436. {
  437.     const char *p;
  438.     if (p = pstrUTF8->psz)
  439.     {
  440.         ULONG ulChar;
  441.         while (ulChar = encDecodeUTF8(&p))
  442.             xstrcatc(pstrTarget,
  443.                      (CHAR)encUni2Char(pCodec, ulChar));
  444.     }
  445. }
  446.  
  447. /*
  448.  *@@ InsertAttribDecls:
  449.  *
  450.  *@@added V0.9.9 (2001-02-16) [umoeller]
  451.  */
  452.  
  453. VOID XWPENTRY InsertAttribDecls(PCONVERSION pCodec,
  454.                                 TREE *t,        // in: PCMATTRIBUTEDECL really
  455.                                 PVOID pUser)    // in: PINSERTSTACK user param
  456. {
  457.     PCMATTRIBUTEDECL pAttribDecl = (PCMATTRIBUTEDECL)t;
  458.     PINSERTSTACK pInsertStack = (PINSERTSTACK)pUser;
  459.  
  460.     PNODERECORD prec = (PNODERECORD)cnrhAllocRecords(G_hwndCnr,
  461.                                                      sizeof(NODERECORD),
  462.                                                      1);
  463.  
  464.     XSTRING str;
  465.     xstrInitCopy(&str, "attrib: \"", 0);
  466.     xstrcats(&str, &pAttribDecl->NodeBase.strNodeName);
  467.     xstrcat(&str, "\", type: ", 0);
  468.     switch (pAttribDecl->ulAttrType)
  469.     {
  470.         case CMAT_ENUM:
  471.         {
  472.             xstrcat(&str, "ENUM(", 0);
  473.             PNODEBASE pNode = (PNODEBASE)treeFirst(pAttribDecl->ValuesTree);
  474.             while (pNode)
  475.             {
  476.                 AppendUTF8(pCodec, &str, &pNode->strNodeName);
  477.                 // xstrcats(&str, &pNode->strNodeName);
  478.                 xstrcatc(&str, '|');
  479.  
  480.                 pNode = (PNODEBASE)treeNext(&pNode->Tree);
  481.             }
  482.             /* treeTraverse(pAttribDecl->ValuesTree,
  483.                          CatAttribEnums,
  484.                          &str,
  485.                          0); */
  486.             *(str.psz + str.ulLength - 1) = ')';
  487.         break; }
  488.  
  489.         case CMAT_CDATA:
  490.             xstrcat(&str, "CDATA", 0);
  491.         break;
  492.         case CMAT_ID:
  493.             xstrcat(&str, "ID", 0);
  494.         break;
  495.         case CMAT_IDREF:
  496.             xstrcat(&str, "IDREF", 0);
  497.         break;
  498.         case CMAT_IDREFS:
  499.             xstrcat(&str, "IDREFS", 0);
  500.         break;
  501.         case CMAT_ENTITY:
  502.             xstrcat(&str, "ENTITY", 0);
  503.         break;
  504.         case CMAT_ENTITIES:
  505.             xstrcat(&str, "ENTITIES", 0);
  506.         break;
  507.         case CMAT_NMTOKEN:
  508.             xstrcat(&str, "NMTOKEN", 0);
  509.         break;
  510.         case CMAT_NMTOKENS:
  511.             xstrcat(&str, "NMTOKENS", 0);
  512.         break;
  513.     }
  514.  
  515.     cnrhInsertRecords(G_hwndCnr,
  516.                       (PRECORDCORE)pInsertStack->pParentRecord,
  517.                       (PRECORDCORE)prec,
  518.                       TRUE,
  519.                       str.psz,
  520.                       CRA_RECORDREADONLY,
  521.                       1);
  522. }
  523.  
  524. /*
  525.  *@@ InsertEDParticle:
  526.  *
  527.  *@@added V0.9.9 (2001-02-16) [umoeller]
  528.  */
  529.  
  530. PNODERECORD InsertEDParticle(PCMELEMENTPARTICLE pParticle,
  531.                              PNODERECORD pParentRecord)
  532. {
  533.     PNODERECORD prec = (PNODERECORD)cnrhAllocRecords(G_hwndCnr,
  534.                                                      sizeof(NODERECORD),
  535.                                                      1);
  536.  
  537.     XSTRING str;
  538.     xstrInitCopy(&str, "\"", 0);
  539.     xstrcats(&str, &pParticle->NodeBase.strNodeName);
  540.  
  541.     switch (pParticle->ulRepeater)
  542.     {
  543.         // XML_CQUANT_NONE
  544.  
  545.         case XML_CQUANT_OPT:
  546.             xstrcatc(&str, '?');
  547.         break;
  548.  
  549.         case XML_CQUANT_REP:
  550.             xstrcatc(&str, '*');
  551.         break;
  552.  
  553.         case XML_CQUANT_PLUS:
  554.             xstrcatc(&str, '+');
  555.         break;
  556.     }
  557.  
  558.     const char *pcsz = "unknown";
  559.     switch (pParticle->NodeBase.ulNodeType)
  560.     {
  561.         case ELEMENTPARTICLE_EMPTY:
  562.             pcsz = "\" EMPTY";
  563.         break;
  564.  
  565.         case ELEMENTPARTICLE_ANY:
  566.             pcsz = "\" ANY";
  567.         break;
  568.  
  569.         case ELEMENTPARTICLE_MIXED:
  570.             pcsz = "\" MIXED";
  571.         break;
  572.  
  573.         case ELEMENTPARTICLE_CHOICE:
  574.             pcsz = "\" CHOICE";
  575.         break;
  576.  
  577.         case ELEMENTPARTICLE_SEQ:
  578.             pcsz = "\" SEQ";
  579.         break;
  580.  
  581.         case ELEMENTPARTICLE_NAME:        // subtypes
  582.             pcsz = "\" NAME";
  583.         break;
  584.     }
  585.  
  586.     xstrcat(&str, pcsz, 0);
  587.  
  588.     cnrhInsertRecords(G_hwndCnr,
  589.                       (PRECORDCORE)pParentRecord,
  590.                       (PRECORDCORE)prec,
  591.                       TRUE,
  592.                       str.psz,
  593.                       CRA_RECORDREADONLY,
  594.                       1);
  595.  
  596.     if (pParticle->pllSubNodes)
  597.     {
  598.         PLISTNODE pNode = lstQueryFirstNode(pParticle->pllSubNodes);
  599.         while (pNode)
  600.         {
  601.             PCMELEMENTPARTICLE pSub = (PCMELEMENTPARTICLE)pNode->pItemData;
  602.             InsertEDParticle(pSub,
  603.                              prec);
  604.  
  605.             pNode = pNode->pNext;
  606.         }
  607.     }
  608.  
  609.     return (prec);
  610. }
  611.  
  612. /*
  613.  *@@ InsertElementDecls:
  614.  *      tree traversal function initiated from InsertDocType.
  615.  *
  616.  *@@added V0.9.9 (2001-02-16) [umoeller]
  617.  */
  618.  
  619. VOID XWPENTRY InsertElementDecls(TREE *t,           // in: an CMELEMENTDECLNODE really
  620.                                  PVOID pUser)       // in: PINSERTSTACK user param
  621. {
  622. }
  623.  
  624. /*
  625.  *@@ InsertDocType:
  626.  *
  627.  *@@added V0.9.9 (2001-02-16) [umoeller]
  628.  */
  629.  
  630. VOID InsertDocType(PCONVERSION pCodec,
  631.                    PXMLDOM pDom,
  632.                    PDOMDOCTYPENODE pDocTypeNode,
  633.                    PNODERECORD pParentRecord)
  634. {
  635.     // INSERTSTACK InsertStack = {pParentRecord, pDom};
  636.  
  637.     TREE *t = treeFirst(pDocTypeNode->ElementDeclsTree);
  638.     while (t)
  639.     {
  640.         PCMELEMENTDECLNODE pElementDecl = (PCMELEMENTDECLNODE)t;
  641.  
  642.         // insert the element declaration root particle and subparticles
  643.         // (this recurses)
  644.         PNODERECORD pElementRec = InsertEDParticle(&pElementDecl->Particle,
  645.                                                    pParentRecord);
  646.  
  647.         // now insert attribute decls, if any
  648.         PCMATTRIBUTEDECLBASE pAttribDeclBase
  649.             = xmlFindAttribDeclBase(pDom,
  650.                                     &pElementDecl->Particle.NodeBase.strNodeName);
  651.         if (pAttribDeclBase)
  652.         {
  653.             // traverse the attributes tree as well
  654.             INSERTSTACK Stack2 = {pElementRec, pDom};
  655.  
  656.             TREE *t = treeFirst(pAttribDeclBase->AttribDeclsTree);
  657.             while (t)
  658.             {
  659.                 InsertAttribDecls(pCodec,
  660.                                   t,
  661.                                   &Stack2);
  662.                 t = treeNext(t);
  663.             }
  664.             /* treeTraverse(pAttribDeclBase->AttribDeclsTree,
  665.                          InsertAttribDecls,
  666.                          &Stack2,       // user param
  667.                          0); */
  668.         }
  669.  
  670.         /* InsertAttribDecls(t,
  671.                           &InsertStack); */
  672.         t = treeNext(t);
  673.     }
  674.  
  675.     /* treeTraverse(pDocTypeNode->ElementDeclsTree,
  676.                  InsertElementDecls,
  677.                  &InsertStack,         // user param
  678.                  0); */
  679. }
  680.  
  681. VOID InsertDom(PCONVERSION pCodec,
  682.                PXMLDOM pDom,
  683.                PDOMNODE pDomNode,
  684.                PNODERECORD pParentRecord);
  685.  
  686. /*
  687.  *@@ InsertDom:
  688.  *
  689.  *      Note: We get the document node first, which we must
  690.  *      not insert, but only its children.
  691.  */
  692.  
  693. VOID InsertDom(PCONVERSION pCodec,
  694.                PXMLDOM pDom,
  695.                PDOMNODE pDomNode,
  696.                PNODERECORD pParentRecord)       // initially NULL
  697. {
  698.     BOOL        fInsertChildren = FALSE;
  699.     PNODERECORD prec = NULL;
  700.     PDOMDOCTYPENODE pDocType = NULL;
  701.  
  702.     XSTRING     str;
  703.     xstrInit(&str, 100);
  704.  
  705.     switch (pDomNode->NodeBase.ulNodeType)
  706.     {
  707.         case DOMNODE_ELEMENT:
  708.             // xstrcpys(&str, &pDomNode->NodeBase.strNodeName);
  709.             AppendUTF8(pCodec, &str, &pDomNode->NodeBase.strNodeName);
  710.         break;
  711.  
  712.         case DOMNODE_ATTRIBUTE:
  713.             AppendUTF8(pCodec, &str, &pDomNode->NodeBase.strNodeName);
  714.             // xstrcpys(&str, &pDomNode->NodeBase.strNodeName);
  715.             xstrcat(&str, "=\"", 2);
  716.             // xstrcats(&str, pDomNode->pstrNodeValue);
  717.             AppendUTF8(pCodec, &str, pDomNode->pstrNodeValue);
  718.             xstrcatc(&str, '\"');
  719.         break;
  720.  
  721.         case DOMNODE_TEXT:
  722.             xstrcpy(&str, "\"", 1);
  723.             // xstrcats(&str, pDomNode->pstrNodeValue);
  724.             AppendUTF8(pCodec, &str, pDomNode->pstrNodeValue);
  725.             xstrcatc(&str, '\"');
  726.         break;
  727.  
  728.         case DOMNODE_DOCUMENT:
  729.             xstrcpy(&str, "Document", 0);
  730.             xstrcat(&str, " \"", 0);
  731.             // xstrcats(&str, &pDomNode->NodeBase.strNodeName);
  732.             AppendUTF8(pCodec, &str, &pDomNode->NodeBase.strNodeName);
  733.             xstrcat(&str, "\"", 0);
  734.         break;
  735.  
  736.         case DOMNODE_DOCUMENT_TYPE:
  737.         {
  738.             pDocType = (PDOMDOCTYPENODE)pDomNode;
  739.             xstrcpy(&str, "DOCTYPE system: \"", 0);
  740.             // xstrcats(&str, &pDocType->strSystemID);
  741.             AppendUTF8(pCodec, &str, &pDocType->strSystemID);
  742.             xstrcat(&str, "\", public: \"", 0);
  743.             // xstrcats(&str, &pDocType->strPublicID);
  744.             AppendUTF8(pCodec, &str, &pDocType->strPublicID);
  745.             xstrcatc(&str, '\"');
  746.         break; }
  747.  
  748.         default:
  749.         {
  750.             CHAR sz[1000];
  751.             sprintf(sz, "Unknown node type %d", pDomNode->NodeBase.ulNodeType);
  752.             xstrcpy(&str, sz, 0);
  753.  
  754.         break; }
  755.     }
  756.  
  757.     if (str.ulLength)
  758.     {
  759.         prec = (PNODERECORD)cnrhAllocRecords(G_hwndCnr,
  760.                                              sizeof(NODERECORD),
  761.                                              1);
  762.  
  763.         cnrhInsertRecords(G_hwndCnr,
  764.                           (PRECORDCORE)pParentRecord,
  765.                           (PRECORDCORE)prec,
  766.                           TRUE,
  767.                           str.psz,
  768.                           CRA_RECORDREADONLY,
  769.                           1);
  770.         fInsertChildren = TRUE;
  771.     }
  772.  
  773.     if (fInsertChildren)
  774.     {
  775.         // insert attributes
  776.         INSERTSTACK Stack2 = {prec, pDom};
  777.         PDOMNODE pAttrNode = (PDOMNODE)treeFirst(pDomNode->AttributesMap);
  778.         while (pAttrNode)
  779.         {
  780.             InsertDom(pCodec,
  781.                       pDom,
  782.                       pAttrNode,
  783.                       prec);
  784.             pAttrNode = (PDOMNODE)treeNext((TREE*)pAttrNode);
  785.         }
  786.  
  787.         // and children
  788.         PLISTNODE pSubNode = lstQueryFirstNode(&pDomNode->llChildren);
  789.         while (pSubNode)
  790.         {
  791.             PDOMNODE pDomSubnode = (PDOMNODE)pSubNode->pItemData;
  792.             InsertDom(pCodec,
  793.                       pDom,
  794.                       pDomSubnode,
  795.                       prec);
  796.  
  797.             pSubNode = pSubNode->pNext;
  798.         }
  799.     }
  800.     else
  801.         xstrClear(&str);
  802.  
  803.     if (prec && pDocType)
  804.         InsertDocType(pCodec, pDom, pDocType, prec);
  805. }
  806.  
  807. /* ******************************************************************
  808.  *
  809.  *   Container window proc
  810.  *
  811.  ********************************************************************/
  812.  
  813. /*
  814.  *@@ LoadAndInsert:
  815.  *
  816.  */
  817.  
  818. VOID LoadAndInsert(const char *pcszFile)
  819. {
  820.     PSZ pszError;
  821.     APIRET  arc = LoadXMLFile(pcszFile,
  822.                               &G_pLoadedFile,
  823.                               &pszError);
  824.     if (    (arc == ERROR_DOM_PARSING)
  825.          || (arc == ERROR_DOM_VALIDITY)
  826.        )
  827.     {
  828.         winhSetVarWindowText(G_hwndStatusBar,
  829.                              pszError);
  830.         free(pszError);
  831.     }
  832.     else if (arc != NO_ERROR)
  833.     {
  834.         winhSetVarWindowText(G_hwndStatusBar,
  835.                              "Error %d loading \"%s\".",
  836.                              arc,
  837.                              pcszFile);
  838.     }
  839.     else
  840.     {
  841.         winhSetVarWindowText(G_hwndStatusBar,
  842.                           "File \"%s\" loaded, %d bytes",
  843.                           pcszFile,
  844.                           G_pLoadedFile->strContents.ulLength);
  845.  
  846.         cnrhRemoveAll(G_hwndCnr);
  847.  
  848.         // create codec for process codepage
  849.         ULONG acp[8];       // fixed V0.9.19 (2002-04-14) [umoeller], this needs an array
  850.         ULONG cb = 0;
  851.         APIRET arcCP;
  852.         ENCID id;
  853.         PCONVERSION pCodec;
  854.         if (    (!(arcCP = DosQueryCp(sizeof(acp),
  855.                                       acp,
  856.                                       &cb)))
  857.              && (id = encFindIdForCodepage(acp[0], NULL, NULL))
  858.              && (pCodec = encCreateCodec(id))
  859.            )
  860.         {
  861.             InsertDom(pCodec,
  862.                       G_pLoadedFile->pDom,
  863.                       (PDOMNODE)G_pLoadedFile->pDom->pDocumentNode,
  864.                       NULL);
  865.             encFreeCodec(&pCodec);
  866.         }
  867.     }
  868. }
  869.  
  870. /*
  871.  *@@ fnwpSubclassedCnr:
  872.  *
  873.  */
  874.  
  875. MRESULT EXPENTRY fnwpSubclassedCnr(HWND hwndCnr, ULONG msg, MPARAM mp1, MPARAM mp2)
  876. {
  877.     MRESULT mrc = 0;
  878.  
  879.     switch (msg)
  880.     {
  881.         case WM_CLOSE:
  882.             winhSaveWindowPos(G_hwndMain,
  883.                               HINI_USER,
  884.                               INIAPP,
  885.                               INIKEY_MAINWINPOS);
  886.             mrc = G_pfnwpCnrOrig(hwndCnr, msg, mp1, mp2);
  887.         break;
  888.  
  889.         case WM_COMMAND:
  890.             switch (SHORT1FROMMP(mp1))
  891.             {
  892.                 case IDMI_OPEN:
  893.                 {
  894.                     CHAR    szFile[CCHMAXPATH] = "*.xml";
  895.                     if (SuperFileDlg(G_hwndMain,
  896.                                      szFile,
  897.                                      WINH_FOD_INILOADDIR | WINH_FOD_INISAVEDIR,
  898.                                      HINI_USER,
  899.                                      INIAPP,
  900.                                      INIKEY_OPENDLG))
  901.                         LoadAndInsert(szFile);
  902.                 break; }
  903.  
  904.                 case IDMI_EXIT:
  905.                     WinPostMsg(G_hwndMain,
  906.                                WM_SYSCOMMAND,
  907.                                (MPARAM)SC_CLOSE,
  908.                                0);
  909.                 break;
  910.             }
  911.         break;
  912.  
  913.         default:
  914.             mrc = G_pfnwpCnrOrig(hwndCnr, msg, mp1, mp2);
  915.     }
  916.  
  917.     return (mrc);
  918. }
  919.  
  920. /*
  921.  *@@ SetupCnr:
  922.  *
  923.  */
  924.  
  925. VOID SetupCnr(HWND hwndCnr)
  926. {
  927.     BEGIN_CNRINFO()
  928.     {
  929.         // switch view
  930.         cnrhSetView(CV_TREE | CA_TREELINE | CV_TEXT);
  931.         cnrhSetTreeIndent(30);
  932.     } END_CNRINFO(hwndCnr)
  933.  
  934.     winhSetWindowFont(hwndCnr, NULL);
  935. }
  936.  
  937. /* ******************************************************************
  938.  *
  939.  *   Frame window proc
  940.  *
  941.  ********************************************************************/
  942.  
  943. /*
  944.  *@@ winhCreateStatusBar:
  945.  *
  946.  *@@added V0.9.5 (2000-08-13) [umoeller]
  947.  */
  948.  
  949. HWND winhCreateStatusBar(HWND hwndFrame,
  950.                          HWND hwndOwner,
  951.                          USHORT usID,
  952.                          PSZ pszFont,
  953.                          LONG lColor)
  954. {
  955.     // create status bar
  956.     HWND        hwndReturn = NULLHANDLE;
  957.     PPRESPARAMS ppp = NULL;
  958.     winhStorePresParam(&ppp, PP_FONTNAMESIZE, strlen(pszFont)+1, pszFont);
  959.     lColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0);
  960.     winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
  961.     lColor = CLR_BLACK;
  962.     winhStorePresParam(&ppp, PP_FOREGROUNDCOLOR, sizeof(lColor), &lColor);
  963.     hwndReturn = WinCreateWindow(G_hwndMain,
  964.                                   WC_STATIC,
  965.                                   "Welcome to xmlview, built " __DATE__,
  966.                                   SS_TEXT | DT_VCENTER | WS_VISIBLE,
  967.                                   0, 0, 0, 0,
  968.                                   hwndOwner,
  969.                                   HWND_TOP,
  970.                                   usID,
  971.                                   NULL,
  972.                                   ppp);
  973.     free(ppp);
  974.     return (hwndReturn);
  975. }
  976.  
  977. static PRECORDCORE s_preccExpanded = NULL;
  978.  
  979. /*
  980.  *@@ winh_fnwpFrameWithStatusBar:
  981.  *      subclassed frame window proc.
  982.  *
  983.  *@@added V0.9.5 (2000-08-13) [umoeller]
  984.  */
  985.  
  986. MRESULT EXPENTRY winh_fnwpFrameWithStatusBar(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
  987. {
  988.     MRESULT mrc = 0;
  989.  
  990.     switch (msg)
  991.     {
  992.         case WM_QUERYFRAMECTLCOUNT:
  993.         {
  994.             // query the standard frame controls count
  995.             ULONG ulrc = (ULONG)(G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2));
  996.  
  997.             // if we have a status bar, increment the count
  998.             ulrc++;
  999.  
  1000.             mrc = (MPARAM)ulrc;
  1001.         break; }
  1002.  
  1003.         case WM_FORMATFRAME:
  1004.         {
  1005.             //  query the number of standard frame controls
  1006.             ULONG ulCount = (ULONG)(G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2));
  1007.  
  1008.             // we have a status bar:
  1009.             // format the frame
  1010.             ULONG       ul;
  1011.             PSWP        swpArr = (PSWP)mp1;
  1012.  
  1013.             for (ul = 0; ul < ulCount; ul++)
  1014.             {
  1015.                 if (WinQueryWindowUShort( swpArr[ul].hwnd, QWS_ID ) == 0x8008 )
  1016.                                                                  // FID_CLIENT
  1017.                 {
  1018.                     POINTL      ptlBorderSizes;
  1019.                     ULONG       ulStatusBarHeight = 20;
  1020.                     WinSendMsg(hwndFrame,
  1021.                                WM_QUERYBORDERSIZE,
  1022.                                (MPARAM)&ptlBorderSizes,
  1023.                                0);
  1024.  
  1025.                     // first initialize the _new_ SWP for the status bar.
  1026.                     // Since the SWP array for the std frame controls is
  1027.                     // zero-based, and the standard frame controls occupy
  1028.                     // indices 0 thru ulCount-1 (where ulCount is the total
  1029.                     // count), we use ulCount for our static text control.
  1030.                     swpArr[ulCount].fl = SWP_MOVE | SWP_SIZE | SWP_NOADJUST | SWP_ZORDER;
  1031.                     swpArr[ulCount].x  = ptlBorderSizes.x;
  1032.                     swpArr[ulCount].y  = ptlBorderSizes.y;
  1033.                     swpArr[ulCount].cx = swpArr[ul].cx;  // same as cnr's width
  1034.                     swpArr[ulCount].cy = ulStatusBarHeight;
  1035.                     swpArr[ulCount].hwndInsertBehind = HWND_BOTTOM; // HWND_TOP;
  1036.                     swpArr[ulCount].hwnd = G_hwndStatusBar;
  1037.  
  1038.                     // adjust the origin and height of the container to
  1039.                     // accomodate our static text control
  1040.                     swpArr[ul].y  += swpArr[ulCount].cy;
  1041.                     swpArr[ul].cy -= swpArr[ulCount].cy;
  1042.                 }
  1043.             }
  1044.  
  1045.             // increment the number of frame controls
  1046.             // to include our status bar
  1047.             mrc = (MRESULT)(ulCount + 1);
  1048.         break; }
  1049.  
  1050.         case WM_CALCFRAMERECT:
  1051.         {
  1052.             mrc = G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2);
  1053.  
  1054.             // we have a status bar: calculate its rectangle
  1055.             // CalcFrameRect(mp1, mp2);
  1056.         break; }
  1057.  
  1058.         case WM_CONTROL:
  1059.             if (SHORT1FROMMP(mp1) == FID_CLIENT)
  1060.             {
  1061.                 switch (SHORT2FROMMP(mp1))
  1062.                 {
  1063.                     case CN_EXPANDTREE:
  1064.                         s_preccExpanded = (PRECORDCORE)mp2;
  1065.                         WinStartTimer(WinQueryAnchorBlock(hwndFrame),
  1066.                                       hwndFrame,
  1067.                                       1,
  1068.                                       100);
  1069.                     break;
  1070.                 }
  1071.             }
  1072.         break;
  1073.  
  1074.         case WM_TIMER:
  1075.             // stop timer (it's just for one shot)
  1076.             WinStopTimer(WinQueryAnchorBlock(hwndFrame),
  1077.                          hwndFrame,
  1078.                          (ULONG)mp1);       // timer ID
  1079.  
  1080.             switch ((ULONG)mp1)        // timer ID
  1081.             {
  1082.                 case 1:
  1083.                 {
  1084.                     if (s_preccExpanded->flRecordAttr & CRA_EXPANDED)
  1085.                     {
  1086.                         HWND hwndCnr = WinWindowFromID(hwndFrame, FID_CLIENT);
  1087.                         PRECORDCORE     preccLastChild;
  1088.                         // scroll the tree view properly
  1089.                         preccLastChild = (PRECORDCORE)WinSendMsg(hwndCnr,
  1090.                                                     CM_QUERYRECORD,
  1091.                                                     s_preccExpanded,
  1092.                                                             // expanded PRECORDCORE from CN_EXPANDTREE
  1093.                                                     MPFROM2SHORT(CMA_LASTCHILD,
  1094.                                                                  CMA_ITEMORDER));
  1095.                         if (preccLastChild)
  1096.                         {
  1097.                             // ULONG ulrc;
  1098.                             cnrhScrollToRecord(hwndCnr,
  1099.                                     (PRECORDCORE)preccLastChild,
  1100.                                     CMA_TEXT,   // record text rectangle only
  1101.                                     TRUE);      // keep parent visible
  1102.                         }
  1103.                     }
  1104.                 break; }
  1105.  
  1106.             }
  1107.         break;
  1108.  
  1109.         default:
  1110.             mrc = G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2);
  1111.     }
  1112.  
  1113.     return (mrc);
  1114. }
  1115.  
  1116. /* ******************************************************************
  1117.  *
  1118.  *   main
  1119.  *
  1120.  ********************************************************************/
  1121.  
  1122. #define ARRAYITEMCOUNT(array) sizeof(array) / sizeof(array[0])
  1123.  
  1124. /* typedef struct _CONTROLDEF
  1125. {
  1126.     const char  *pcszClass;         // registered PM window class
  1127.     const char  *pcszText;          // window text (class-specific);
  1128.                                     // NULL for tables
  1129.  
  1130.     ULONG       flStyle;
  1131.  
  1132.     ULONG       ulID;
  1133.  
  1134.     USHORT      usAdjustPosition;
  1135.             // flags for winhAdjustControls; any combination of
  1136.             // XAC_MOVEX, XAC_MOVEY, XAC_SIZEX, XAC_SIZEY
  1137.  
  1138.     SIZEL       szlControl;         // proposed size
  1139.     ULONG       ulSpacing;          // spacing around control
  1140.  
  1141. } CONTROLDEF, *PCONTROLDEF; */
  1142.  
  1143. static CONTROLDEF
  1144.             Static1 =
  1145.                     {
  1146.                         WC_STATIC,
  1147.                         "Test text row 1 column 1",
  1148.                         WS_VISIBLE | SS_TEXT | DT_LEFT | DT_TOP,
  1149.                         0,
  1150.                         CTL_COMMON_FONT,
  1151.                         0,
  1152.                         { 300, 30 },
  1153.                         5
  1154.                     },
  1155.             Static2 =
  1156.                     {
  1157.                         WC_STATIC,
  1158.                         "Test text row 1 column 2",
  1159.                         WS_VISIBLE | SS_TEXT | DT_LEFT | DT_TOP,
  1160.                         0,
  1161.                         CTL_COMMON_FONT,
  1162.                         0,
  1163.                         { 300, 30 },
  1164.                         5
  1165.                     },
  1166.             Cnr =
  1167.                     {
  1168.                         WC_CONTAINER,
  1169.                         NULL,
  1170.                         WS_VISIBLE | WS_TABSTOP,
  1171.                         0,
  1172.                         CTL_COMMON_FONT,
  1173.                         0,
  1174.                         { 100, 100 },
  1175.                         5
  1176.                     },
  1177.             CnrGroup =
  1178.                     {
  1179.                         WC_STATIC,
  1180.                         "Container1",
  1181.                         WS_VISIBLE | SS_GROUPBOX | DT_MNEMONIC,
  1182.                         0,
  1183.                         CTL_COMMON_FONT,
  1184.                         0,
  1185.                         { 100, 100 },       // ignored
  1186.                         5
  1187.                     },
  1188.             OKButton =
  1189.                     {
  1190.                         WC_BUTTON,
  1191.                         "~OK",
  1192.                         WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_DEFAULT,
  1193.                         0,
  1194.                         CTL_COMMON_FONT,
  1195.                         0,
  1196.                         { SZL_AUTOSIZE, 30 },
  1197.                         5
  1198.                     },
  1199.             CancelButton =
  1200.                     {
  1201.                         WC_BUTTON,
  1202.                         "~Cancel",
  1203.                         WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
  1204.                         0,
  1205.                         CTL_COMMON_FONT,
  1206.                         0,
  1207.                         { SZL_AUTOSIZE, 30 },
  1208.                         5
  1209.                     };
  1210.  
  1211. static _DLGHITEM aDlgItems[] =
  1212.                 {
  1213.                     START_TABLE,
  1214.                         START_ROW(0),
  1215.                             CONTROL_DEF(&Static1),
  1216.                         START_ROW(0),
  1217.                             START_GROUP_TABLE(&CnrGroup),
  1218.                                 START_ROW(0),
  1219.                                     CONTROL_DEF(&Cnr),
  1220.                             END_TABLE,
  1221.                             START_GROUP_TABLE(&CnrGroup),
  1222.                                 START_ROW(0),
  1223.                                     CONTROL_DEF(&Cnr),
  1224.                                 START_ROW(0),
  1225.                                     CONTROL_DEF(&OKButton),
  1226.                                 START_ROW(0),
  1227.                                     CONTROL_DEF(&CancelButton),
  1228.                             END_TABLE,
  1229.                         START_ROW(0),
  1230.                             CONTROL_DEF(&Static2),
  1231.  
  1232.                         START_ROW(0),
  1233.                             CONTROL_DEF(&OKButton),
  1234.                             CONTROL_DEF(&CancelButton),
  1235.                     END_TABLE
  1236.                 };
  1237.  
  1238. /*
  1239.  *@@ main:
  1240.  *
  1241.  */
  1242.  
  1243. int main(int argc, char* argv[])
  1244. {
  1245.     HAB         hab;
  1246.     HMQ         hmq;
  1247.     QMSG        qmsg;
  1248.  
  1249.     if (!(hab = WinInitialize(0)))
  1250.         return FALSE;
  1251.  
  1252.     if (!(hmq = WinCreateMsgQueue(hab, 0)))
  1253.         return FALSE;
  1254.  
  1255.     DosError(FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
  1256.  
  1257.     /* HWND hwndDlg = NULLHANDLE;
  1258.     APIRET arc = dlghCreateDlg(&hwndDlg,
  1259.                                NULLHANDLE,      // owner
  1260.                                FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
  1261.                                WinDefDlgProc,
  1262.                                "DemoDlg",
  1263.                                aDlgItems,
  1264.                                ARRAYITEMCOUNT(aDlgItems),
  1265.                                NULL,
  1266.                                "9.WarpSans");
  1267.     if (arc == NO_ERROR)
  1268.     {
  1269.         WinProcessDlg(hwndDlg);
  1270.         WinDestroyWindow(hwndDlg);
  1271.     }
  1272.     else
  1273.     {
  1274.         CHAR szErr[100];
  1275.         sprintf(szErr, "Error %d", arc);
  1276.         winhDebugBox(NULLHANDLE, "Error", szErr);
  1277.     } */
  1278.  
  1279.     // create frame and container
  1280.     G_hwndMain = winhCreateStdWindow(HWND_DESKTOP,
  1281.                                      0,
  1282.                                      FCF_TITLEBAR
  1283.                                         | FCF_SYSMENU
  1284.                                         | FCF_MINMAX
  1285.                                         | FCF_SIZEBORDER
  1286.                                         | FCF_ICON
  1287.                                         | FCF_MENU
  1288.                                         | FCF_TASKLIST,
  1289.                                      0,
  1290.                                      APPTITLE,      // xmlview
  1291.                                      1,     // icon resource
  1292.                                      WC_CONTAINER,
  1293.                                      CCS_MINIICONS | CCS_READONLY | CCS_EXTENDSEL
  1294.                                         | WS_VISIBLE,
  1295.                                      0,
  1296.                                      NULL,
  1297.                                      &G_hwndCnr);
  1298.  
  1299.     if ((G_hwndMain) && (G_hwndCnr))
  1300.     {
  1301.         // subclass cnr (it's our client)
  1302.         G_pfnwpCnrOrig = WinSubclassWindow(G_hwndCnr, fnwpSubclassedCnr);
  1303.  
  1304.         // create status bar as child of the frame
  1305.         G_hwndStatusBar = winhCreateStatusBar(G_hwndMain,
  1306.                                               G_hwndCnr,
  1307.                                               0,
  1308.                                               "9.WarpSans",
  1309.                                               CLR_BLACK);
  1310.         // subclass frame for supporting status bar and msgs
  1311.         G_fnwpFrameOrig = WinSubclassWindow(G_hwndMain,
  1312.                                             winh_fnwpFrameWithStatusBar);
  1313.  
  1314.         SetupCnr(G_hwndCnr);
  1315.  
  1316.         if (!winhRestoreWindowPos(G_hwndMain,
  1317.                                   HINI_USER,
  1318.                                   INIAPP,
  1319.                                   INIKEY_MAINWINPOS,
  1320.                                   SWP_SHOW | SWP_ACTIVATE | SWP_MOVE | SWP_SIZE))
  1321.             WinSetWindowPos(G_hwndMain,
  1322.                             HWND_TOP,
  1323.                             10, 10, 500, 500,
  1324.                             SWP_SHOW | SWP_ACTIVATE | SWP_MOVE | SWP_SIZE);
  1325.  
  1326.         if (argc > 1)
  1327.             LoadAndInsert(argv[1]);
  1328.  
  1329.         //  standard PM message loop
  1330.         while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
  1331.         {
  1332.             WinDispatchMsg(hab, &qmsg);
  1333.         }
  1334.     }
  1335.  
  1336.     // clean up on the way out
  1337.     WinDestroyMsgQueue(hmq);
  1338.     WinTerminate(hab);
  1339.  
  1340.     return (0);
  1341. }
  1342.  
  1343.