
/*
 *@@sourcefile classlst.c:
 *      contains the logic to display the WPS class tree
 *      in a container control.
 *
 *      Most of this file was called "objdlgs.c" in V0.80.
 *
 *      All the funcs in here have the cls* prefix.
 *
 *      In order to use this, you need to do the following:
 *
 *      1) set up a SELECTCLASSDATA structure (classlst.h)
 +             SELECTCLASSDATA scd;
 +             strcpy(scd.szDlgTitle, "Select Class");
 +             strcpy(scd.szIntroText, "Select the class for which you want to change "
 +                                   "the status bar info.");
 +             strcpy(scd.szRootClass, "WPObject");
 +             strcpy(scd.szClassSelected, "WPObject");
 +             scd.ulHelpPanel = 0;
 *
 *      2) pass this to WinDlgBox:
 +             WinDlgBox(HWND_DESKTOP, hwndDlg,
 +                     fnwpSelectWPSClass,
 +                     NLS_MODULE, ID_XSD_SELECTCLASS,  // resources
 +                     &scd);
 *
 *      The selected WPS class will then be written into
 *      the SELECTCLASSDATA structure.
 *
 *      Alternatively, you can use clsSelectWpsClassDlg.
 *
 *      Naturally, these functions require the dialog templates
 *      in xfldr.dlg.
 *
 *@@include #define INCL_WINSTDCNR
 *@@include #define INCL_WINWORKPLACE
 *@@include #include <os2.h>
 *@@include #include <wpobject.h>
 *@@include #include "classlst.h"
 */

/*
 *      Copyright (C) 1997-99 Ulrich M”ller.
 *      This file is part of the XFolder source package.
 *      XFolder 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.
 */

/*
 *  Suggested #include order:
 *  1)  os2.h
 *  2)  C library headers
 *  3)  SOM headers which work with precompiled header files
 *  4)  headers in /helpers
 *  5)  headers in /main with dlgids.h and common.h first
 *  6)  #pragma hdrstop to prevent VAC++ crashes
 *  7)  other needed SOM headers
 *  8)  for non-SOM-class files: corresponding header (e.g. classlst.h)
 */

#define INCL_DOSPROCESS         // DosSleep, priorities, PIDs etc.
#define INCL_DOSSEMAPHORES      // needed for xthreads.h
#define INCL_DOSEXCEPTIONS

#define INCL_WINWINDOWMGR
#define INCL_WINSYS             // presparams, WinQuerySysValue()
#define INCL_WINPOINTERS
#define INCL_WINMENUS           // needed for menus.h
#define INCL_WINENTRYFIELDS
#define INCL_WINDIALOGS
#define INCL_WINSTDCNR          // needed for winh.h
#define INCL_WINSTDFILE         // file dialog

#define INCL_GPILOGCOLORTABLE
#define INCL_GPIPRIMITIVES

#include <os2.h>

// C library headers
#include <stdio.h>
#include <malloc.h>

// headers in /helpers
#include "winh.h"               // PM helper routines

#include "linklist.h"           // linked list helper routines

// SOM headers which don't crash with prec. header files

// headers in /main
#include "dlgids.h"             // all the IDs that are shared with NLS
#include "common.h"             // the majestic XFolder include file

#include "cnrsort.h"            // container sort comparison functions

// other SOM headers
#pragma hdrstop                 // VAC++ keeps crashing otherwise
#include <wpclsmgr.h>           // this includes SOMClassMgr
#include <wpdesk.h>             // WPDesktop

// finally, our own header file
#include "classlst.h"           // SOM logic for "WPS Classes" page

// #define DEBUG_WPSCLASSES

/* ******************************************************************
 *                                                                  *
 *   "Select class" dialog                                          *
 *                                                                  *
 ********************************************************************/

/*
 *@@  clsAddClass2Cnr:
 *      This inserts one single WPS class record core into a given container.
 *      We create a new record core (of CLASSRECORDCORE type) from the
 *      data which is given in the WPSLISTITEM structure.
 *      SELECTCLASSDATA is a "Select class" dialog description
 *      structure used throughout this file.
 *      All these structs are defined in classlst.h.
 *
 *      <P><B>Usage:</B> by all XFolder WPS class list views.
 */

