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 >
Wrap
C/C++ Source or Header
|
2002-07-21
|
41KB
|
1,343 lines
/*
*@@sourcefile xmlview.cpp:
* XML viewer, which displays the elements,
* attributes, and DTD of any XML document
* in a PM container tree view.
*
*@@header "xmlview.h"
*@@added V0.9.5 (2000-08-13) [umoeller]
*/
/*
* Copyright (C) 2000 Ulrich Möller.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, in version 2 as it comes in the COPYING
* file of the XFolder main distribution.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define OS2EMX_PLAIN_CHAR
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_WIN
#include <os2.h>
// C library headers
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <setjmp.h>
// generic headers
#include "setup.h"
#include "xwpapi.h"
#include "expat\expat.h"
#include "helpers\cnrh.h"
#include "helpers\datetime.h" // date/time helper routines
#include "helpers\dialog.h"
#include "helpers\dosh.h"
#include "helpers\except.h"
#include "helpers\linklist.h"
#include "helpers\standards.h"
#include "helpers\stringh.h"
#include "helpers\threads.h"
#include "helpers\tree.h"
#include "helpers\winh.h"
#include "helpers\xstring.h"
#include "helpers\xml.h"
#include "encodings\base.h"
#include "xmlview.h"
#pragma hdrstop
/* ******************************************************************
*
* Global variables
*
********************************************************************/
HWND G_hwndMain = NULLHANDLE;
HWND G_hwndStatusBar = NULLHANDLE;
HWND G_hwndCnr = NULLHANDLE;
PFNWP G_pfnwpCnrOrig = NULL,
G_fnwpFrameOrig = NULL;
const char *INIAPP = "XWorkplace:XMLView";
const char *INIKEY_MAINWINPOS = "HandleFixWinPos";
const char *INIKEY_OPENDLG = "FileOpenDlgPath";
const char *APPTITLE = "xmlview";
/*
*@@ NODERECORD:
*
*/
typedef struct _NODERECORD
{
RECORDCORE recc;
} NODERECORD, *PNODERECORD;
/*
*@@ XMLFILE:
*
*@@added V0.9.9 (2001-02-10) [umoeller]
*/
typedef struct _XMLFILE
{
XSTRING strContents;
// contents of the XML document
PXMLDOM pDom;
} XMLFILE, *PXMLFILE;
PXMLFILE G_pLoadedFile = NULL;
/* ******************************************************************
*
* XML load
*
********************************************************************/
/*
*@@ winhSetWindowText:
* sets a window's text to a variable text. This is
* just like WinSetWindowText, but supports printf()
* formatting.
*
* This is limited to 2000 characters.
*
*@@added V0.9.9 (2001-02-10) [umoeller]
*/
VOID winhSetVarWindowText(HWND hwnd,
const char *pcszFormat, // in: format string (as with printf)
...) // in: additional stuff (as with printf)
{
va_list args;
CHAR sz[2000];
va_start(args, pcszFormat);
vsprintf(sz, pcszFormat, args);
va_end(args);
WinSetWindowText(hwnd, sz);
}
/*
*@@ SuperFileDlg:
*
*/
BOOL SuperFileDlg(HWND hwndOwner, // in: owner for file dlg
PSZ pszFile, // in: file mask; out: fully q'd filename
// (should be CCHMAXPATH in size)
ULONG flFlags, // in: any combination of the following:
// -- WINH_FOD_SAVEDLG: save dlg; else open dlg
// -- WINH_FOD_INILOADDIR: load FOD path from INI
// -- WINH_FOD_INISAVEDIR: store FOD path to INI on OK
HINI hini, // in: INI file to load/store last path from (can be HINI_USER)
const char *pcszApplication, // in: INI application to load/store last path from
const char *pcszKey) // in: INI key to load/store last path from
{
FILEDLG fd;
memset(&fd, 0, sizeof(FILEDLG));
fd.cbSize = sizeof(FILEDLG);
fd.fl = FDS_CENTER;
if (flFlags & WINH_FOD_SAVEDLG)
fd.fl |= FDS_SAVEAS_DIALOG;
else
fd.fl |= FDS_OPEN_DIALOG;
// default: copy pszFile
strcpy(fd.szFullFile, pszFile);
if ( (hini) && (flFlags & WINH_FOD_INILOADDIR) )
{
// overwrite with initial directory for FOD from OS2.INI
if (PrfQueryProfileString(hini,
pcszApplication,
pcszKey,
"", // default string V0.9.9 (2001-02-10) [umoeller]
fd.szFullFile,
sizeof(fd.szFullFile)-10)
>= 2)
{
// found: append "\*"
strcat(fd.szFullFile, "\\");
strcat(fd.szFullFile, pszFile);
}
}
if ( WinFileDlg(HWND_DESKTOP, // parent
hwndOwner, // owner
&fd)
&& (fd.lReturn == DID_OK)
)
{
// save path back?
if ( (hini)
&& (flFlags & WINH_FOD_INISAVEDIR)
)
{
// get the directory that was used
PSZ p = strrchr(fd.szFullFile, '\\');
if (p)
{
// contains directory:
// copy to OS2.INI
PSZ pszDir = strhSubstr(fd.szFullFile, p);
if (pszDir)
{
PrfWriteProfileString(hini,
pcszApplication,
pcszKey,
pszDir);
free(pszDir);
}
}
}
strcpy(pszFile, fd.szFullFile);
return (TRUE);
}
return (FALSE);
}
/*
*@@ GetCPData:
* DOM callback for filling in codepage data
* for unicode conversion.
*
*@@added V0.9.14 (2001-08-09) [umoeller]
*/
int APIENTRY GetCPData(PXMLDOM pDom,
ULONG ulCP,
int *piMap)
{
ULONG ul;
ENCID id;
XWPENCODINGMAP *pMap = NULL;
ULONG cEntries = 0;
if ( (id == encFindIdForCodepage(ulCP,
NULL,
NULL))
&& (encGetTable(id,
&pMap,
&cEntries))
)
{
for (ul = 0;
ul < cEntries;
ul++)
{
USHORT usCP = pMap[ul].usCP; // cp value
if (usCP < 256)
piMap[usCP] = (ULONG)pMap[ul].usUni; // unicode value
}
// success
return (1);
}
return (0);
}
/*
*@@ ParseExternal:
*
*@@added V0.9.14 (2001-08-09) [umoeller]
*/
APIRET APIENTRY ParseExternal(PXMLDOM pDom,
XML_Parser pSubParser,
const char *pcszSystemID,
const char *pcszPublicID)
{
APIRET arc = ERROR_FILE_NOT_FOUND;
if (pcszSystemID)
{
PSZ pszContents = NULL;
if (!(arc = doshLoadTextFile(pcszSystemID,
&pszContents,
NULL)))
{
if (!XML_Parse(pSubParser,
pszContents,
strlen(pszContents),
TRUE))
arc = -1;
free(pszContents);
}
}
return (arc);
}
/*
*@@ LoadXMLFile:
* loads and parses an XML file into an XMLFILE
* structure, which is created.
*
* If this returns ERROR_DOM_PARSING or
* ERROR_DOM_VALIDITY, ppszError
* receives an error description, which the
* caller must free.
*
*@@added V0.9.9 (2001-02-10) [umoeller]
*/
APIRET LoadXMLFile(const char *pcszFilename, // in: file:/K:\...
PXMLFILE *ppXMLFile, // out: loaded file
PSZ *ppszError) // out: error string
{
APIRET arc = NO_ERROR;
PSZ pszContents = NULL;
arc = doshLoadTextFile(pcszFilename,
&pszContents,
NULL);
if (arc == NO_ERROR)
{
PXMLFILE pFile = (PXMLFILE)malloc(sizeof(XMLFILE));
if (!pFile)
arc = ERROR_NOT_ENOUGH_MEMORY;
else
{
memset(pFile, 0, sizeof(*pFile));
xstrInitSet(&pFile->strContents, pszContents);
xstrConvertLineFormat(&pFile->strContents,
CRLF2LF);
arc = xmlCreateDOM(DF_PARSEDTD | DF_DROP_WHITESPACE,
NULL, // V0.9.20 (2002-07-17) [pr]
0,
GetCPData,
ParseExternal,
NULL,
&pFile->pDom);
if (arc == NO_ERROR)
{
arc = xmlParse(pFile->pDom,
pFile->strContents.psz,
pFile->strContents.ulLength,
TRUE); // last
if (arc == ERROR_DOM_PARSING)
{
CHAR sz[1000];
sprintf(sz,
"Parsing error: %s (line %d, column %d)",
pFile->pDom->pcszErrorDescription,
pFile->pDom->ulErrorLine,
pFile->pDom->ulErrorColumn);
if (pFile->pDom->pxstrFailingNode)
sprintf(sz + strlen(sz),
" (%s)",
pFile->pDom->pxstrFailingNode->psz);
*ppszError = strdup(sz);
}
else if (arc == ERROR_DOM_VALIDITY)
{
CHAR sz[1000];
sprintf(sz,
"Validation error: %s (line %d, column %d)",
pFile->pDom->pcszErrorDescription,
pFile->pDom->ulErrorLine,
pFile->pDom->ulErrorColumn);
if (pFile->pDom->pxstrFailingNode)
sprintf(sz + strlen(sz),
" (%s)",
pFile->pDom->pxstrFailingNode->psz);
*ppszError = strdup(sz);
}
}
if (arc == NO_ERROR)
*ppXMLFile = pFile;
else
// error:
free(pFile);
}
}
return (arc);
}
/*
*@@ FreeXMLFile:
*
*/
VOID FreeXMLFile(PXMLFILE pFile)
{
xmlFreeDOM(pFile->pDom);
}
/* ******************************************************************
*
* XML view
*
********************************************************************/
/*
*@@ INSERTSTACK:
*
*/
typedef struct _INSERTSTACK
{
PNODERECORD pParentRecord;
PXMLDOM pDom;
} INSERTSTACK, *PINSERTSTACK;
/*
*@@ AppendUTF8:
*
*@@added V0.9.14 (2001-08-09) [umoeller]
*/
VOID AppendUTF8(PCONVERSION pCodec,
PXSTRING pstrTarget,
PXSTRING pstrUTF8)
{
const char *p;
if (p = pstrUTF8->psz)
{
ULONG ulChar;
while (ulChar = encDecodeUTF8(&p))
xstrcatc(pstrTarget,
(CHAR)encUni2Char(pCodec, ulChar));
}
}
/*
*@@ InsertAttribDecls:
*
*@@added V0.9.9 (2001-02-16) [umoeller]
*/
VOID XWPENTRY InsertAttribDecls(PCONVERSION pCodec,
TREE *t, // in: PCMATTRIBUTEDECL really
PVOID pUser) // in: PINSERTSTACK user param
{
PCMATTRIBUTEDECL pAttribDecl = (PCMATTRIBUTEDECL)t;
PINSERTSTACK pInsertStack = (PINSERTSTACK)pUser;
PNODERECORD prec = (PNODERECORD)cnrhAllocRecords(G_hwndCnr,
sizeof(NODERECORD),
1);
XSTRING str;
xstrInitCopy(&str, "attrib: \"", 0);
xstrcats(&str, &pAttribDecl->NodeBase.strNodeName);
xstrcat(&str, "\", type: ", 0);
switch (pAttribDecl->ulAttrType)
{
case CMAT_ENUM:
{
xstrcat(&str, "ENUM(", 0);
PNODEBASE pNode = (PNODEBASE)treeFirst(pAttribDecl->ValuesTree);
while (pNode)
{
AppendUTF8(pCodec, &str, &pNode->strNodeName);
// xstrcats(&str, &pNode->strNodeName);
xstrcatc(&str, '|');
pNode = (PNODEBASE)treeNext(&pNode->Tree);
}
/* treeTraverse(pAttribDecl->ValuesTree,
CatAttribEnums,
&str,
0); */
*(str.psz + str.ulLength - 1) = ')';
break; }
case CMAT_CDATA:
xstrcat(&str, "CDATA", 0);
break;
case CMAT_ID:
xstrcat(&str, "ID", 0);
break;
case CMAT_IDREF:
xstrcat(&str, "IDREF", 0);
break;
case CMAT_IDREFS:
xstrcat(&str, "IDREFS", 0);
break;
case CMAT_ENTITY:
xstrcat(&str, "ENTITY", 0);
break;
case CMAT_ENTITIES:
xstrcat(&str, "ENTITIES", 0);
break;
case CMAT_NMTOKEN:
xstrcat(&str, "NMTOKEN", 0);
break;
case CMAT_NMTOKENS:
xstrcat(&str, "NMTOKENS", 0);
break;
}
cnrhInsertRecords(G_hwndCnr,
(PRECORDCORE)pInsertStack->pParentRecord,
(PRECORDCORE)prec,
TRUE,
str.psz,
CRA_RECORDREADONLY,
1);
}
/*
*@@ InsertEDParticle:
*
*@@added V0.9.9 (2001-02-16) [umoeller]
*/
PNODERECORD InsertEDParticle(PCMELEMENTPARTICLE pParticle,
PNODERECORD pParentRecord)
{
PNODERECORD prec = (PNODERECORD)cnrhAllocRecords(G_hwndCnr,
sizeof(NODERECORD),
1);
XSTRING str;
xstrInitCopy(&str, "\"", 0);
xstrcats(&str, &pParticle->NodeBase.strNodeName);
switch (pParticle->ulRepeater)
{
// XML_CQUANT_NONE
case XML_CQUANT_OPT:
xstrcatc(&str, '?');
break;
case XML_CQUANT_REP:
xstrcatc(&str, '*');
break;
case XML_CQUANT_PLUS:
xstrcatc(&str, '+');
break;
}
const char *pcsz = "unknown";
switch (pParticle->NodeBase.ulNodeType)
{
case ELEMENTPARTICLE_EMPTY:
pcsz = "\" EMPTY";
break;
case ELEMENTPARTICLE_ANY:
pcsz = "\" ANY";
break;
case ELEMENTPARTICLE_MIXED:
pcsz = "\" MIXED";
break;
case ELEMENTPARTICLE_CHOICE:
pcsz = "\" CHOICE";
break;
case ELEMENTPARTICLE_SEQ:
pcsz = "\" SEQ";
break;
case ELEMENTPARTICLE_NAME: // subtypes
pcsz = "\" NAME";
break;
}
xstrcat(&str, pcsz, 0);
cnrhInsertRecords(G_hwndCnr,
(PRECORDCORE)pParentRecord,
(PRECORDCORE)prec,
TRUE,
str.psz,
CRA_RECORDREADONLY,
1);
if (pParticle->pllSubNodes)
{
PLISTNODE pNode = lstQueryFirstNode(pParticle->pllSubNodes);
while (pNode)
{
PCMELEMENTPARTICLE pSub = (PCMELEMENTPARTICLE)pNode->pItemData;
InsertEDParticle(pSub,
prec);
pNode = pNode->pNext;
}
}
return (prec);
}
/*
*@@ InsertElementDecls:
* tree traversal function initiated from InsertDocType.
*
*@@added V0.9.9 (2001-02-16) [umoeller]
*/
VOID XWPENTRY InsertElementDecls(TREE *t, // in: an CMELEMENTDECLNODE really
PVOID pUser) // in: PINSERTSTACK user param
{
}
/*
*@@ InsertDocType:
*
*@@added V0.9.9 (2001-02-16) [umoeller]
*/
VOID InsertDocType(PCONVERSION pCodec,
PXMLDOM pDom,
PDOMDOCTYPENODE pDocTypeNode,
PNODERECORD pParentRecord)
{
// INSERTSTACK InsertStack = {pParentRecord, pDom};
TREE *t = treeFirst(pDocTypeNode->ElementDeclsTree);
while (t)
{
PCMELEMENTDECLNODE pElementDecl = (PCMELEMENTDECLNODE)t;
// insert the element declaration root particle and subparticles
// (this recurses)
PNODERECORD pElementRec = InsertEDParticle(&pElementDecl->Particle,
pParentRecord);
// now insert attribute decls, if any
PCMATTRIBUTEDECLBASE pAttribDeclBase
= xmlFindAttribDeclBase(pDom,
&pElementDecl->Particle.NodeBase.strNodeName);
if (pAttribDeclBase)
{
// traverse the attributes tree as well
INSERTSTACK Stack2 = {pElementRec, pDom};
TREE *t = treeFirst(pAttribDeclBase->AttribDeclsTree);
while (t)
{
InsertAttribDecls(pCodec,
t,
&Stack2);
t = treeNext(t);
}
/* treeTraverse(pAttribDeclBase->AttribDeclsTree,
InsertAttribDecls,
&Stack2, // user param
0); */
}
/* InsertAttribDecls(t,
&InsertStack); */
t = treeNext(t);
}
/* treeTraverse(pDocTypeNode->ElementDeclsTree,
InsertElementDecls,
&InsertStack, // user param
0); */
}
VOID InsertDom(PCONVERSION pCodec,
PXMLDOM pDom,
PDOMNODE pDomNode,
PNODERECORD pParentRecord);
/*
*@@ InsertDom:
*
* Note: We get the document node first, which we must
* not insert, but only its children.
*/
VOID InsertDom(PCONVERSION pCodec,
PXMLDOM pDom,
PDOMNODE pDomNode,
PNODERECORD pParentRecord) // initially NULL
{
BOOL fInsertChildren = FALSE;
PNODERECORD prec = NULL;
PDOMDOCTYPENODE pDocType = NULL;
XSTRING str;
xstrInit(&str, 100);
switch (pDomNode->NodeBase.ulNodeType)
{
case DOMNODE_ELEMENT:
// xstrcpys(&str, &pDomNode->NodeBase.strNodeName);
AppendUTF8(pCodec, &str, &pDomNode->NodeBase.strNodeName);
break;
case DOMNODE_ATTRIBUTE:
AppendUTF8(pCodec, &str, &pDomNode->NodeBase.strNodeName);
// xstrcpys(&str, &pDomNode->NodeBase.strNodeName);
xstrcat(&str, "=\"", 2);
// xstrcats(&str, pDomNode->pstrNodeValue);
AppendUTF8(pCodec, &str, pDomNode->pstrNodeValue);
xstrcatc(&str, '\"');
break;
case DOMNODE_TEXT:
xstrcpy(&str, "\"", 1);
// xstrcats(&str, pDomNode->pstrNodeValue);
AppendUTF8(pCodec, &str, pDomNode->pstrNodeValue);
xstrcatc(&str, '\"');
break;
case DOMNODE_DOCUMENT:
xstrcpy(&str, "Document", 0);
xstrcat(&str, " \"", 0);
// xstrcats(&str, &pDomNode->NodeBase.strNodeName);
AppendUTF8(pCodec, &str, &pDomNode->NodeBase.strNodeName);
xstrcat(&str, "\"", 0);
break;
case DOMNODE_DOCUMENT_TYPE:
{
pDocType = (PDOMDOCTYPENODE)pDomNode;
xstrcpy(&str, "DOCTYPE system: \"", 0);
// xstrcats(&str, &pDocType->strSystemID);
AppendUTF8(pCodec, &str, &pDocType->strSystemID);
xstrcat(&str, "\", public: \"", 0);
// xstrcats(&str, &pDocType->strPublicID);
AppendUTF8(pCodec, &str, &pDocType->strPublicID);
xstrcatc(&str, '\"');
break; }
default:
{
CHAR sz[1000];
sprintf(sz, "Unknown node type %d", pDomNode->NodeBase.ulNodeType);
xstrcpy(&str, sz, 0);
break; }
}
if (str.ulLength)
{
prec = (PNODERECORD)cnrhAllocRecords(G_hwndCnr,
sizeof(NODERECORD),
1);
cnrhInsertRecords(G_hwndCnr,
(PRECORDCORE)pParentRecord,
(PRECORDCORE)prec,
TRUE,
str.psz,
CRA_RECORDREADONLY,
1);
fInsertChildren = TRUE;
}
if (fInsertChildren)
{
// insert attributes
INSERTSTACK Stack2 = {prec, pDom};
PDOMNODE pAttrNode = (PDOMNODE)treeFirst(pDomNode->AttributesMap);
while (pAttrNode)
{
InsertDom(pCodec,
pDom,
pAttrNode,
prec);
pAttrNode = (PDOMNODE)treeNext((TREE*)pAttrNode);
}
// and children
PLISTNODE pSubNode = lstQueryFirstNode(&pDomNode->llChildren);
while (pSubNode)
{
PDOMNODE pDomSubnode = (PDOMNODE)pSubNode->pItemData;
InsertDom(pCodec,
pDom,
pDomSubnode,
prec);
pSubNode = pSubNode->pNext;
}
}
else
xstrClear(&str);
if (prec && pDocType)
InsertDocType(pCodec, pDom, pDocType, prec);
}
/* ******************************************************************
*
* Container window proc
*
********************************************************************/
/*
*@@ LoadAndInsert:
*
*/
VOID LoadAndInsert(const char *pcszFile)
{
PSZ pszError;
APIRET arc = LoadXMLFile(pcszFile,
&G_pLoadedFile,
&pszError);
if ( (arc == ERROR_DOM_PARSING)
|| (arc == ERROR_DOM_VALIDITY)
)
{
winhSetVarWindowText(G_hwndStatusBar,
pszError);
free(pszError);
}
else if (arc != NO_ERROR)
{
winhSetVarWindowText(G_hwndStatusBar,
"Error %d loading \"%s\".",
arc,
pcszFile);
}
else
{
winhSetVarWindowText(G_hwndStatusBar,
"File \"%s\" loaded, %d bytes",
pcszFile,
G_pLoadedFile->strContents.ulLength);
cnrhRemoveAll(G_hwndCnr);
// create codec for process codepage
ULONG acp[8]; // fixed V0.9.19 (2002-04-14) [umoeller], this needs an array
ULONG cb = 0;
APIRET arcCP;
ENCID id;
PCONVERSION pCodec;
if ( (!(arcCP = DosQueryCp(sizeof(acp),
acp,
&cb)))
&& (id = encFindIdForCodepage(acp[0], NULL, NULL))
&& (pCodec = encCreateCodec(id))
)
{
InsertDom(pCodec,
G_pLoadedFile->pDom,
(PDOMNODE)G_pLoadedFile->pDom->pDocumentNode,
NULL);
encFreeCodec(&pCodec);
}
}
}
/*
*@@ fnwpSubclassedCnr:
*
*/
MRESULT EXPENTRY fnwpSubclassedCnr(HWND hwndCnr, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
switch (msg)
{
case WM_CLOSE:
winhSaveWindowPos(G_hwndMain,
HINI_USER,
INIAPP,
INIKEY_MAINWINPOS);
mrc = G_pfnwpCnrOrig(hwndCnr, msg, mp1, mp2);
break;
case WM_COMMAND:
switch (SHORT1FROMMP(mp1))
{
case IDMI_OPEN:
{
CHAR szFile[CCHMAXPATH] = "*.xml";
if (SuperFileDlg(G_hwndMain,
szFile,
WINH_FOD_INILOADDIR | WINH_FOD_INISAVEDIR,
HINI_USER,
INIAPP,
INIKEY_OPENDLG))
LoadAndInsert(szFile);
break; }
case IDMI_EXIT:
WinPostMsg(G_hwndMain,
WM_SYSCOMMAND,
(MPARAM)SC_CLOSE,
0);
break;
}
break;
default:
mrc = G_pfnwpCnrOrig(hwndCnr, msg, mp1, mp2);
}
return (mrc);
}
/*
*@@ SetupCnr:
*
*/
VOID SetupCnr(HWND hwndCnr)
{
BEGIN_CNRINFO()
{
// switch view
cnrhSetView(CV_TREE | CA_TREELINE | CV_TEXT);
cnrhSetTreeIndent(30);
} END_CNRINFO(hwndCnr)
winhSetWindowFont(hwndCnr, NULL);
}
/* ******************************************************************
*
* Frame window proc
*
********************************************************************/
/*
*@@ winhCreateStatusBar:
*
*@@added V0.9.5 (2000-08-13) [umoeller]
*/
HWND winhCreateStatusBar(HWND hwndFrame,
HWND hwndOwner,
USHORT usID,
PSZ pszFont,
LONG lColor)
{
// create status bar
HWND hwndReturn = NULLHANDLE;
PPRESPARAMS ppp = NULL;
winhStorePresParam(&ppp, PP_FONTNAMESIZE, strlen(pszFont)+1, pszFont);
lColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_DIALOGBACKGROUND, 0);
winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor);
lColor = CLR_BLACK;
winhStorePresParam(&ppp, PP_FOREGROUNDCOLOR, sizeof(lColor), &lColor);
hwndReturn = WinCreateWindow(G_hwndMain,
WC_STATIC,
"Welcome to xmlview, built " __DATE__,
SS_TEXT | DT_VCENTER | WS_VISIBLE,
0, 0, 0, 0,
hwndOwner,
HWND_TOP,
usID,
NULL,
ppp);
free(ppp);
return (hwndReturn);
}
static PRECORDCORE s_preccExpanded = NULL;
/*
*@@ winh_fnwpFrameWithStatusBar:
* subclassed frame window proc.
*
*@@added V0.9.5 (2000-08-13) [umoeller]
*/
MRESULT EXPENTRY winh_fnwpFrameWithStatusBar(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mrc = 0;
switch (msg)
{
case WM_QUERYFRAMECTLCOUNT:
{
// query the standard frame controls count
ULONG ulrc = (ULONG)(G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2));
// if we have a status bar, increment the count
ulrc++;
mrc = (MPARAM)ulrc;
break; }
case WM_FORMATFRAME:
{
// query the number of standard frame controls
ULONG ulCount = (ULONG)(G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2));
// we have a status bar:
// format the frame
ULONG ul;
PSWP swpArr = (PSWP)mp1;
for (ul = 0; ul < ulCount; ul++)
{
if (WinQueryWindowUShort( swpArr[ul].hwnd, QWS_ID ) == 0x8008 )
// FID_CLIENT
{
POINTL ptlBorderSizes;
ULONG ulStatusBarHeight = 20;
WinSendMsg(hwndFrame,
WM_QUERYBORDERSIZE,
(MPARAM)&ptlBorderSizes,
0);
// first initialize the _new_ SWP for the status bar.
// Since the SWP array for the std frame controls is
// zero-based, and the standard frame controls occupy
// indices 0 thru ulCount-1 (where ulCount is the total
// count), we use ulCount for our static text control.
swpArr[ulCount].fl = SWP_MOVE | SWP_SIZE | SWP_NOADJUST | SWP_ZORDER;
swpArr[ulCount].x = ptlBorderSizes.x;
swpArr[ulCount].y = ptlBorderSizes.y;
swpArr[ulCount].cx = swpArr[ul].cx; // same as cnr's width
swpArr[ulCount].cy = ulStatusBarHeight;
swpArr[ulCount].hwndInsertBehind = HWND_BOTTOM; // HWND_TOP;
swpArr[ulCount].hwnd = G_hwndStatusBar;
// adjust the origin and height of the container to
// accomodate our static text control
swpArr[ul].y += swpArr[ulCount].cy;
swpArr[ul].cy -= swpArr[ulCount].cy;
}
}
// increment the number of frame controls
// to include our status bar
mrc = (MRESULT)(ulCount + 1);
break; }
case WM_CALCFRAMERECT:
{
mrc = G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2);
// we have a status bar: calculate its rectangle
// CalcFrameRect(mp1, mp2);
break; }
case WM_CONTROL:
if (SHORT1FROMMP(mp1) == FID_CLIENT)
{
switch (SHORT2FROMMP(mp1))
{
case CN_EXPANDTREE:
s_preccExpanded = (PRECORDCORE)mp2;
WinStartTimer(WinQueryAnchorBlock(hwndFrame),
hwndFrame,
1,
100);
break;
}
}
break;
case WM_TIMER:
// stop timer (it's just for one shot)
WinStopTimer(WinQueryAnchorBlock(hwndFrame),
hwndFrame,
(ULONG)mp1); // timer ID
switch ((ULONG)mp1) // timer ID
{
case 1:
{
if (s_preccExpanded->flRecordAttr & CRA_EXPANDED)
{
HWND hwndCnr = WinWindowFromID(hwndFrame, FID_CLIENT);
PRECORDCORE preccLastChild;
// scroll the tree view properly
preccLastChild = (PRECORDCORE)WinSendMsg(hwndCnr,
CM_QUERYRECORD,
s_preccExpanded,
// expanded PRECORDCORE from CN_EXPANDTREE
MPFROM2SHORT(CMA_LASTCHILD,
CMA_ITEMORDER));
if (preccLastChild)
{
// ULONG ulrc;
cnrhScrollToRecord(hwndCnr,
(PRECORDCORE)preccLastChild,
CMA_TEXT, // record text rectangle only
TRUE); // keep parent visible
}
}
break; }
}
break;
default:
mrc = G_fnwpFrameOrig(hwndFrame, msg, mp1, mp2);
}
return (mrc);
}
/* ******************************************************************
*
* main
*
********************************************************************/
#define ARRAYITEMCOUNT(array) sizeof(array) / sizeof(array[0])
/* typedef struct _CONTROLDEF
{
const char *pcszClass; // registered PM window class
const char *pcszText; // window text (class-specific);
// NULL for tables
ULONG flStyle;
ULONG ulID;
USHORT usAdjustPosition;
// flags for winhAdjustControls; any combination of
// XAC_MOVEX, XAC_MOVEY, XAC_SIZEX, XAC_SIZEY
SIZEL szlControl; // proposed size
ULONG ulSpacing; // spacing around control
} CONTROLDEF, *PCONTROLDEF; */
static CONTROLDEF
Static1 =
{
WC_STATIC,
"Test text row 1 column 1",
WS_VISIBLE | SS_TEXT | DT_LEFT | DT_TOP,
0,
CTL_COMMON_FONT,
0,
{ 300, 30 },
5
},
Static2 =
{
WC_STATIC,
"Test text row 1 column 2",
WS_VISIBLE | SS_TEXT | DT_LEFT | DT_TOP,
0,
CTL_COMMON_FONT,
0,
{ 300, 30 },
5
},
Cnr =
{
WC_CONTAINER,
NULL,
WS_VISIBLE | WS_TABSTOP,
0,
CTL_COMMON_FONT,
0,
{ 100, 100 },
5
},
CnrGroup =
{
WC_STATIC,
"Container1",
WS_VISIBLE | SS_GROUPBOX | DT_MNEMONIC,
0,
CTL_COMMON_FONT,
0,
{ 100, 100 }, // ignored
5
},
OKButton =
{
WC_BUTTON,
"~OK",
WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_DEFAULT,
0,
CTL_COMMON_FONT,
0,
{ SZL_AUTOSIZE, 30 },
5
},
CancelButton =
{
WC_BUTTON,
"~Cancel",
WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
0,
CTL_COMMON_FONT,
0,
{ SZL_AUTOSIZE, 30 },
5
};
static _DLGHITEM aDlgItems[] =
{
START_TABLE,
START_ROW(0),
CONTROL_DEF(&Static1),
START_ROW(0),
START_GROUP_TABLE(&CnrGroup),
START_ROW(0),
CONTROL_DEF(&Cnr),
END_TABLE,
START_GROUP_TABLE(&CnrGroup),
START_ROW(0),
CONTROL_DEF(&Cnr),
START_ROW(0),
CONTROL_DEF(&OKButton),
START_ROW(0),
CONTROL_DEF(&CancelButton),
END_TABLE,
START_ROW(0),
CONTROL_DEF(&Static2),
START_ROW(0),
CONTROL_DEF(&OKButton),
CONTROL_DEF(&CancelButton),
END_TABLE
};
/*
*@@ main:
*
*/
int main(int argc, char* argv[])
{
HAB hab;
HMQ hmq;
QMSG qmsg;
if (!(hab = WinInitialize(0)))
return FALSE;
if (!(hmq = WinCreateMsgQueue(hab, 0)))
return FALSE;
DosError(FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
/* HWND hwndDlg = NULLHANDLE;
APIRET arc = dlghCreateDlg(&hwndDlg,
NULLHANDLE, // owner
FCF_TITLEBAR | FCF_SYSMENU | FCF_DLGBORDER | FCF_NOBYTEALIGN,
WinDefDlgProc,
"DemoDlg",
aDlgItems,
ARRAYITEMCOUNT(aDlgItems),
NULL,
"9.WarpSans");
if (arc == NO_ERROR)
{
WinProcessDlg(hwndDlg);
WinDestroyWindow(hwndDlg);
}
else
{
CHAR szErr[100];
sprintf(szErr, "Error %d", arc);
winhDebugBox(NULLHANDLE, "Error", szErr);
} */
// create frame and container
G_hwndMain = winhCreateStdWindow(HWND_DESKTOP,
0,
FCF_TITLEBAR
| FCF_SYSMENU
| FCF_MINMAX
| FCF_SIZEBORDER
| FCF_ICON
| FCF_MENU
| FCF_TASKLIST,
0,
APPTITLE, // xmlview
1, // icon resource
WC_CONTAINER,
CCS_MINIICONS | CCS_READONLY | CCS_EXTENDSEL
| WS_VISIBLE,
0,
NULL,
&G_hwndCnr);
if ((G_hwndMain) && (G_hwndCnr))
{
// subclass cnr (it's our client)
G_pfnwpCnrOrig = WinSubclassWindow(G_hwndCnr, fnwpSubclassedCnr);
// create status bar as child of the frame
G_hwndStatusBar = winhCreateStatusBar(G_hwndMain,
G_hwndCnr,
0,
"9.WarpSans",
CLR_BLACK);
// subclass frame for supporting status bar and msgs
G_fnwpFrameOrig = WinSubclassWindow(G_hwndMain,
winh_fnwpFrameWithStatusBar);
SetupCnr(G_hwndCnr);
if (!winhRestoreWindowPos(G_hwndMain,
HINI_USER,
INIAPP,
INIKEY_MAINWINPOS,
SWP_SHOW | SWP_ACTIVATE | SWP_MOVE | SWP_SIZE))
WinSetWindowPos(G_hwndMain,
HWND_TOP,
10, 10, 500, 500,
SWP_SHOW | SWP_ACTIVATE | SWP_MOVE | SWP_SIZE);
if (argc > 1)
LoadAndInsert(argv[1]);
// standard PM message loop
while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
{
WinDispatchMsg(hab, &qmsg);
}
}
// clean up on the way out
WinDestroyMsgQueue(hmq);
WinTerminate(hab);
return (0);
}