PCLASSRECORDCORE clsAddClass2Cnr(HWND hwndCnr,                  // in: cnr to insert into
                                 PCLASSRECORDCORE preccParent,  // in: parent recc for tree view
                                 PWPSLISTITEM pwpsMyself,       // in: class description
                                 PSELECTCLASSDATA pscd)         // in: dlg info
{
    PCLASSRECORDCORE      preccNew = (PCLASSRECORDCORE)
            winhCnrAllocRecord(hwndCnr, sizeof(CLASSRECORDCORE)); // /helpers/winh.c
    // recc attributes
    ULONG            usAttrs = CRA_EXPANDED | CRA_RECORDREADONLY;

    #ifdef DEBUG_WPSCLASSES
        _Pmpf(("  clsAddClass2Cnr %s\n", pszCurrentClass));
    #endif

    if (preccNew) {
        // store the WPS class info structure into recc
        // set extra recc attribute (WPSLISTITEM)
        preccNew->pwps = pwpsMyself;
        if (pwpsMyself) {
            pwpsMyself->pRecord = (PRECORDCORE)preccNew;

            if (pscd)
                // now call callback function defined in
                // SELECTCLASSDATA to determine the recc
                // attributes; this way, a class can be
                // disabled, expanded etc.
                if (pscd->pfnwpReturnAttrForClass)
                    usAttrs = (USHORT)(*pscd->pfnwpReturnAttrForClass)(
                            hwndCnr, (ULONG)pscd,
                            (MPARAM)pwpsMyself, (MPARAM)preccParent);
                else {
                    // no callback defined: set read-only
                    usAttrs = CRA_RECORDREADONLY;
                    // and expand three tree view levels
                    if (pscd->lRecurse < 3)
                        usAttrs |= CRA_EXPANDED;
                }
        } else
            // pwpsMyself might be NULL for the "Orphans" tree parent item
            usAttrs = CRA_RECORDREADONLY;

        #ifdef DEBUG_WPSCLASSES
            _Pmpf(("    Inserting %s\n", pszCurrentClass));
        #endif
        // insert the record (helpers/winh.c)
        winhCnrInsertRecord(hwndCnr, (PRECORDCORE)preccParent, (PRECORDCORE)preccNew,
                        (pwpsMyself)
                            ? pwpsMyself->pszClassName
                            : pscd->pszOrphans,     // "Orphans" string
                        usAttrs);

        // select the new record?
        if (usAttrs & (CRA_INUSE | CRA_CURSORED | CRA_PICKED | CRA_SELECTED)) {
            #ifdef DEBUG_WPSCLASSES
                _Pmpf(("    Selecting %s\n", pszCurrentClass));
            #endif
            WinSendMsg(hwndCnr, CM_SETRECORDEMPHASIS,
                    (MPARAM)preccNew,
                    MPFROM2SHORT(TRUE,
                            (USHORT)(usAttrs & (CRA_INUSE | CRA_CURSORED | CRA_PICKED | CRA_SELECTED)))
            );
        }

        // mark the class as processed (needed by clsWPSClasses2Cnr)
        if (pwpsMyself)
            pwpsMyself->fProcessed = TRUE;
    }
    return (preccNew);
}

/*
 *@@ clsAddClassTree2Cnr:
 *      This is initially called by clsWpsClasses2Cnr
 *      with pwpsCurrent being the list item for the
 *      root class (normally WPObject). This will insert
 *      that class into the container and then recurse
 *      to process descendant classes.
 */

VOID clsAddClassTree2Cnr(HWND hwndCnr,              // in: cnr
                    PWPSLISTITEM pwpsCurrent,       // in: class to process
                    PWPSCLASSESINFO pwpsci,         // in: complete list
                    PCLASSRECORDCORE preccParent,   // in: parent record for tree view or NULL for root
                    PSELECTCLASSDATA pscd)          // in: insertion info structure
{
    PWPSLISTITEM pwps0, pwpsParentToCheck;
    PCLASSRECORDCORE preccParentToCheck;
    PSZ pszParentToCheckClass;

    // add the current class to the cnr;
    // initially, this is WPObject, for recursive calls,
    // this will be a child of WPObject
    clsAddClass2Cnr(hwndCnr,
                    preccParent, // parent recc
                    pwpsCurrent,
                    pscd);

    // query if any parent class has been replaced by this class;
    // in order to find out about that, we climb up the parent record
    // cores in the container and ask the WPS class manager if
    // replacements are in effect
    preccParentToCheck = preccParent;
    #ifdef DEBUG_WPSCLASSES
        _Pmpf(("Checking whether %s replaces", pwpsCurrent->pszClassName));
    #endif
    while (preccParentToCheck) {
        // get WPSLISTITEM struct from record core
        pwpsParentToCheck = preccParentToCheck->pwps;
        pszParentToCheckClass = _somGetName(pwpsParentToCheck->pClassObject);
        if (_wpReplacementIsInEffect(SOMClassMgrObject,
                        pszParentToCheckClass,      // parent class
                        pwpsCurrent->pszClassName)) // current class
        {
            // pszParentToCheckClass has been replaced by current class:
            pwpsCurrent->pszReplacesThisClass = strdup(pszParentToCheckClass);
            if (pwpsParentToCheck->pszReplacedWithClasses) {
                // if there are several class replacements for the parent,
                // append this class name to the other replacements
                PSZ psz2 = malloc(strlen(pwpsParentToCheck->pszReplacedWithClasses)
                                  +strlen(pwpsCurrent->pszClassName)+6);
                sprintf(psz2, "%s, %s",
                        pwpsParentToCheck->pszReplacedWithClasses,
                        pwpsCurrent->pszClassName);
                free(pwpsParentToCheck->pszReplacedWithClasses);
                pwpsParentToCheck->pszReplacedWithClasses = psz2;
            } else {
                // otherwise, use this class name only
                pwpsParentToCheck->pszReplacedWithClasses =
                        strdup(pwpsCurrent->pszClassName);
            }
        }

        // climb up to next parent
        preccParentToCheck = (PCLASSRECORDCORE)WinSendMsg(hwndCnr,
                                CM_QUERYRECORD,
                                (MPARAM)preccParentToCheck,
                                MPFROM2SHORT(CMA_PARENT, // get parent recc
                                    CMA_ITEMORDER));
        if (preccParentToCheck == (PCLASSRECORDCORE)-1)
            // container error: stop
            preccParentToCheck = NULL;
    }

    // now go thru the whole list and add all immediate
    // children of ours
    pwps0 = pwpsci->pwpsClassList;
    while (pwps0) {
        if (pwps0->pParentClassObject == pwpsCurrent->pClassObject)
        {
            // this class is an immediate child of ours:
            // recurse! This will add this class's
            // record core to the cnr and check for
            // children again
            clsAddClassTree2Cnr(hwndCnr,
                    pwps0,
                    pwpsci,
                    (PCLASSRECORDCORE)pwpsCurrent->pRecord,
                    pscd);
        }
        pwps0 = pwps0->pNext;
    }
}

/*
 *@@ clsWpsClasses2Cnr:
 *      this func inserts a WPS class hierarchy starting with
 *      the class "pszCurrentClass" as the root class into
 *      a given container window. This cnr should be in tree
 *      view to have a meaningful display. This function is
 *      used by the "WPS Classes" notebook page, but can be
 *      used with any existing container also.
 *      Parameters:
 *      --  HWND hwndCnr:       guess what this is
 *      --  PSZ pszRootClass:   the "root" class of the display,
 *                              e.g. "WPObject"; make sure this
 *                              class exists, or nothing happens
 *      --  PSELECTCLASSDATA pscd:
 *                              structure with lots of information
 *                              about how classes are to be inserted;
 *                              this may be NULL for defaults. If
 *                              pfnwpReturnAttrForClass in this
 *                              this structure is != NULL, it will
 *                              be called as a callback for each
 *                              item inserted.
 *
 *      Returns:
 *      --  PWPSCLASSESINFO     structure for cleanup later
 *
 *      NOTE: This function eats up a lot of memory, depending
 *      on how many WPS classes are installed. This memory is NOT
 *      freed when this function returns, because the container
 *      still needs it. AFTER destroying the container window,
 *      you must issue clsCleanupWpsClasses (below) with the
 *      return value of whis function. This will free the linked
 *      WPS class item list and free allocated SOM resources.
 */

PWPSCLASSESINFO clsWpsClasses2Cnr(HWND hwndCnr,
                    PSZ pszRootClass,  // in: e.g. WPObject
                    PSELECTCLASSDATA pscd)          // in: insertion info struct
{
    PWPSCLASSESINFO  pwpsciReturn = NULL;
    BOOL             fBufferAllocated = FALSE;
    POBJCLASS        pObjClass;
    PWPSLISTITEM     pwpsRoot = NULL,
                     pwps0 = NULL;
    somId            somidRoot;
    SOMClass         *pRootClassObject = NULL;

    SOMClassSequence RegisteredClasses;

    ULONG   ulSize;
    ULONG   ul;

    // create common buffer
    pwpsciReturn = malloc(sizeof(WPSCLASSESINFO));
    memset(pwpsciReturn, 0, sizeof(WPSCLASSESINFO));

    #ifdef DEBUG_WPSCLASSES
        _Pmpf(("  clsWpsClasses2Cnr: Collecting classes\n"));
    #endif

    // get WPS class list
    WinEnumObjectClasses(NULL, &ulSize);
    pwpsciReturn->pObjClass = malloc(ulSize+1);
    WinEnumObjectClasses(pwpsciReturn->pObjClass, &ulSize);
    fBufferAllocated = TRUE;

    if (pscd->pszOrphans) {
        // "Orphans" tree requested: load all
        // registered WPS classes.
        PCLASSRECORDCORE preccOrphans = NULL;
                // for "root" record cor ("Orphaned classes")
        pObjClass = pwpsciReturn->pObjClass;
        // now go thru the WPS class list
        while (pObjClass) {
            // the following will load the class
            somId       somidThis = somIdFromString(pObjClass->pszClassName);
            SOMClass    *pClassThis = _somFindClass(SOMClassMgrObject, somidThis, 0, 0);
            if (!pClassThis) {
                // class object == NULL: means that class loading failed
                PWPSLISTITEM pwpsNew = malloc(sizeof(WPSLISTITEM));
                memset(pwpsNew, 0, sizeof(WPSLISTITEM));

                // create "orphaned" tree, if not yet created
                if (preccOrphans == NULL) {
                    preccOrphans = clsAddClass2Cnr(hwndCnr,
                            NULL,       // parent recc
                            NULL,       // "orphans" string
                            pscd);
                }

                // set up WPSLISTITEM data
                pwpsNew->pszClassName = pObjClass->pszClassName;
                pwpsNew->pszModName = pObjClass->pszModName;
                // add orphaned class to "Orphaned" tree
                clsAddClass2Cnr(hwndCnr, preccOrphans, pwpsNew,
                        pscd);
            }
            // next class
            pObjClass = pObjClass->pNext;
        }
    }

    // get SOMClassMgr's list of registered classes.
    // This is only mentioned in SOMCM.IDL and not
    // otherwise documented, but it works. This list
    // only contains the class objects which have
    // actually been successfully loaded.
    // (New with V0.82. Previous versions used the WPS
    // class list, which returned garbage sometimes.)
    RegisteredClasses = __get_somRegisteredClasses(SOMClassMgrObject);

    // get class object of root class to process
    somidRoot = somIdFromString(pszRootClass);
    if (somidRoot) {
        // find root class object (e.g. M_WPObject).
        pRootClassObject = _somFindClass(SOMClassMgrObject, somidRoot, 0, 0);

        // note: somFindClass will return the replacement class
        // if the given root class has been replaced. (That's how
        // class replacements work.) For example, if pszRootClass
        // is "WPObject", we will now have the XFldObject class
        // object. So if the title is not right, we need to climb
        // up the parents until we find the "real" root.
        while (     (pRootClassObject)
                 && (strcmp(_somGetName(pRootClassObject), pszRootClass) != 0)
              )
            pRootClassObject = _somGetParent(pRootClassObject);

        // now go thru SOMClassMgr's class list
        // (RegisteredClasses is a SOMClassSequence)
        for (ul=0; ul < RegisteredClasses._length; ul++)
        {
            SOMClass *pClass = RegisteredClasses._buffer[ul];

            // filter out unwanted classes; the SOM class
            // tree contains SOMClass, SOMObject, the interface
            // repository and all that, which we don't want,
            // so we include only descendants of the "root"
            // class
            if (_somDescendedFrom(pClass, pRootClassObject))
            {
                // set up WPSLISTITEM data
                PWPSLISTITEM pwpsNew = malloc(sizeof(WPSLISTITEM));
                pObjClass = pwpsciReturn->pObjClass;
                memset(pwpsNew, 0, sizeof(WPSLISTITEM));
                pwpsNew->pClassObject = pClass;
                pwpsNew->pszClassName = _somGetName(pClass);

                // are we currently working on the root class object?
                // If so, store it for later
                if (pwpsNew->pClassObject == pRootClassObject)
                    pwpsRoot = pwpsNew;

                // use WPS class list to find out the DLL
                while (pObjClass) {
                    if (strcmp(pwpsNew->pszClassName, pObjClass->pszClassName) == 0)
                        break;
                    else
                        pObjClass = pObjClass->pNext;
                }
                if (pObjClass)
                    pwpsNew->pszModName = pObjClass->pszModName;
                // else DLL = NULL; this happens if class loading
                // failed or if an undocumented WPS class does not
                // appear in the "official" WPS class list

                // get class's parent
                pwpsNew->pParentClassObject = _somGetParent(pClass);
                if (pwpsNew->pParentClassObject)
                    strcpy(pwpsNew->szParentClass,
                            _somGetName(pwpsNew->pParentClassObject));

                // mark this list item as "not processed"
                pwpsNew->fProcessed = FALSE;
                // append list item to list
                lstAppendItem((PLISTITEM*)&(pwpsciReturn->pwpsClassList), NULL,
                            (PLISTITEM)pwpsNew);
            }
        }

        // now insert the root record (normally WPObject),
        // this will then recurse; this also handles
        // class replacement info
        clsAddClassTree2Cnr(hwndCnr,
                pwpsRoot,
                pwpsciReturn,
                NULL,
                pscd);
    }

    return (pwpsciReturn);
}

/*
 *@@ clsCleanupWpsClasses:
 *      this cleans up the resources allocated by
 *      clsWpsClasses2Cnr (memory and SOM stuff);
 *      pwpsci must be the pointer returned
 *      by clsWpsClasses2Cnr
 */

VOID clsCleanupWpsClasses(PWPSCLASSESINFO pwpsci) // in: struct returned by clsWpsClasses2Cnr
{
    PWPSLISTITEM pItem, pItem2;

    pItem = pwpsci->pwpsClassList;
    while (pItem) {
        if (pItem->pszReplacedWithClasses)
            free(pItem->pszReplacedWithClasses);
        if (pItem->pszReplacesThisClass)
            free(pItem->pszReplacesThisClass);
        pItem2 = pItem->pNext;
        free(pItem);
        pItem = pItem2;
    } while (pItem);

    free(pwpsci->pObjClass);
    free(pwpsci);
}

/*
 *@@ fnwpSelectWPSClass:
 *      dlg proc for "Select class" dialog for selecting a
 *      class in single-object status bar mode
 */

MRESULT EXPENTRY fnwpSelectWPSClass(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc;
    PSELECTCLASSDATA pscd = (PSELECTCLASSDATA)WinQueryWindowULong(hwndDlg, QWL_USER);

    switch(msg) {

        /*
         * WM_INITDLG:
         *      set up the container data and colors
         */

        case WM_INITDLG: {
            CNRINFO CnrInfo;
            WinSetWindowULong(hwndDlg, QWL_USER, (ULONG)mp2);
            pscd = (PSELECTCLASSDATA)mp2;

            WinSetWindowText(hwndDlg, pscd->pszDlgTitle);
            WinSetDlgItemText(hwndDlg, ID_XSDI_SC_INTROTEXT, pscd->pszIntroText);

            // setup container
            pscd->hwndCnr = WinWindowFromID(hwndDlg, ID_XSDI_SC_CNR);
            WinSendMsg(pscd->hwndCnr, CM_QUERYCNRINFO, &CnrInfo, (MPARAM)sizeof(CnrInfo));
            CnrInfo.pSortRecord = (PVOID)fnCompareName;
            CnrInfo.flWindowAttr =
                    CV_TREE | CA_TREELINE | CV_TEXT
                    | CA_OWNERDRAW;
            CnrInfo.cxTreeIndent = 30;
            WinSendMsg(pscd->hwndCnr, CM_SETCNRINFO,
                    &CnrInfo,
                    (MPARAM)(CMA_PSORTRECORD | CMA_FLWINDOWATTR | CMA_CXTREEINDENT));

            // hide help button if panel was not specified
            if (pscd->ulHelpPanel == 0)
                winhShowDlgItem(hwndDlg, DID_HELP, FALSE);

            // get container colors for owner draw later
            pscd->lBackground =
                    winhQueryPresColor(hwndDlg, PP_BACKGROUNDCOLOR, "Window", "255 255 255");
            pscd->lText =
                    winhQueryPresColor(hwndDlg, PP_FOREGROUNDCOLOR, "WindowText", "0 0 0");
            pscd->lStaticText =
                    winhQueryPresColor(hwndDlg, -1, "ShadowText", "0 0 255");
            pscd->lSelBackground =
                    winhQueryPresColor(hwndDlg, PP_HILITEBACKGROUNDCOLOR, "HiliteBackground", "255 0 0");
            pscd->lSelText =
                    winhQueryPresColor(hwndDlg, PP_HILITEFOREGROUNDCOLOR, "HiliteForeground", "255 255 255");

            // fill container later
            WinPostMsg(hwndDlg, WM_FILLCNR, MPNULL, MPNULL);

            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        break; }

        /*
         * WM_FILLCNR:
         *      this fills the cnr with all the WPS classes
         */

        case WM_FILLCNR: {
            PRECORDCORE preccSelected;
            HPOINTER hptrOld = WinQueryPointer(HWND_DESKTOP);
            WinSetPointer(HWND_DESKTOP,
                    WinQuerySysPointer(HWND_DESKTOP, SPTR_WAIT, FALSE));
            pscd->pwpsc = clsWpsClasses2Cnr(pscd->hwndCnr, pscd->pszRootClass,
                    pscd);  // also contains callback
            // scroll cnr to the item that is selected
            preccSelected = WinSendMsg(pscd->hwndCnr, CM_QUERYRECORDEMPHASIS,
                    (MPARAM)CMA_FIRST,
                    (MPARAM)CRA_SELECTED);
            winhCnrScrollToRecord(pscd->hwndCnr, preccSelected,
                    CMA_TEXT, TRUE);
            WinSetPointer(HWND_DESKTOP, hptrOld);
        break; }

        /*
         * WM_DRAWITEM:
         *      container control owner draw: the
         *      cnr only allows the same color for
         *      all text items, so we need to draw
         *      the text ourselves
         */

        case WM_DRAWITEM: {
            // get generic DRAWITEM structure
            POWNERITEM poi = (POWNERITEM)mp2;

            // check if we're to draw the text
            // (and not the icon)
            if (poi->idItem == CMA_TEXT) {

                // get container-specific draw-item struct
                PCNRDRAWITEMINFO pcdii = (PCNRDRAWITEMINFO)poi->hItem;
                ULONG flCmd = DT_LEFT | DT_VCENTER | DT_ERASERECT;
                RECTL rcl2;

                // set draw colors
                LONG  lBackground = pscd->lBackground;
                LONG  lForeground = pscd->lText;

                // switch to RGB
                GpiCreateLogColorTable(poi->hps, 0,
                    LCOLF_RGB,
                    0, 0, NULL);

                if (((pcdii->pRecord->flRecordAttr) & CRA_DISABLED) == 0) {
                    // not disabled == valid WPS class
                    if ((pcdii->pRecord->flRecordAttr) & CRA_SELECTED) {
                        lBackground = pscd->lSelBackground;
                        lForeground = pscd->lSelText;
                    }
                } else {
                    if ((pcdii->pRecord->flRecordAttr) & CRA_SELECTED) {
                        lBackground = pscd->lStaticText;
                        lForeground = pscd->lBackground;
                    } else
                        lForeground = pscd->lStaticText;
                }

                memcpy(&rcl2, &(poi->rclItem), sizeof(rcl2));
                WinDrawText(poi->hps,
                    strlen(pcdii->pRecord->pszText),
                    pcdii->pRecord->pszText,
                    &rcl2,
                    lForeground,  // foreground
                    lBackground,
                    flCmd);
                mrc = (MPARAM)TRUE; // tell cnr that we've drawn the item
            } else
                mrc = (MPARAM)FALSE; // tell cnr to draw the item
        break; }

        /*
         * WM_CONTROL:
         *      capture cnr notifications
         */

        case WM_CONTROL: {
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
            if (SHORT1FROMMP(mp1) == ID_XSDI_SC_CNR) {
                switch (SHORT2FROMMP(mp1)) { // notify code

                    /*
                     * CN_ENTER:
                     *      double-click or "Enter" on
                     *      record core
                     */

                    case CN_ENTER: {
                        if (pscd->fSelectionValid) {
                            WinPostMsg(hwndDlg, WM_COMMAND,
                                    (MPARAM)DID_OK,
                                    MPNULL);
                        }
                    break; }

                    /*
                     * CN_EMPHASIS:
                     *      selection changed: call
                     *      callback func specified in
                     *      SELECTCLASSDATA
                     */

                    case CN_EMPHASIS: {
                        // get cnr notification struct
                        PNOTIFYRECORDEMPHASIS pnre = (PNOTIFYRECORDEMPHASIS)mp2;
                        if (pnre)
                            if (    (pnre->fEmphasisMask & CRA_SELECTED)
                                 && (pnre->pRecord)
                               )
                            {
                                pscd->preccSelection = (PCLASSRECORDCORE)(pnre->pRecord);
                                // per definition, this callback func must
                                // return TRUE if the OK button is to be
                                // enabled
                                if (pscd->pfnwpClassSelected) {
                                    pscd->fSelectionValid =
                                        (BOOL)(*(pscd->pfnwpClassSelected))(
                                            pscd->hwndCnr,
                                            pscd->ulUserClassSelected,
                                            // mp1: WPSLISTITEM struct
                                            (MPARAM)( ((PCLASSRECORDCORE)(pnre->pRecord))->pwps ),
                                            // mp2: hwnd of info static text control below cnr
                                            (MPARAM)( WinWindowFromID(hwndDlg, ID_XSDI_SC_TEXT2) )
                                        );
                                    // enable OK button according to
                                    // callback return value
                                    winhEnableDlgItem(hwndDlg, DID_OK, pscd->fSelectionValid);
                                }
                            }
                    break; }
                }
            }
        break; }

        /*
         * WM_COMMAND:
         *      if OK button was pressed (or CN_ENTER posted, see above),
         *      update the SELECTCLASSDATA struct
         */

        case WM_COMMAND: {
            switch (SHORT1FROMMP(mp1)) {
                case DID_OK: {
                    if (pscd->preccSelection)
                        strcpy(pscd->szClassSelected,
                                pscd->preccSelection->pwps->pszClassName);
                break; }
            }
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        break; }

        case WM_HELP: {
            if (pscd->ulHelpPanel) {
                _wpDisplayHelp(_wpclsQueryActiveDesktop(_WPDesktop),
                        pscd->ulHelpPanel,
                        pscd->pszHelpLibrary);
            }
        break; }

        case WM_DESTROY: {
            // cleanup
            clsCleanupWpsClasses(pscd->pwpsc);
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        break; }

        default:
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
    }
    return (mrc);
}

/*
 *@@ clsSelectWpsClassDlg:
 *      this is a one-shot func for displaying a
 *      "Select WPS class" dialog; you must fill
 *      the SELECTCLASSDATA struct with the
 *      necessary data. The class selected by
 *      the user will also be copied to this struct.
 *      This func returns either DID_OK or
 *      DID_CANCEL, in which case you shouldn't
 *      evaluate the return values in SELECTCLASSDATA.
 *      hmod and idDlg specify the dlg template for
 *      this func.
 */

ULONG clsSelectWpsClassDlg(HWND hwndOwner,
            HMODULE hmod, ULONG idDlg,
            PSELECTCLASSDATA pscd)
{
    return (WinDlgBox(HWND_DESKTOP, hwndOwner,
                            fnwpSelectWPSClass,
                            hmod, idDlg,
                            pscd));
}

/* ******************************************************************
 *                                                                  *
 *   "Register new class" dlg                                       *
 *                                                                  *
 ********************************************************************/

/*
 * fnwpOpenFilter:
 *      just a dummy.
 */

MRESULT EXPENTRY fnwpOpenFilter(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
   return (WinDefFileDlgProc(hwnd, msg, mp1, mp2));
}

/*
 * fnwpRegisterClass:
 *      dlg func for use with WinLoadDlg()
 */

MRESULT EXPENTRY fnwpRegisterClass(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc;
    PREGISTERCLASSDATA prcd = (PREGISTERCLASSDATA)WinQueryWindowULong(hwndDlg, QWL_USER);

    switch(msg) {

        /*
         * WM_INITDLG:
         *      set up the container data and colors
         */

        case WM_INITDLG: {
            // CNRINFO CnrInfo;
            WinSetWindowULong(hwndDlg, QWL_USER, (ULONG)mp2);
            prcd = (PREGISTERCLASSDATA)mp2;
            if (prcd->ulHelpPanel == 0)
                winhShowDlgItem(hwndDlg, DID_HELP, FALSE);
            WinSendDlgItemMsg(hwndDlg, ID_XSDI_SC_CLASSNAME, EM_SETTEXTLIMIT,
                (MPARAM)(255-1), MPNULL);
            WinSendDlgItemMsg(hwndDlg, ID_XSDI_SC_CLASSMODULE, EM_SETTEXTLIMIT,
                (MPARAM)(255-1), MPNULL);
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
        break; }

        case WM_CONTROL: {
                if ( (     (SHORT1FROMMP(mp1) == ID_XSDI_SC_CLASSNAME)
                        || (SHORT1FROMMP(mp1) == ID_XSDI_SC_CLASSMODULE)
                     )
                     && (SHORT2FROMMP(mp1) == EN_CHANGE)
                   )
                {
                    BOOL fEnable = FALSE;
                    WinQueryDlgItemText(hwndDlg, ID_XSDI_SC_CLASSNAME,
                            sizeof(prcd->szClassName), prcd->szClassName);
                    if (strlen(prcd->szClassName)) {
                        WinQueryDlgItemText(hwndDlg, ID_XSDI_SC_CLASSMODULE,
                                sizeof(prcd->szModName), prcd->szModName);
                        if (strlen(prcd->szClassName))
                            fEnable = TRUE;
                    }
                    winhEnableDlgItem(hwndDlg, DID_OK, fEnable);
                }

        break; }

        case WM_COMMAND: {
            switch (SHORT1FROMMP(mp1)) {
                case DID_OK: {
                    if (prcd) {
                        WinQueryDlgItemText(hwndDlg, ID_XSDI_SC_CLASSNAME,
                                sizeof(prcd->szClassName), prcd->szClassName);
                        WinQueryDlgItemText(hwndDlg, ID_XSDI_SC_CLASSMODULE,
                                sizeof(prcd->szModName), prcd->szModName);
                    }
                    mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
                break; }

                case ID_XSDI_SC_BROWSE: {
                    FILEDLG fd;
                    memset(&fd, 0, sizeof(FILEDLG));
                    fd.cbSize = sizeof(FILEDLG);
                    fd.fl = FDS_OPEN_DIALOG | FDS_CENTER;
                    fd.pfnDlgProc = fnwpOpenFilter;
                    // fd.pszTitle = "Blah";
                    // fd.pszOKButton = "~OK";
                    strcpy(fd.szFullFile, "*.DLL");
                    // fd.pszIDrive = "F:"
                    // fd.ulFQFCount = 1;
                    if (WinFileDlg(HWND_DESKTOP, hwndDlg, &fd) && (fd.lReturn == DID_OK)) {
                        // strcpy(prcd->szClassName, fd.szFullFile);
                        WinSetDlgItemText(hwndDlg, ID_XSDI_SC_CLASSMODULE,
                                    fd.szFullFile);
                    }
                break; }

                default:
                    mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
            }
        break; }

        case WM_HELP: {
            if (prcd->ulHelpPanel) {
                _wpDisplayHelp(_wpclsQueryActiveDesktop(_WPDesktop),
                        prcd->ulHelpPanel,
                        prcd->pszHelpLibrary);
            }
        break; }

        default:
            mrc = WinDefDlgProc(hwndDlg, msg, mp1, mp2);
    }
    return (mrc);
}


