
/*
 *@@sourcefile common.c:
 *      common.c contains functions that are common to all
 *      parts of XFolder. As opposed to /helpers/*, these
 *      functions work with XFolder only, while the helper
 *      functions will work with any PM application.
 *
 *      These functions mainly deal with the following features:
 *      -- global settings;
 *      -- NLS management;
 *      -- folder hotkeys;
 *      -- subclassing folder frame windows. The actual
 *         window proc for that is fnwpSubclassedFolderFrame
 *         in xfldr.c though;
 *      -- extended XFolder message boxes (cmnMessageBox);
 *      -- querying system sounds (cmnQuerySystemSound).
 *
 *@@include #define INCL_WINWINDOWMGR
 *@@include #define INCL_DOSMODULEMGR
 *@@include #include <os2.h>
 *@@include #include "xfldr.h"  // only for some functions
 *@@include #include "common.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_DOSMODULEMGR
#define INCL_DOSEXCEPTIONS      // needed for except.h
#define INCL_DOSSEMAPHORES      // needed for xthreads.h
#define INCL_DOSMISC            // DosGetMessage etc.
#define INCL_DOSERRORS

#define INCL_WINWINDOWMGR
#define INCL_WINSHELLDATA       // profile funcs
#define INCL_WINSYS             // presparams, WinQuerySysValue()
#define INCL_WININPUT           // WM_BUTTON1DOWN etc.

#define INCL_WINDIALOGS
#define INCL_WINBUTTONS
#define INCL_WINSTDCNR          // needed for winh.h
#define INCL_WINLISTBOXES

#define INCL_GPILOGCOLORTABLE

#include <os2.h>

// C library headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>             // mem*, str* functions
#include <setjmp.h>             // needed for except.h
#include <assert.h>             // needed for except.h
#include <io.h>

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

#include "linklist.h"           // linked list helper routines
#include "undoc.h"              // some undocumented stuff

// SOM headers which don't crash with prec. header files
#pragma hdrstop                 // VAC++ keeps crashing otherwise
#include "xfldr.h"

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

#include "module.h"             // XFolder main DLL information
#include "sound.h"              // declarations for SOUND.DLL
#include "except.h"             // XFolder exception handling
#include "statbars.h"           // status bar translation logic
#include "xshutdwn.h"           // needed for default settings
#include "xwps.h"               // XFolder pseudo SOM functions

// other SOM headers
#include  <wpdesk.h>            // WPDesktop


CHAR            szHelpLibrary[CCHMAXPATH] = "";
CHAR            szMessageFile[CCHMAXPATH] = "";

HMODULE         hmodNLS = NULLHANDLE;
PVOID           pNLSStringsGlobal = NULL;
PGLOBALSETTINGS pGlobalSettings = NULL;

HMODULE         hmodIconsDLL = NULLHANDLE;
CHAR            szLanguageCode[20] = "";

ULONG           ulCurHelpPanel = 0;      // holds help panel for dialog

CHAR            szStatusBarFont[100];
CHAR            szSBTextNoneSel[CCHMAXMNEMONICS],
                szSBTextMultiSel[CCHMAXMNEMONICS];
ULONG           ulStatusBarHeight;

/*-------------------------------------------------------------------
 *                                                                  *
 *   XFolder debugging helpers                                      *
 *                                                                  *
 ********************************************************************/

#ifdef _PMPRINTF_

/*
 *@@ cmnDumpMemoryBlock:
 *      if _PMPRINTF_ has been #define'd in common.h,
 *      this will dump a block of memory to the PMPRINTF
 *      output window. Useful for debugging internal
 *      structures.
 *      If _PMPRINTF_ has been NOT #define'd in common.h,
 *      no code will be produced. :-)
 */

void cmnDumpMemoryBlock(PBYTE pb,       // in: start address
                        ULONG ulSize,   // in: size of block
                        ULONG ulIndent) // in: how many spaces to put
                                        //     before each output line
{
    PBYTE   pbCurrent = pb;
    ULONG   ulCount = 0,
            ulLineCount = 0;
    CHAR    szLine[400] = "",
            szAscii[30] = "         ";
    PSZ     pszLine = szLine,
            pszAscii = szAscii;

    for (pbCurrent = pb;
         ulCount < ulSize;
         pbCurrent++, ulCount++)
    {
        if (ulLineCount == 0) {
            memset(szLine, ' ', ulIndent);
            pszLine += ulIndent;
        }
        pszLine += sprintf(pszLine, "%02lX ", *pbCurrent);

        if ((*pbCurrent > 31) && (*pbCurrent < 127))
            *pszAscii = *pbCurrent;
        else
            *pszAscii = '.';
        pszAscii++;

        ulLineCount++;
        if ( (ulLineCount > 7) || (ulCount == ulSize-1) )
        {
            _Pmpf(("%04lX:  %s  %s",
                    ulCount-7,
                    szLine,
                    szAscii));
            pszLine = szLine;
            pszAscii = szAscii;
            ulLineCount = 0;
        }
    }
}
#else
    // _PMPRINTF not #define'd: do nothing
    #define cmnDumpMemoryBlock(pb, ulSize, ulIndent)
#endif

/*-------------------------------------------------------------------
 *                                                                  *
 *   XFolder module handling                                        *
 *                                                                  *
 ********************************************************************/

/*
 *  The following routines are for querying the XFolder
 *  installation path and similiar routines, such as
 *  querying the current NLS module, changing it, loading
 *  strings, the help file and all that sort of stuff.
 */


/*
 *@@ cmnQueryXFolderBasePath:
 *      this routine returns the path of where XFolder was installed,
 *      i.e. the parent directory of where the xfldr.dll file
 *      resides, without a trailing backslash (e.g. "C:\XFolder").
 *      The buffer to copy this to is assumed to be CCHMAXPATH in size.
 *      As opposed to versions before V0.81, OS2.INI is no longer
 *      needed for this to work. The path is retrieved from the
 *      DLL directly by evaluating what was passed to _DLL_InitModule
 *      (module.*).
 */

BOOL cmnQueryXFolderBasePath(PSZ pszPath)
{
    BOOL brc = FALSE;
    PSZ pszDLL = modQueryFile();
    if (pszDLL) {
        // copy until last backslash minus four characters
        // (leave out "\bin\xfldr.dll")
        PSZ pszLastSlash = strrchr(pszDLL, '\\');
        #ifdef DEBUG_LANGCODES
            _Pmpf(( "modQueryFile: %s", pszDLL));
        #endif
        strncpy(pszPath, pszDLL, (pszLastSlash-pszDLL)-4);
        pszPath[(pszLastSlash-pszDLL-4)] = '\0';
        brc = TRUE;
    }
    #ifdef DEBUG_LANGCODES
        _Pmpf(( "cmnQueryXFolderBasePath: %s", pszPath ));
    #endif
    return (brc);
}

/*
 *@@ cmnQueryLanguageCode:
 *      returns PSZ to three-digit language code (e.g. "001").
 */

PSZ cmnQueryLanguageCode(VOID)
{
    if (szLanguageCode[0] == '\0')
        PrfQueryProfileString(HINI_USERPROFILE,
               INIAPP_XFOLDER, INIKEY_LANGUAGECODE,
               DEFAULT_LANGUAGECODE,
               (PVOID)szLanguageCode,
               sizeof(szLanguageCode));

    szLanguageCode[3] = '\0';
    #ifdef DEBUG_LANGCODES
        _Pmpf(( "cmnQueryLanguageCode: %s", szLanguageCode ));
    #endif
    return (szLanguageCode);
}

/*
 *@@ cmnSetLanguageCode:
 *      changes XFolder's language to three-digit language code in
 *      pszLanguage (e.g. "001"). This does not reload the NLS DLL,
 *      but only change the setting.
 */

BOOL cmnSetLanguageCode(PSZ pszLanguage)
{
    strcpy(szLanguageCode, pszLanguage);

    return (PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_LANGUAGECODE,
        szLanguageCode));
}

/*
 *@@ cmnQueryHelpLibrary:
 *      returns PSZ to full help library path in XFolder directory,
 *      depending on where XFolder was installed and on the current
 *      language (e.g. "C:\XFolder\help\xfldr001.hlp").
 */

PSZ cmnQueryHelpLibrary(VOID)
{
    if (cmnQueryXFolderBasePath(szHelpLibrary)) {
        // path found: append helpfile
        sprintf(szHelpLibrary+strlen(szHelpLibrary),
                "\\help\\xfldr%s.hlp",
                cmnQueryLanguageCode());
        #ifdef DEBUG_LANGCODES
            _Pmpf(( "cmnQueryHelpLibrary: %s", szHelpLibrary ));
        #endif
        return (szHelpLibrary);
    } else
        return (NULL);
}

/*
 *@@ cmnQueryMessageFile:
 *      returns PSZ to full message file path in XFolder directory,
 *      depending on where XFolder was installed and on the current
 *      language (e.g. "C:\XFolder\help\xfldr001.msg").
 */

PSZ cmnQueryMessageFile(VOID)
{
    if (cmnQueryXFolderBasePath(szMessageFile)) {
        // path found: append message file
        sprintf(szMessageFile+strlen(szMessageFile),
                "\\help\\xfldr%s.msg",
                cmnQueryLanguageCode());
        #ifdef DEBUG_LANGCODES
            _Pmpf(( "cmnQueryMessageFile: %s", szMessageFile));
        #endif
        return (szMessageFile);
    } else
        return (NULL);
}

/*
 *@@ cmnQueryIconsDLL:
 *      this returns the HMODULE of XFolder ICONS.DLL
 *      (new with V0.84).
 *      If this is queried for the first time, the DLL
 *      is loaded from the /BIN directory.
 *      In this case, this routine also checks if
 *      ICONS.DLL exists in the /ICONS directory. If
 *      so, it is copied to /BIN before loading the
 *      DLL. This allows for replacing the DLL using
 *      the REXX script in /ICONS.
 *
 *      New with V0.84.
 */

HMODULE cmnQueryIconsDLL(VOID)
{
    // first query?
    if (hmodIconsDLL == NULLHANDLE)
    {
        CHAR    szIconsDLL[CCHMAXPATH],
                szIconsDLL2[CCHMAXPATH];
        cmnQueryXFolderBasePath(szIconsDLL);
        strcpy(szIconsDLL2, szIconsDLL);

        sprintf(szIconsDLL+strlen(szIconsDLL),
                "\\bin\\icons.dll");
        sprintf(szIconsDLL2+strlen(szIconsDLL2),
                "\\icons\\icons.dll");

        // first check for /ICONS/ICONS.DLL
        if (access(szIconsDLL2, 0) == 0)
        {
            // exists: then move to /BIN
            DosDelete(szIconsDLL);
            DosMove(szIconsDLL2, szIconsDLL);
        }

        // now load /BIN/ICONS.DLL
        if (DosLoadModule(NULL,
                           0,
                           szIconsDLL,
                           &hmodIconsDLL))
            hmodIconsDLL = NULLHANDLE;
    }
    return (hmodIconsDLL);
}

/*
 *@@ cmnLoadString:
 *      pretty similar to WinLoadString, but allocates
 *      necessary memory as well. *ppsz is a pointer
 *      to a PSZ; if this PSZ is != NULL, whatever it
 *      points to will be free()d, so you should set this
 *      to NULL if you initially call this function.
 *      This is used at WPS startup and when XFolder's
 *      language is changed later to load all the strings
 *      from a NLS DLL.
 */

void cmnLoadString(HAB habDesktop, HMODULE hmodResource, ULONG ulID, PSZ *ppsz)
{
    CHAR    szBuf[200];
    if (*ppsz)
        free(*ppsz);
    if (WinLoadString(habDesktop, hmodResource, ulID , sizeof(szBuf), szBuf))
        *ppsz = strcpy(malloc(strlen(szBuf)+1), szBuf);
    else
        *ppsz = "Error: String resource not found";
}

/*
 *@@ cmnQueryNLSModuleHandle:
 *      returns the module handle of the language-dependent XFolder
 *      national language support DLL.
 *
 *      This is called in two situations:
 *          1) with fEnforceReload == FALSE everytime some part
 *             of XFolder needs the NLS resources (e.g. for dialogs);
 *             on the first call, the NLS DLL is loaded into memory.
 *          2) with fEnforceReload == TRUE when the user changes
 *             XFolder's language in the "Workplace Shell" object.
 *
 *      If the DLL is (re)loaded, this function also initializes
 *      all language-dependent XFolder parts such as the NLSSTRINGS
 *      structure, which can always be accessed using cmnQueryNLSStrings.
 *      This function also checks for whether the NLS DLL has a
 *      decent version level to support this XFolder version.
 */

HMODULE cmnQueryNLSModuleHandle(BOOL fEnforceReload)
{
    CHAR    szResourceModuleName[CCHMAXPATH];
    ULONG   ulCopied;
    HMODULE hmodOldResource = hmodNLS;

    // load resource DLL if it's not loaded yet or a reload is enforced
    if ((hmodNLS == NULLHANDLE) || (fEnforceReload))
    {
        PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();

        // get the XFolder path first
        if (cmnQueryXFolderBasePath(szResourceModuleName))
        {
            // now compose module name from language code
            strcat(szResourceModuleName, "\\bin\\xfldr");
            strcat(szResourceModuleName, cmnQueryLanguageCode());
            strcat(szResourceModuleName, ".dll");

            // try to load the module
            if (DosLoadModule(NULL,
                               0,
                               szResourceModuleName,
                               &hmodNLS))
            {
                DebugBox("XFolder: Couldn't Find Resource DLL",
                    szResourceModuleName);
            } else {
                // module loaded alright!
                HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);

                if (habDesktop)
                {
                    if (fEnforceReload) {
                        // if fEnforceReload == TRUE, we will load a test string from
                        // the module to see if it has at least the version level which
                        // this XFolder version requires. This is done using a #define
                        // in dlgids.h: XFOLDER_VERSION is compiled into both the NLS
                        // DLL (as a string resource) and into the main DLL (this file),
                        // so we always have the versions in there automatically.
                        // MINIMUM_NLS_VERSION (dlgids.h too) contains the minimum
                        // NLS version level that this XFolder version requires.
                        CHAR   szTest[30] = "";
                        LONG   lLength;
                        lLength = WinLoadString(habDesktop,
                                hmodNLS,
                                ID_XSSI_XFOLDERVERSION,
                                sizeof(szTest), szTest);
                        #ifdef DEBUG_LANGCODES
                            _Pmpf(("%s version: %s", szResourceModuleName, szTest));
                        #endif

                        if (    (lLength == 0)
                            ||  (memcmp(szTest, MINIMUM_NLS_VERSION, 4) < 0)
                                    // szTest has NLS version (e.g. "0.81 beta"),
                                    // MINIMUM_NLS_VERSION has minimum version required
                                    // (e.g. "0.90")
                           )
                        {
                            DosBeep(1500, 100);
                            cmnSetHelpPanel(-1);
                            if (lLength == 0) {
                                // version string not found: complain
                                DebugBox("XFolder", "The requested file is not an XFolder National Language Support DLL.");
                                DosFreeModule(hmodNLS);
                                hmodNLS = hmodOldResource;
                                return NULLHANDLE;
                            }
                            else
                            {
                                // version level not sufficient:
                                // load dialog from previous NLS DLL which says
                                // that the DLL is too old; if user presses
                                // "Cancel", we abort loading the DLL
                                DosBeep(1700, 100);
                                if (WinDlgBox(HWND_DESKTOP,
                                         HWND_DESKTOP,
                                         (PFNWP)fnwpDlgGeneric,
                                         hmodOldResource,
                                         ID_XFD_WRONGVERSION,
                                         (PVOID)NULL)
                                   == DID_CANCEL)
                                {
                                    DebugBox("XFolder", "The new National Language Support DLL was not loaded.");
                                    // unload new NLS DLL
                                    DosFreeModule(hmodNLS);
                                    hmodNLS = hmodOldResource;
                                    return NULLHANDLE;
                                }
                            }
                        }
                    }

                    // now let's load strings
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_NOTDEFINED, &(pNLSStrings->pszNotDefined));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_PRODUCTINFO, &(pNLSStrings->pszProductInfo));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_REFRESHNOW, &(pNLSStrings->pszRefreshNow));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SNAPTOGRID, &(pNLSStrings->pszSnapToGrid));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_FLDRCONTENT, &(pNLSStrings->pszFldrContent));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_COPYFILENAME, &(pNLSStrings->pszCopyFilename));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_BORED       , &(pNLSStrings->pszBored));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_FLDREMPTY   , &(pNLSStrings->pszFldrEmpty));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SELECTSOME  , &(pNLSStrings->pszSelectSome));

                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_QUICKSTATUS    , &(pNLSStrings->pszQuickStatus));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_NAME     , &(pNLSStrings->pszSortByName));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_TYPE     , &(pNLSStrings->pszSortByType));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_CLASS    , &(pNLSStrings->pszSortByClass));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_REALNAME , &(pNLSStrings->pszSortByRealName));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_SIZE     , &(pNLSStrings->pszSortBySize));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_WRITEDATE, &(pNLSStrings->pszSortByWriteDate));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_ACCESSDATE, &(pNLSStrings->pszSortByAccessDate));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_CREATIONDATE, &(pNLSStrings->pszSortByCreationDate));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_EXT       , &(pNLSStrings->pszSortByExt));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_FOLDERSFIRST, &(pNLSStrings->pszSortFoldersFirst));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_CTRL       , &(pNLSStrings->pszCtrl));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_Alt        , &(pNLSStrings->pszAlt));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_SHIFT      , &(pNLSStrings->pszShift));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_BACKSPACE  , &(pNLSStrings->pszBackspace));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_TAB        , &(pNLSStrings->pszTab));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_BACKTABTAB , &(pNLSStrings->pszBacktab));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_ENTER      , &(pNLSStrings->pszEnter));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_ESC        , &(pNLSStrings->pszEsc));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_SPACE      , &(pNLSStrings->pszSpace));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_PAGEUP     , &(pNLSStrings->pszPageup));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_PAGEDOWN   , &(pNLSStrings->pszPagedown));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_END        , &(pNLSStrings->pszEnd));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_HOME       , &(pNLSStrings->pszHome));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_LEFT       , &(pNLSStrings->pszLeft));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_UP         , &(pNLSStrings->pszUp));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_RIGHT      , &(pNLSStrings->pszRight));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_DOWN       , &(pNLSStrings->pszDown));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_PRINTSCRN  , &(pNLSStrings->pszPrintscrn));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_INSERT     , &(pNLSStrings->pszInsert));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_DELETE     , &(pNLSStrings->pszDelete));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_SCRLLOCK   , &(pNLSStrings->pszScrlLock));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_NUMLOCK    , &(pNLSStrings->pszNumLock));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_WINLEFT    , &(pNLSStrings->pszWinLeft));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_WINRIGHT   , &(pNLSStrings->pszWinRight));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_KEY_WINMENU    , &(pNLSStrings->pszWinMenu));

                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_FLUSHING       , &(pNLSStrings->pszSDFlushing));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_CAD            , &(pNLSStrings->pszSDCAD));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_REBOOTING      , &(pNLSStrings->pszSDRebooting));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_CLOSING        , &(pNLSStrings->pszSDClosing));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_SHUTDOWN       , &(pNLSStrings->pszShutdown));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_RESTARTWPS     , &(pNLSStrings->pszRestartWPS));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_RESTARTINGWPS  , &(pNLSStrings->pszSDRestartingWPS));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_SAVINGDESKTOP  , &(pNLSStrings->pszSDSavingDesktop));
                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_SAVINGPROFILES , &(pNLSStrings->pszSDSavingProfiles));

                    cmnLoadString(habDesktop, hmodNLS, ID_SDSI_STARTING       , &(pNLSStrings->pszStarting));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_POPULATING     , &(pNLSStrings->pszPopulating));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_1GENERIC       , &(pNLSStrings->psz1Generic));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_2REMOVEITEMS   , &(pNLSStrings->psz2RemoveItems));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_25ADDITEMS     , &(pNLSStrings->psz25AddItems));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_26CONFIGITEMS  , &(pNLSStrings->psz26ConfigFolderMenus));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_27STATUSBAR    , &(pNLSStrings->psz27StatusBar));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_3SNAPTOGRID    , &(pNLSStrings->psz3SnapToGrid));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_4ACCELERATORS  , &(pNLSStrings->psz4Accelerators));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_5INTERNALS     , &(pNLSStrings->psz5Internals));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_FILEOPS        , &(pNLSStrings->pszFileOps));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SORT           , &(pNLSStrings->pszSort));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SV_ALWAYSSORT  , &(pNLSStrings->pszAlwaysSort));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_INTERNALS      , &(pNLSStrings->pszInternals));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SB_CLASSMNEMONICS   , &(pNLSStrings->pszSBClassMnemonics));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SB_CLASSNOTSUPPORTED, &(pNLSStrings->pszSBClassNotSupported));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPSCLASSES     , &(pNLSStrings->pszWpsClasses));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPSCLASSLOADED , &(pNLSStrings->pszWpsClassLoaded));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPSCLASSLOADINGFAILED, &(pNLSStrings->pszWpsClassLoadingFailed));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPSCLASSREPLACEDBY, &(pNLSStrings->pszWpsClassReplacedBy));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPSCLASSORPHANS   , &(pNLSStrings->pszWpsClassOrphans));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPSCLASSORPHANSINFO, &(pNLSStrings->pszWpsClassOrphansInfo));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_SCHEDULER      , &(pNLSStrings->pszScheduler));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_MEMORY         , &(pNLSStrings->pszMemory));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_ERRORS         , &(pNLSStrings->pszErrors));
                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_WPS            , &(pNLSStrings->pszWPS));

                    cmnLoadString(habDesktop, hmodNLS, ID_XSSI_PROCESSCONTENT , &(pNLSStrings->pszProcessContent));

                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_SETTINGS       , &(pNLSStrings->pszSettings));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_SETTINGSNOTEBOOK,&(pNLSStrings->pszSettingsNotebook));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_ATTRIBUTES     , &(pNLSStrings->pszAttributes));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_ATTR_ARCHIVED  , &(pNLSStrings->pszAttrArchived));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_ATTR_SYSTEM    , &(pNLSStrings->pszAttrSystem));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_ATTR_HIDDEN    , &(pNLSStrings->pszAttrHidden));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_ATTR_READONLY  , &(pNLSStrings->pszAttrReadOnly));

                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_FLDRSETTINGS   , &(pNLSStrings->pszWarp3FldrView));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_SMALLICONS     , &(pNLSStrings->pszSmallIcons  ));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_FLOWED         , &(pNLSStrings->pszFlowed));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_NONFLOWED      , &(pNLSStrings->pszNonFlowed));
                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_NOGRID         , &(pNLSStrings->pszNoGrid));

                    cmnLoadString(habDesktop, hmodNLS, ID_XFSI_SHOWSTATUSBAR  , &(pNLSStrings->pszShowStatusBar));
                }

                // after all this, unload the old resource module
                DosFreeModule(hmodOldResource);

            } // end else

        }
    }

    // return (new?) module handle
    return (hmodNLS);
}

/*
 *@@ cmnQueryNLSStrings:
 *      returns pointer to global NLSSTRINGS structure which contains
 *      all the language-dependent XFolder strings from the resource
 *      files.
 */

PNLSSTRINGS cmnQueryNLSStrings(VOID)
{
    if (pNLSStringsGlobal == NULL) {
        pNLSStringsGlobal = malloc(sizeof(NLSSTRINGS));
        memset(pNLSStringsGlobal, 0, sizeof(NLSSTRINGS));
    }
    return (pNLSStringsGlobal);
}

/*-------------------------------------------------------------------
 *                                                                  *
 *   XFolder Global Settings                                        *
 *                                                                  *
 ********************************************************************/

/*
 *@@ cmnQueryStatusBarSetting:
 *      returns a PSZ to a certain status bar setting, which
 *      may be:
 *      --      SBS_STATUSBARFONT       font (e.g. "9.WarpSans")
 *      --      SBS_TEXTNONESEL         mnemonics for no-object mode
 *      --      SBS_TEXTMULTISEL        mnemonics for multi-object mode
 *
 *      Note that there is no key for querying the mnemonics for
 *      one-object mode, because this is now handled by the
 *      functions in statbars.c
 *      to provide different data depending on the
 *      class of the selected object.
 */

PSZ cmnQueryStatusBarSetting(USHORT usSetting)
{
    switch (usSetting) {
        case SBS_STATUSBARFONT:
                return (szStatusBarFont);
        case SBS_TEXTNONESEL:
                return (szSBTextNoneSel);
        case SBS_TEXTMULTISEL:
                return (szSBTextMultiSel);
        default:
                return (NULL);
    }
}

/*
 *@@ cmnSetStatusBarSetting:
 *      sets usSetting to pszSetting. If pszSetting == NULL, the
 *      default value will be loaded from the XFolder NLS DLL.
 *      usSetting works just like in cmnQueryStatusBarSetting.
 */

BOOL cmnSetStatusBarSetting(USHORT usSetting, PSZ pszSetting)
{
    HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
    HMODULE hmodResource = cmnQueryNLSModuleHandle(FALSE);

    switch (usSetting) {
        case SBS_STATUSBARFONT: {
                CHAR szDummy[CCHMAXMNEMONICS];
                if (pszSetting) {
                    strcpy(szStatusBarFont, pszSetting);
                    PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_STATUSBARFONT,
                             szStatusBarFont);
                } else
                    strcpy(szStatusBarFont, "8.Helv");
                sscanf(szStatusBarFont, "%d.%s", &(ulStatusBarHeight), &szDummy);
                ulStatusBarHeight += 15;
        break; }
        case SBS_TEXTNONESEL: {
                if (pszSetting) {
                    strcpy(szSBTextNoneSel, pszSetting);
                    PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_SBTEXTNONESEL,
                             szSBTextNoneSel);
                } else
                    WinLoadString(habDesktop, hmodResource, ID_XSSI_SBTEXTNONESEL,
                        sizeof(szSBTextNoneSel), szSBTextNoneSel);
        break; }
        case SBS_TEXTMULTISEL: {
                if (pszSetting) {
                    strcpy(szSBTextMultiSel, pszSetting);
                    PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_SBTEXTMULTISEL,
                             szSBTextMultiSel);
                } else
                    WinLoadString(habDesktop, hmodResource, ID_XSSI_SBTEXTMULTISEL,
                        sizeof(szSBTextMultiSel), szSBTextMultiSel);
        break; }
        default:
                return (FALSE);
    }
    return (TRUE);
}

/*
 *@@ cmnQueryStatusBarHeight:
 *      returns the height of the status bars according to the
 *      current settings in pixels. This was calculated when
 *      the status bar font was set.
 */

ULONG cmnQueryStatusBarHeight(VOID)
{
    return (ulStatusBarHeight);
}

/*
 *@@ cmnLoadGlobalSettings:
 *      this loads the Global Settings from the INI files; should
 *      not be called directly, because this is done automatically
 *      by cmnQueryGlobalSettings, if necessary.
 */

PGLOBALSETTINGS cmnLoadGlobalSettings(VOID)
{
    ULONG       ulCopied1;

    if (pGlobalSettings == NULL) {
        pGlobalSettings = malloc(sizeof(GLOBALSETTINGS));
        memset(pGlobalSettings, 0, sizeof(GLOBALSETTINGS));
    }

    // first set default settings for each settings page;
    // we only load the "real" settings from OS2.INI afterwards
    // because the user might have updated XFolder, and the
    // settings struct in the INIs might be too short
    cmnSetDefaultSettings(SP_1GENERIC              );
    cmnSetDefaultSettings(SP_2REMOVEITEMS          );
    cmnSetDefaultSettings(SP_25ADDITEMS            );
    cmnSetDefaultSettings(SP_26CONFIGITEMS         );
    cmnSetDefaultSettings(SP_27STATUSBAR           );
    cmnSetDefaultSettings(SP_3SNAPTOGRID           );
    cmnSetDefaultSettings(SP_4ACCELERATORS         );
    cmnSetDefaultSettings(SP_5INTERNALS            );
    cmnSetDefaultSettings(SP_DTP1                  );
    cmnSetDefaultSettings(SP_DTP2                  );
    cmnSetDefaultSettings(SP_FLDRSORT_GLOBAL       );
    cmnSetDefaultSettings(SP_FILEOPS               );

    // set global settings w/out settings page
    pGlobalSettings->ShowBootupStatus = 0;
    pGlobalSettings->WpsShowClassInfo = 1;

    PrfQueryProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_STATUSBARFONT,
             "8.Helv", &(szStatusBarFont), sizeof(szStatusBarFont));
    sscanf(szStatusBarFont, "%d.*%s", &(ulStatusBarHeight));
    ulStatusBarHeight += 15;

    PrfQueryProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_SBTEXTNONESEL,
                NULL, &(szSBTextNoneSel), sizeof(szSBTextNoneSel));
    PrfQueryProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_SBTEXTMULTISEL,
                NULL, &(szSBTextMultiSel), sizeof(szSBTextMultiSel));

    ulCopied1 = sizeof(GLOBALSETTINGS);
    // get global XFolder settings from OS2.INI
    PrfQueryProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_GLOBALSETTINGS,
                pGlobalSettings, &ulCopied1);

    return (pGlobalSettings);
}

/*
 *@@ cmnQueryGlobalSettings:
 *      returns pointer to the GLOBALSETTINGS structure which
 *      contains the XFolder Global Settings valid for all
 *      classes. Loads the settings from the INI files if this
 *      hasn't been done yet. Used all the time throughout XFolder.
 */

PGLOBALSETTINGS cmnQueryGlobalSettings(VOID)
{
    if (pGlobalSettings == NULL) {
        cmnLoadGlobalSettings();
    }

    return (pGlobalSettings);
}

/*
 *@@ cmnStoreGlobalSettings:
 *      stores the current Global Settings back into the INI files;
 *      returns TRUE if successful.
 */

BOOL cmnStoreGlobalSettings(VOID)
{
    return (PrfWriteProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_GLOBALSETTINGS,
        pGlobalSettings, sizeof(GLOBALSETTINGS)));
}

/*
 *@@ cmnSetDefaultSettings:
 *      resets those Global Settings which correspond to usSettingsPage
 *      in the System notebook to the default values; usSettingsPage
 *      may have 1 thru the number of XFolder Settings pages in the
 *      System notebook (SP_* flags). This is used to initialize
 *      XFolder settings at startup and by the "Default" buttons on
 *      the notebook settings pages.
 */

BOOL cmnSetDefaultSettings(USHORT usSettingsPage)
{
    // HMODULE hmodResource = cmnQueryNLSModuleHandle(FALSE);

    switch(usSettingsPage) {
        case SP_1GENERIC: {
            pGlobalSettings->ShowInternals = 1;
            pGlobalSettings->ReplIcons = 1;
            pGlobalSettings->FullPath = 1;
            pGlobalSettings->KeepTitle = 1;
            pGlobalSettings->MaxPathChars = 25;
            pGlobalSettings->TreeViewAutoScroll = 1;
        break; }

        case SP_2REMOVEITEMS: {
            pGlobalSettings->DefaultMenuItems = 0;
            pGlobalSettings->RemoveLockInPlaceItem = 0;
            pGlobalSettings->RemoveCheckDiskItem = 0;
            pGlobalSettings->RemoveFormatDiskItem = 0;
            pGlobalSettings->RemoveViewMenu = 0;
            pGlobalSettings->RemovePasteItem = 0;
        break; }

        case SP_25ADDITEMS: {
            pGlobalSettings->FileAttribs = 1;
            pGlobalSettings->AddCopyFilenameItem = 1;
            pGlobalSettings->ExtendFldrViewMenu = 1;
            pGlobalSettings->MoveRefreshNow = (doshIsWarp4() ? 1 : 0);
            pGlobalSettings->AddSelectSomeItem = 1;
            pGlobalSettings->AddFolderContentItem = 1;
            pGlobalSettings->FCShowIcons = 0;
        break; }

        case SP_26CONFIGITEMS: {
            pGlobalSettings->MenuCascadeMode = 0;
            pGlobalSettings->RemoveX = 1;
            pGlobalSettings->AppdParam = 1;
            pGlobalSettings->TemplatesOpenSettings = BM_INDETERMINATE;
            pGlobalSettings->TemplatesReposition = 1;
        break; }

        case SP_27STATUSBAR: {
            pGlobalSettings->StatusBar = 0;
            pGlobalSettings->SBStyle = (doshIsWarp4() ? SBSTYLE_WARP4MENU : SBSTYLE_WARP3RAISED);
            pGlobalSettings->SBForViews = SBV_ICON | SBV_DETAILS;
            pGlobalSettings->lSBBgndColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_INACTIVEBORDER, 0);
            pGlobalSettings->lSBTextColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_OUTPUTTEXT, 0);
            cmnSetStatusBarSetting(SBS_TEXTNONESEL, NULL);
            cmnSetStatusBarSetting(SBS_TEXTMULTISEL, NULL);
        break; }

        case SP_3SNAPTOGRID: {
            pGlobalSettings->AddSnapToGridItem = 1;
            pGlobalSettings->GridX = 15;
            pGlobalSettings->GridY = 10;
            pGlobalSettings->GridCX = 20;
            pGlobalSettings->GridCY = 35;
        break; }

        case SP_4ACCELERATORS: {
            pGlobalSettings->Accelerators = 1;
        break; }

        case SP_5INTERNALS: {
            pGlobalSettings->VarMenuOffset = 300;
            pGlobalSettings->NoWorkerThread = 0;
            pGlobalSettings->NoSubclassing = 0;
            pGlobalSettings->ShowXFolderAnim = 1;
        break; }

        case SP_DTP1: { // shutdown page
            pGlobalSettings->ulXShutdownFlags =
                XSD_RESTARTWPS | XSD_WPS_CLOSEWINDOWS | XSD_CONFIRM | XSD_REBOOT | XSD_ANIMATE;
        break; }

        case SP_DTP2: {
            pGlobalSettings->ShowStartupProgress = 1;
            pGlobalSettings->ulStartupDelay = 1000;
        break; }

        case SP_FLDRSORT_GLOBAL: {
            pGlobalSettings->ReplaceSort = 0;
            pGlobalSettings->DefaultSort = SV_NAME;
            pGlobalSettings->AlwaysSort = 0;
        break; }

        case SP_FILEOPS: {
            pGlobalSettings->ReplConfirms = 1;
            pGlobalSettings->CleanupINIs = 1;
        break; }
    }

    return (TRUE);
}

/*-------------------------------------------------------------------
 *                                                                  *
 *   Folder hotkey functions                                        *
 *                                                                  *
 ********************************************************************/

/*
 *  The folder hotkeys are stored in a static array which
 *  is is FLDRHOTKEYCOUNT items in size. This is calculated
 *  at compile time according to the items which were
 *  declared in dlgids.h.
 *
 *  The following functions give the other XFolder parts
 *  access to this data and/or manipulate it.
 */

// XFolder folder hotkeys static array
XFLDHOTKEY     FolderHotkeys[FLDRHOTKEYCOUNT];

/*
 *@@ cmnQueryFldrHotkeys:
 *      this returns the address of the static
 *      folder hotkeys array in common.c. The
 *      size of that array is FLDRHOTKEYSSIZE (common.h).
 */

PXFLDHOTKEY cmnQueryFldrHotkeys(VOID) {
    return (&FolderHotkeys[0]);
}

/*
 *@@ cmnLoadDefaultFldrHotkeys:
 *      this resets the folder hotkeys to the default
 *      values.
 */

void cmnLoadDefaultFldrHotkeys(VOID)
{
    // Ctrl+A: Select all
    FolderHotkeys[0].usKeyCode  = (USHORT)'a';
    FolderHotkeys[0].usFlags    = KC_CTRL;
    FolderHotkeys[0].usCommand  = WPMENUID_SELALL;

    // F5
    FolderHotkeys[1].usKeyCode  = VK_F5;
    FolderHotkeys[1].usFlags    = KC_VIRTUALKEY;
    FolderHotkeys[1].usCommand  = WPMENUID_REFRESH,

    // Backspace
    FolderHotkeys[2].usKeyCode  = VK_BACKSPACE;
    FolderHotkeys[2].usFlags    = KC_VIRTUALKEY;
    FolderHotkeys[2].usCommand  = ID_XFMI_OFS_OPENPARENT;

    //  Ctrl+D: De-select all
    FolderHotkeys[3].usKeyCode  = (USHORT)'d';
    FolderHotkeys[3].usFlags    = KC_CTRL;
    FolderHotkeys[3].usCommand  = WPMENUID_DESELALL;

    // Ctrl+Shift+D: Details view
    FolderHotkeys[4].usKeyCode  = (USHORT)'D';
    FolderHotkeys[4].usFlags    = KC_CTRL+KC_SHIFT;
    FolderHotkeys[4].usCommand  = WPMENUID_DETAILS;

    // Ctrl+Shift+I: Icon  view
    FolderHotkeys[5].usKeyCode  = (USHORT)'I';
    FolderHotkeys[5].usFlags    = KC_CTRL+KC_SHIFT;
    FolderHotkeys[5].usCommand  = WPMENUID_ICON;

    // Ctrl+Shift+S: Open Settings
    FolderHotkeys[6].usKeyCode  = (USHORT)'S';
    FolderHotkeys[6].usFlags    = KC_CTRL+KC_SHIFT;
    FolderHotkeys[6].usCommand  = WPMENUID_PROPERTIES;

    // Ctrl+N: Sort by name
    FolderHotkeys[7].usKeyCode  = (USHORT)'n';
    FolderHotkeys[7].usFlags    = KC_CTRL;
    FolderHotkeys[7].usCommand  = ID_WPMI_SORTBYNAME;

    // Ctrl+Z: Sort by size
    FolderHotkeys[8].usKeyCode  = (USHORT)'z';
    FolderHotkeys[8].usFlags    = KC_CTRL;
    FolderHotkeys[8].usCommand  = ID_WPMI_SORTBYSIZE;

    // Ctrl+E: Sort by extension (NPS)
    FolderHotkeys[9].usKeyCode  = (USHORT)'e';
    FolderHotkeys[9].usFlags    = KC_CTRL;
    FolderHotkeys[9].usCommand  = ID_XFMI_OFS_SORTBYEXT;

    // Ctrl+W: Sort by write date
    FolderHotkeys[10].usKeyCode  = (USHORT)'w';
    FolderHotkeys[10].usFlags    = KC_CTRL;
    FolderHotkeys[10].usCommand  = ID_WPMI_SORTBYWRITEDATE;

    // Ctrl+Y: Sort by type
    FolderHotkeys[11].usKeyCode  = (USHORT)'y';
    FolderHotkeys[11].usFlags    = KC_CTRL;
    FolderHotkeys[11].usCommand  = ID_WPMI_SORTBYTYPE;

    // Shift+Backspace
    FolderHotkeys[12].usKeyCode  = VK_BACKSPACE;
    FolderHotkeys[12].usFlags    = KC_VIRTUALKEY+KC_SHIFT;
    FolderHotkeys[12].usCommand  = ID_XFMI_OFS_OPENPARENTANDCLOSE;

    // Ctrl+S: Select by name
    FolderHotkeys[13].usKeyCode  = (USHORT)'s';
    FolderHotkeys[13].usFlags    = KC_CTRL;
    FolderHotkeys[13].usCommand  = ID_XFMI_OFS_SELECTSOME;

    // Ctrl+insert: copy filename (w/out path)
    FolderHotkeys[14].usKeyCode  = VK_INSERT;
    FolderHotkeys[14].usFlags    = KC_VIRTUALKEY+KC_CTRL;
    FolderHotkeys[14].usCommand  = ID_XFMI_OFS_COPYFILENAME_SHORT;

    FolderHotkeys[15].usCommand = 0;
}

/*
 *@@ cmnLoadFolderHotkeys:
 *      this initializes the folder hotkey array with
 *      the data which was previously stored in OS2.INI.
 */

void cmnLoadFolderHotkeys(VOID)
{
    ULONG ulCopied2 = sizeof(FolderHotkeys);
    if (!PrfQueryProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_ACCELERATORS,
                &FolderHotkeys, &ulCopied2))
        cmnLoadDefaultFldrHotkeys();
}

/*
 *@@ cmnStoreFldrHotkeys:
 *       this stores the folder hotkeys in OS2.INI.
 */

void cmnStoreFldrHotkeys(VOID)
{
    SHORT i2 = 0;

    // store only the accels that are actually used
    while (FolderHotkeys[i2].usCommand)
        i2++;

    PrfWriteProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_ACCELERATORS,
        &FolderHotkeys, (i2+1) * sizeof(XFLDHOTKEY));
}

/*
 *@@ cmnProcessFldrHotkey:
 *      this is called by the subclassed folder frame wnd proc
 *      to check for whether a given WM_CHAR message matches
 *      one of the folder hotkeys.
 *
 *      The parameters are those of the WM_CHAR message. This
 *      returns TRUE if the pressed key was a hotkey; in that
 *      case, the corresponding WM_COMMAND message is
 *      automatically posted to the folder frame, which will
 *      cause the defined action to occur.
 */

BOOL cmnProcessFldrHotkey(HWND hwndFrame, MPARAM mp1, MPARAM mp2)
{
    USHORT  us;

    USHORT usFlags    = SHORT1FROMMP(mp1);
    USHORT usch       = SHORT1FROMMP(mp2);
    USHORT usvk       = SHORT2FROMMP(mp2);
    USHORT usKeyCode  = 0;

    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();

    // now check if the key is relevant: filter out KEY UP
    // messages and check if either a virtual key (such as F5)
    // or Ctrl or Alt was pressed
    if (    ((usFlags & KC_KEYUP) == 0)
        &&  (     ((usFlags & KC_VIRTUALKEY) != 0)
                  // Ctrl pressed?
               || ((usFlags & KC_CTRL) != 0)
                  // Alt pressed?
               || ((usFlags & KC_ALT) != 0)
                  // or one of the Win95 keys?
               || (   ((usFlags & KC_VIRTUALKEY) == 0)
                   && (     (usch == 0xEC00)
                        ||  (usch == 0xED00)
                        ||  (usch == 0xEE00)
                      )
                  )
            )
       )
    {

        if (usFlags & KC_VIRTUALKEY)
            usKeyCode = usvk;
        else
            usKeyCode = usch;

        usFlags &= (KC_VIRTUALKEY | KC_CTRL | KC_ALT | KC_SHIFT);
        us = 0;

        #ifdef DEBUG_KEYS
            _Pmpf(("cmnProcessFldrHotkey: usKeyCode: 0x%lX, usFlags: 0x%lX", usKeyCode, usFlags));
        #endif

        // now go through the global accelerator list and check
        // if the pressed key was assigned an action to
        while (FolderHotkeys[us].usCommand)
        {
            USHORT usCommand;
            if (
                   (  (FolderHotkeys[us].usFlags == usFlags)
                   && (FolderHotkeys[us].usKeyCode == usKeyCode)
                   )
               )
            {   // OK: this is a hotkey; find the corresponding
                // "command" (= menu ID) and post it to the frame
                // window, which will execute it
                usCommand = FolderHotkeys[us].usCommand;
                if ( (usCommand >= WPMENUID_USER) &&
                     (usCommand < WPMENUID_USER+FIRST_VARIABLE) )
                        usCommand += pGlobalSettings->VarMenuOffset;
                WinPostMsg(hwndFrame,
                        WM_COMMAND,
                        (MPARAM)usCommand,
                        MPFROM2SHORT(CMDSRC_MENU,
                                FALSE) );     // results from keyboard operation

                #ifdef DEBUG_KEYS
                    _Pmpf(("  Posting command 0x%lX", usCommand));
                #endif

                return (TRUE);
            }
            us++;
        }
    }
    return (FALSE);
}

/*
 *@@ cmnDescribeKey:
 *      this stores a description of a certain
 *      key into pszBuf, using the NLS DLL strings.
 *      usFlags must contain usFlags from WM_CHAR,
 *      usKeyCode must be either usch (char code)
 *      or, with virtual keys, usvk (vk code).
 *      Returns TRUE if this was a valid key combo.
 */

BOOL cmnDescribeKey(PSZ pszBuf, USHORT usFlags, USHORT usKeyCode)
{
    BOOL rc = TRUE;

    PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();

    *pszBuf = 0;
    if (usFlags & KC_CTRL)
        strcpy(pszBuf, pNLSStrings->pszCtrl);
    if (usFlags & KC_SHIFT)
        strcat(pszBuf, pNLSStrings->pszShift);
    if (usFlags & KC_ALT)
        strcat(pszBuf, pNLSStrings->pszAlt);

    if (usFlags & KC_VIRTUALKEY) {
        switch (usKeyCode) {
            case VK_BACKSPACE: strcat(pszBuf, pNLSStrings->pszBackspace); break;
            case VK_TAB: strcat(pszBuf, pNLSStrings->pszTab); break;
            case VK_BACKTAB: strcat(pszBuf, pNLSStrings->pszBacktab); break;
            case VK_NEWLINE: strcat(pszBuf, pNLSStrings->pszEnter); break;
            case VK_ESC: strcat(pszBuf, pNLSStrings->pszEsc); break;
            case VK_SPACE: strcat(pszBuf, pNLSStrings->pszSpace); break;
            case VK_PAGEUP: strcat(pszBuf, pNLSStrings->pszPageup); break;
            case VK_PAGEDOWN: strcat(pszBuf, pNLSStrings->pszPagedown); break;
            case VK_END: strcat(pszBuf, pNLSStrings->pszEnd); break;
            case VK_HOME: strcat(pszBuf, pNLSStrings->pszHome); break;
            case VK_LEFT: strcat(pszBuf, pNLSStrings->pszLeft); break;
            case VK_UP: strcat(pszBuf, pNLSStrings->pszUp); break;
            case VK_RIGHT: strcat(pszBuf, pNLSStrings->pszRight); break;
            case VK_DOWN: strcat(pszBuf, pNLSStrings->pszDown); break;
            case VK_PRINTSCRN: strcat(pszBuf, pNLSStrings->pszPrintscrn); break;
            case VK_INSERT: strcat(pszBuf, pNLSStrings->pszInsert); break;
            case VK_DELETE: strcat(pszBuf, pNLSStrings->pszDelete); break;
            case VK_SCRLLOCK: strcat(pszBuf, pNLSStrings->pszScrlLock); break;
            case VK_NUMLOCK: strcat(pszBuf, pNLSStrings->pszNumLock); break;
            case VK_ENTER: strcat(pszBuf, pNLSStrings->pszEnter); break;
            case VK_F1: strcat(pszBuf, "F1"); break;
            case VK_F2: strcat(pszBuf, "F2"); break;
            case VK_F3: strcat(pszBuf, "F3"); break;
            case VK_F4: strcat(pszBuf, "F4"); break;
            case VK_F5: strcat(pszBuf, "F5"); break;
            case VK_F6: strcat(pszBuf, "F6"); break;
            case VK_F7: strcat(pszBuf, "F7"); break;
            case VK_F8: strcat(pszBuf, "F8"); break;
            case VK_F9: strcat(pszBuf, "F9"); break;
            case VK_F10: strcat(pszBuf, "F10"); break;
            case VK_F11: strcat(pszBuf, "F11"); break;
            case VK_F12: strcat(pszBuf, "F12"); break;
            case VK_F13: strcat(pszBuf, "F13"); break;
            case VK_F14: strcat(pszBuf, "F14"); break;
            case VK_F15: strcat(pszBuf, "F15"); break;
            case VK_F16: strcat(pszBuf, "F16"); break;
            case VK_F17: strcat(pszBuf, "F17"); break;
            case VK_F18: strcat(pszBuf, "F18"); break;
            case VK_F19: strcat(pszBuf, "F19"); break;
            case VK_F20: strcat(pszBuf, "F20"); break;
            case VK_F21: strcat(pszBuf, "F21"); break;
            case VK_F22: strcat(pszBuf, "F22"); break;
            case VK_F23: strcat(pszBuf, "F23"); break;
            case VK_F24: strcat(pszBuf, "F24"); break;
            default: break;
        }
    } // end virtualkeys
    else {
        switch (usKeyCode) {
            case 0xEC00: strcat(pszBuf, pNLSStrings->pszWinLeft); break;
            case 0xED00: strcat(pszBuf, pNLSStrings->pszWinRight); break;
            case 0xEE00: strcat(pszBuf, pNLSStrings->pszWinMenu); break;
            default: {
                CHAR szTemp[2];
                if (usKeyCode >= 'a')
                    szTemp[0] = (CHAR)usKeyCode-32;
                else
                    szTemp[0] = (CHAR)usKeyCode;
                szTemp[1] = '\0';
                strcat(pszBuf, szTemp);
            }
        }
    }

    #ifdef DEBUG_KEYS
        _Pmpf(("Key: %s, usKeyCode: 0x%lX, usFlags: 0x%lX", pszBuf, usKeyCode, usFlags));
    #endif

    return (rc);
}

/*-------------------------------------------------------------------
 *                                                                  *
 *   Folder frame window subclassing                                *
 *                                                                  *
 ********************************************************************/

// root of linked list to remember original window
// procedures when subclassing frame windows for folder hotkeys;
// this list is maintained by wpOpen of XFolder
PSUBCLASSEDLISTITEM psliSubclassed = NULL;
// mutex semaphore for access to this list
HMTX                hmtxSubclassed = NULLHANDLE;

/*
 *@@ cmnInitPSLI:
 *      this is called once from M_XFolder::wpclsInitData
 *      to initialize the SUBCLASSEDLISTITEM list.
 */

VOID cmnInitPSLI(VOID)
{
    if (DosCreateMutexSem(NULL,
                        &hmtxSubclassed, 0, FALSE) != NO_ERROR)
    {
        DosBeep(100, 300);
        hmtxSubclassed = -1;
    }
}

/*
 *@@ cmnQueryPSLI:
 *      this retrieves the PSUBCLASSEDLISTITEM from the
 *      global linked list of subclassed frame windows,
 *      according to a given frame wnd handle. One of these
 *      structs is maintained for each open folder view
 *      to store window data which is needed everywhere.
 */

PSUBCLASSEDLISTITEM cmnQueryPSLI(HWND hwndFrame)
{
    PSUBCLASSEDLISTITEM psli = NULL;
    BOOL    fSemOwned = FALSE;

    TRY_QUIET(excpt1)
    {
        if (hwndFrame)
        {
            fSemOwned = (DosRequestMutexSem(hmtxSubclassed, 4000) == NO_ERROR);
            if (fSemOwned)
            {
                psli = psliSubclassed;
                while (psli) {
                    if (psli->hwndFrame == hwndFrame) {
                        /* pfnwpLast = pfnwpOriginal;
                        hwndLast = hwndFrame;
                        somSelfLast = somSelf; */
                        break; // while
                    }
                    else
                        psli = psli->pNext;
                }

                DosReleaseMutexSem(hmtxSubclassed);
                fSemOwned = FALSE;
            }
        }
    }
    CATCH(excpt1)
    {
        if (fSemOwned) {
            DosReleaseMutexSem(hmtxSubclassed);
            fSemOwned = FALSE;
        }
    } END_CATCH;

    return (psli);
}

/*
 *@@ cmnSubclassFolderFrame:
 *      this routine is used by both XFolder::wpOpen and
 *      XFldDisk::wpOpen to subclass a new frame window
 *      to allow for extra XFolder features.
 *
 *      For this, this routines maintains a global linked
 *      list of subclassed windows (SUBCLASSEDLISTITEM
 *      structures), whose first item is stored in the global
 *      psliSubclassed variable. This is protected internally
 *      by a mutex semaphore; you should therefore always
 *      use cmnQueryPSLI to access that list.
 *
 *      Every list item contains (among other things) the
 *      original wnd proc of the subclassed window, which the
 *      subclassed wnd proc (fnwpSubclassedFolderFrame, xfldr.c)
 *      needs to call the the original folder wnd proc for a
 *      given frame window, because these procs might differ
 *      depending on the class or view type or installed WPS
 *      enhancers.
 *
 *      These list items are also used for various other frame
 *      data, such as the handles of the status bar and
 *      supplementary object windows.
 *      We cannot store this data elsewhere, because QWL_USER
 *      in the wnd data seems to be used by the WPS already.
 *
 *      Note: when called from XFolder::wpOpen, somSelf and
 *      pRealObject both point to the folder. However, when
 *      called from XFldDisk::wpOpen, somSelf has the
 *      disk object's root folder, while pRealObject has the
 *      disk object. Both are stored in the SUBCLASSEDLISTITEM
 *      structure.
 *
 *      This func returns the newly created SUBCLASSEDLISTITEM.
 */

PSUBCLASSEDLISTITEM cmnSubclassFolderFrame(HWND hwndFrame,
                                                // in: frame wnd of new view (returned by wpOpen)
                                           XFolder *somSelf,
                                                // in: folder; either XFolder's somSelf
                                                // or XFldDisk's root folder
                                           WPObject *pRealObject,
                                                // in: the "real" object; for XFolder, this is == somSelf,
                                                // for XFldDisk, this is the disk object (needed for object handles)
                                           ULONG ulView)
                                                // OPEN_CONTENTS et al.
{
    BOOL                fSemOwned = FALSE;
    PFNWP               pfnwpCurrent, pfnwpOriginal;
    HWND                hwndCnr;
    PSUBCLASSEDLISTITEM pNewItem = NULL;
    PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();

    TRY_QUIET(excpt1)
    {
        // subclass only if the user hasn't disabled
        // subclassing globally
        if (pGlobalSettings->NoSubclassing == 0)
        {
            // exclude other views, such as settings notebooks and
            // possible user views
            if (    (hwndFrame)
                 && (   (ulView == OPEN_CONTENTS)
                     || (ulView == OPEN_TREE)
                     || (ulView == OPEN_DETAILS)
                    )
               )
            {
                // get container wnd handle
                hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);
                if (hwndCnr)
                // only subclass frame window if it contains a container;
                // just a security check
                {
                    // now check if frame wnd has already been subclassed;
                    // just another security check
                    pfnwpCurrent = (PFNWP)WinQueryWindowPtr(hwndFrame, QWP_PFNWP);
                    if (pfnwpCurrent != (PFNWP)fnwpSubclassedFolderFrame)
                    {
                        if (!(pfnwpOriginal = WinSubclassWindow(hwndFrame, (PFNWP)fnwpSubclassedFolderFrame)))
                            // error occured subclassing:
                            DebugBox("XFolder", "Folder subclassing failed.");
                            // should never happen
                        else
                        {
                            // subclassing succeeded: now remember the old wnd proc
                            // and other data in the global linked list
                            pNewItem = malloc(sizeof(SUBCLASSEDLISTITEM));
                            pNewItem->pfnwpOriginal = pfnwpOriginal;
                            // store various other data here
                            pNewItem->hwndFrame = hwndFrame;
                            pNewItem->somSelf = somSelf;
                            pNewItem->pRealObject = pRealObject;
                            pNewItem->hwndCnr = hwndCnr;
                            pNewItem->ulView = ulView;
                            pNewItem->fRemoveSrcEmphasis = FALSE;
                            // set status bar hwnd to zero at this point;
                            // this will be created elsewhere
                            pNewItem->hwndStatusBar = NULLHANDLE;
                            // create a supplementary object window
                            // for this folder frame (see
                            // fnwpSupplObject for details)
                            pNewItem->hwndSupplObject = WinCreateWindow(
                                           HWND_OBJECT,
                                           WNDCLASS_SUPPLOBJECT, // class name
                                           (PSZ)"SupplObject",     // title
                                           0,           // style
                                           0,0,0,0,     // position
                                           0,           // owner
                                           HWND_BOTTOM, // z-order
                                           0,           // window id
                                           pNewItem,    // pass the struct to WM_CREATE
                                           NULL);       // pres params
                            // append to global list
                            fSemOwned = (DosRequestMutexSem(hmtxSubclassed, 4000) == NO_ERROR);
                            if (fSemOwned) {
                                lstAppendItem((PLISTITEM*)&psliSubclassed, NULL,
                                            (PLISTITEM)pNewItem);
                                DosReleaseMutexSem(hmtxSubclassed);
                                fSemOwned = FALSE;
                            }
                        }
                    }
                }
            }
        }
    }
    CATCH(excpt1)
    {
        if (fSemOwned) {
            DosReleaseMutexSem(hmtxSubclassed);
            fSemOwned = FALSE;
        }
    } END_CATCH;
    return (pNewItem);
}

/*
 *@@ cmnRemovePSLI:
 *      reverse to cmnSubclassFolderFrame, this removes
 *      a PSUBCLASSEDLISTITEM from the global list again.
 *      Called upon WM_CLOSE in folder frames.
 */

VOID cmnRemovePSLI(PSUBCLASSEDLISTITEM psli)
{
    BOOL fSemOwned = FALSE;

    TRY_QUIET(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxSubclassed, 4000) == NO_ERROR);
        if (fSemOwned) {
            lstRemoveItem((PLISTITEM*)&psliSubclassed, NULL,
                    (PLISTITEM)psli);
            DosReleaseMutexSem(hmtxSubclassed);
            fSemOwned = FALSE;
        }
    }
    CATCH(excpt1)
    {
        if (fSemOwned) {
            DosReleaseMutexSem(hmtxSubclassed);
            fSemOwned = FALSE;
        }
    } END_CATCH;
}

/*-------------------------------------------------------------------
 *                                                                  *
 *   Miscellaneous                                                  *
 *                                                                  *
 ********************************************************************/

/*
 *@@ fnwpAutoSizeStatic:
 *      dlg proc for the subclassed static text control in msg box;
 *      automatically resizes the msg box window when text changes
 */

PFNWP pfnwpOrigStatic = NULL;

MRESULT EXPENTRY fnwpAutoSizeStatic(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc = NULL;
    switch (msg) {

        /*
         * WM_SETLONGTEXT:
         *      this arrives here when the window text changes,
         *      especially when fnwpMessageBox sets the window
         *      text; we will now reposition all the controls.
         *      mp1 is a PSZ to the new text.
         */

        case WM_SETLONGTEXT: {
            CHAR szFont[300];
            PSZ p = (PSZ)WinQueryWindowULong(hwndStatic, QWL_USER);
            if (p)
                free(p);
            p = strdup((PSZ)mp1);
            WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)p);
            // WinPostMsg(hwndStatic, WM_UPDATE, MPNULL, MPNULL);
            PrfQueryProfileString(HINI_USER, INIAPP_XFOLDER, INIKEY_DLGFONT, "9.WarpSans",
                szFont, sizeof(szFont)-1);
            WinSetPresParam(hwndStatic, PP_FONTNAMESIZE,
                 (ULONG)strlen(szFont) + 1, (PVOID)szFont);
            // this will also update the display

            /* PSZ pwp = (PWNDPARAMS)mp1;
            _Pmpf(( "WM_SETWINDOWPARAMS" ));
            mrc = (*pfnwpOrigStatic)(hwndStatic, msg, mp1, mp2);
            if (pwp) {
            } */
        break; }

        /*
         * WM_PRESPARAMCHANGED:
         *      if a font has been dropped on the window, store
         *      the information and enforce control repositioning
         *      also
         */

        case WM_PRESPARAMCHANGED: {
            // _Pmpf(( "PRESPARAMCHANGED" ));
            switch ((ULONG)mp1) {
                case PP_FONTNAMESIZE: {
                    ULONG attrFound; //, abValue[32];
                    CHAR  szFont[100];
                    HWND  hwndDlg = WinQueryWindow(hwndStatic, QW_PARENT);
                    WinQueryPresParam(hwndStatic,
                                PP_FONTNAMESIZE,
                                0,
                                &attrFound,
                                (ULONG)sizeof(szFont),
                                (PVOID)&szFont,
                                0);
                    PrfWriteProfileString(HINI_USER, INIAPP_XFOLDER, INIKEY_DLGFONT,
                        szFont);

                    // now also change the buttons
                    WinSetPresParam(WinWindowFromID(hwndDlg, 1),
                        PP_FONTNAMESIZE,
                        (ULONG)strlen(szFont) + 1, (PVOID)szFont);
                    WinSetPresParam(WinWindowFromID(hwndDlg, 2),
                        PP_FONTNAMESIZE,
                        (ULONG)strlen(szFont) + 1, (PVOID)szFont);
                    WinSetPresParam(WinWindowFromID(hwndDlg, 3),
                        PP_FONTNAMESIZE,
                        (ULONG)strlen(szFont) + 1, (PVOID)szFont);

                    WinPostMsg(hwndStatic, WM_UPDATE, MPNULL, MPNULL);
                break; }
            }
        break; }

        /*
         * WM_UPDATE:
         *      this actually does all the calculations to reposition
         *      all the msg box controls
         */

        case WM_UPDATE: {
            RECTL   rcl, rcl2;
            SWP     swp;
            HWND    hwndIcon, hwndDlg;
            PSZ     pszText;

            HPS hps = WinGetPS(hwndStatic);

            // _Pmpf(( "WM_UPDATE" ));

            pszText = (PSZ)WinQueryWindowULong(hwndStatic, QWL_USER);
            if (pszText) {
                WinQueryWindowRect(hwndStatic, &rcl);         // get window dimensions
                // rcl.yTop = 1000;
                memcpy(&rcl2, &rcl, sizeof(rcl));

                winhDrawFormattedText(hps, &rcl, pszText, DT_LEFT | DT_TOP | DT_QUERYEXTENT);
                WinEndPaint(hps);

                if ((rcl.yTop - rcl.yBottom) < 40)
                    rcl.yBottom -= 40 - (rcl.yTop - rcl.yBottom);

                // printfRtl("  Original rect:", &rcl2);
                // printfRtl("  New rect:     ", &rcl);

                // reposition the text
                WinQueryWindowPos(hwndStatic, &swp);
                swp.cy -= rcl.yBottom;
                WinSetWindowPos(hwndStatic, 0,
                    swp.x, swp.y, swp.cx, swp.cy,
                    SWP_SIZE);

                // reposition the icon
                hwndIcon = WinWindowFromID(WinQueryWindow(hwndStatic, QW_PARENT),
                                ID_XFDI_GENERICDLGICON);
                WinQueryWindowPos(hwndIcon, &swp);
                swp.y -= rcl.yBottom;
                WinSetWindowPos(hwndIcon, 0,
                    swp.x, swp.y, swp.cx, swp.cy,
                    SWP_MOVE);

                // resize the dlg frame window
                hwndDlg = WinQueryWindow(hwndStatic, QW_PARENT);
                WinQueryWindowPos(hwndDlg, &swp);
                swp.cy -= rcl.yBottom;
                swp.y  += (rcl.yBottom / 2);
                WinInvalidateRect(hwndDlg, NULL, FALSE);
                WinSetWindowPos(hwndDlg, 0,
                    swp.x, swp.y, swp.cx, swp.cy,
                    SWP_SIZE | SWP_MOVE);
            }
        break; }

        /*
         * WM_PAINT:
         *      draw the formatted text
         */

        case WM_PAINT: {
            RECTL   rcl;
            PSZ pszText = (PSZ)WinQueryWindowULong(hwndStatic, QWL_USER);
            HPS hps = WinBeginPaint(hwndStatic, NULLHANDLE, &rcl);
            if (pszText) {
                WinQueryWindowRect(hwndStatic, &rcl);         // get window dimensions
                // switch to RGB mode
                GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
                // set "static text" color
                GpiSetColor(hps,
                        WinQuerySysColor(HWND_DESKTOP, SYSCLR_WINDOWSTATICTEXT, 0));
                winhDrawFormattedText(hps, &rcl, pszText, DT_LEFT | DT_TOP);
            }
            WinEndPaint(hps);
        break; }

        case WM_DESTROY: {
            PSZ pszText = (PSZ)WinQueryWindowULong(hwndStatic, QWL_USER);
            free(pszText);
            mrc = (*pfnwpOrigStatic)(hwndStatic, msg, mp1, mp2);
        break; }
        default:
            mrc = (*pfnwpOrigStatic)(hwndStatic, msg, mp1, mp2);
    }
    return (mrc);
}

/*
 *@@ fnwpMessageBox:
 *      dlg proc for cmnMessageBox below
 */

MRESULT EXPENTRY fnwpMessageBox(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc = NULL;
    switch (msg) {
        case WM_INITDLG: {
            // CHAR szFont[CCHMAXPATH];
            HWND hwndStatic = WinWindowFromID(hwndDlg,
                                ID_XFDI_GENERICDLGTEXT);

            /* PrfQueryProfileString(HINI_USER, INIAPP_XFOLDER, INIKEY_DLGFONT,
                    "9.WarpSans", szFont, sizeof(szFont)-1);
            WinSetPresParam(hwndStatic, PP_FONTNAMESIZE,
                 (ULONG)strlen(szFont)+1, (PVOID)szFont); */

            // set string to 0
            WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)NULL);
            pfnwpOrigStatic = WinSubclassWindow(hwndStatic,
                    fnwpAutoSizeStatic);
            mrc = fnwpDlgGeneric(hwndDlg, msg, mp1, mp2);
        break; }

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

/*
 *@@ cmnMessageBox:
 *      this is the generic function for displaying XFolder
 *      message boxes. This is very similar to WinMessageBox,
 *      but a few new features are introduced:
 *      -- an XFolder icon is displayed;
 *      -- fonts can be dropped on the window, upon which the
 *         window will resize itself and store the font in OS2.INI.
 *
 *      Returns MBID_* codes like WinMessageBox.
 */

ULONG cmnMessageBox(HWND hwndOwner,     // in: owner
                    PSZ pszTitle,       // in: msgbox title
                    PSZ pszMessage,     // in: msgbox text
                    ULONG flStyle)      // in: MB_* flags
{
    HAB     hab = WinQueryAnchorBlock(hwndOwner);
    HMODULE hmod = NLS_MODULE;
    HWND    hwndDlg = NULLHANDLE, hwndStatic = NULLHANDLE;
    PSZ     pszButton = NULL;
    ULONG   ulrcDlg, ulrc = DID_CANCEL;

    REGREC2               RegRec2;
    ULONG                 ulExcpt;
    APIRET                rc;

    // set our extended exception handler
    RegRec2.pfnHandler = (PFN)excHandlerQuiet;
    if ( rc = DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&RegRec2) )
        DosBeep(100, 1000);
    // store a known thread state
    ulExcpt = setjmp(RegRec2.jmpThread);
    if (ulExcpt)
    {
        DosBeep(100, 1000);
    } else {
        ULONG flButtons = flStyle & 0xF;        // low nibble contains MB_YESNO etc.

        hwndDlg = WinLoadDlg(HWND_DESKTOP, hwndOwner,
                        fnwpMessageBox,
                        hmod,
                        ID_XFD_GENERICDLG,
                        NULL);

        hwndStatic = WinWindowFromID(hwndDlg, ID_XFDI_GENERICDLGTEXT);

        // now work on the three buttons of the dlg template:
        // give them proper titles or hide them
        if (flButtons == MB_YESNO) {
            cmnLoadString(hab, hmod, ID_XSSI_DLG_YES, &pszButton);
            WinSetDlgItemText(hwndDlg, 1, pszButton);
            cmnLoadString(hab, hmod, ID_XSSI_DLG_NO, &pszButton);
            WinSetDlgItemText(hwndDlg, 2, pszButton);
            winhShowDlgItem(hwndDlg, 3, FALSE);
            free(pszButton);
        }
        else if (flButtons == MB_OK) {
            cmnLoadString(hab, hmod, ID_XSSI_DLG_OK, &pszButton);
            WinSetDlgItemText(hwndDlg, 1, pszButton);
            winhShowDlgItem(hwndDlg, 2, FALSE);
            winhShowDlgItem(hwndDlg, 3, FALSE);
            free(pszButton);
        }
        else if (flButtons == MB_CANCEL) {
            cmnLoadString(hab, hmod, ID_XSSI_DLG_CANCEL, &pszButton);
            WinSetDlgItemText(hwndDlg, 1, pszButton);
            winhShowDlgItem(hwndDlg, 2, FALSE);
            winhShowDlgItem(hwndDlg, 3, FALSE);
            free(pszButton);
        }
        else if (flButtons == MB_OKCANCEL) {
            cmnLoadString(hab, hmod, ID_XSSI_DLG_OK, &pszButton);
            WinSetDlgItemText(hwndDlg, 1, pszButton);
            cmnLoadString(hab, hmod, ID_XSSI_DLG_CANCEL, &pszButton);
            WinSetDlgItemText(hwndDlg, 2, pszButton);
            winhShowDlgItem(hwndDlg, 3, FALSE);
            free(pszButton);
        }
        else if (flButtons == MB_RETRYCANCEL) {
            cmnLoadString(hab, hmod, ID_XSSI_DLG_RETRY, &pszButton);
            WinSetDlgItemText(hwndDlg, 1, pszButton);
            cmnLoadString(hab, hmod, ID_XSSI_DLG_CANCEL, &pszButton);
            WinSetDlgItemText(hwndDlg, 2, pszButton);
            winhShowDlgItem(hwndDlg, 3, FALSE);
            free(pszButton);
        }
        else if (flButtons == MB_ABORTRETRYIGNORE) {
            cmnLoadString(hab, hmod, ID_XSSI_DLG_ABORT, &pszButton);
            WinSetDlgItemText(hwndDlg, 1, pszButton);
            cmnLoadString(hab, hmod, ID_XSSI_DLG_RETRY, &pszButton);
            WinSetDlgItemText(hwndDlg, 2, pszButton);
            cmnLoadString(hab, hmod, ID_XSSI_DLG_IGNORE, &pszButton);
            WinSetDlgItemText(hwndDlg, 3, pszButton);
            free(pszButton);
        }

        if (flStyle & MB_DEFBUTTON2)
            WinSetWindowBits(WinWindowFromID(hwndDlg, 2), QWL_STYLE,
                    BS_DEFAULT,
                    1);
        else if (flStyle & MB_DEFBUTTON3)
            WinSetWindowBits(WinWindowFromID(hwndDlg, 3), QWL_STYLE,
                    BS_DEFAULT,
                    1);
        else
            WinSetWindowBits(WinWindowFromID(hwndDlg, 1), QWL_STYLE,
                    BS_DEFAULT,
                    1);

        WinSetWindowText(hwndDlg, pszTitle);
        WinSendMsg(hwndStatic, WM_SETLONGTEXT, pszMessage, MPNULL);

        winhCenterWindow(hwndDlg);
        if (flStyle & MB_SYSTEMMODAL)
            WinSetSysModalWindow(HWND_DESKTOP, hwndDlg);
        ulrcDlg = WinProcessDlg(hwndDlg);
        WinDestroyWindow(hwndDlg);

        if (flButtons == MB_YESNO)
            switch (ulrcDlg) {
                case 1:     ulrc = MBID_YES; break;
                default:    ulrc = MBID_NO;  break;
            }
        else if (flButtons == MB_OK)
            ulrc = MBID_OK;
        else if (flButtons == MB_CANCEL)
            ulrc = MBID_CANCEL;
        else if (flButtons == MB_OKCANCEL)
            switch (ulrcDlg) {
                case 1:     ulrc = MBID_OK; break;
                default:    ulrc = MBID_CANCEL;  break;
            }
        else if (flButtons == MB_RETRYCANCEL)
            switch (ulrcDlg) {
                case 1:     ulrc = MBID_RETRY; break;
                default:    ulrc = MBID_CANCEL;  break;
            }
        else if (flButtons == MB_ABORTRETRYIGNORE)
            switch (ulrcDlg) {
                case 2:     ulrc = MBID_RETRY;  break;
                case 3:     ulrc = MBID_IGNORE; break;
                default:    ulrc = MBID_ABORT; break;
            }
    }

    DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&RegRec2);

    return (ulrc);
}

/*
 *@@ cmnGetMessage:
 *      like DosGetMessage, but automatically uses the
 *      (NLS) XFolder message file.
 *      The parameters are exactly like with DosGetMessage.
 */

APIRET cmnGetMessage(PCHAR *pTable,     // in: replacement PSZ table or NULL
                     ULONG ulTable,     // in: size of that table or 0
                     PSZ pszBuf,        // out: buffer to hold message string
                     ULONG cbBuf,       // in: size of pszBuf
                     ULONG ulMsgNumber) // in: msg number to retrieve
{
    PSZ     pszMessageFile = cmnQueryMessageFile();
    ULONG   ulReturned;
    BOOL    fCont = FALSE;
    APIRET  arc;
    arc = DosGetMessage(pTable, ulTable,
                        pszBuf, cbBuf,
                        ulMsgNumber, pszMessageFile, &ulReturned);
    pszBuf[ulReturned] = '\0';

    // remove trailing newlines
    do {
        PSZ p = pszBuf + strlen(pszBuf) - 1;
        if (    (*p == '\n')
             || (*p == '\r')
           )
        {
            *p = '\0';
            fCont = TRUE;
        } else
            fCont = FALSE;
    } while (fCont);

    return (arc);
}

/*
 *@@ cmnMessageBoxMsg:
 *      calls cmnMessageBox, but this one accepts ULONG indices
 *      into the XFolder message file (XFLDRxxx.MSG) instead
 *      of real PSZs. This calls cmnGetMessage for retrieving
 *      the messages, but placeholder replacement does not work
 *      here (use cmnMessageBoxMsgExt for that).
 */

ULONG cmnMessageBoxMsg(HWND hwndOwner,
                       ULONG ulTitle,       // in: msg index for dlg title
                       ULONG ulMessage,     // in: msg index for message
                       ULONG flStyle)       // in: like cmnMsgBox
{
    CHAR    szTitle[200], szMessage[2000];

    cmnGetMessage(NULL, 0,
            szTitle, sizeof(szTitle)-1,
            ulTitle);
    cmnGetMessage(NULL, 0,
            szMessage, sizeof(szMessage)-1,
            ulMessage);

    return (cmnMessageBox(hwndOwner, szTitle, szMessage, flStyle));
}

/*
 *@@ cmnMessageBoxMsgExt:
 *      like cmnMessageBoxMsg, but with string substitution
 *      (see cmnGetMessage for more); substitution only
 *      takes place for the message specified with ulMessage,
 *      not for the title.
 */

ULONG cmnMessageBoxMsgExt(HWND hwndOwner,   // in: owner window
                          ULONG ulTitle,    // in: msg number for title
                          PCHAR *pTable,    // in: replacement table for ulMessage
                          ULONG ulTable,    // in: sizeof *pTable
                          ULONG ulMessage,  // in: msg number for message
                          ULONG flStyle)    // in: msg box style flags (cmnMessageBox)
{
    CHAR    szTitle[200], szMessage[2000];

    cmnGetMessage(NULL, 0,
            szTitle, sizeof(szTitle)-1,
            ulTitle);
    cmnGetMessage(pTable, ulTable,
            szMessage, sizeof(szMessage)-1,
            ulMessage);

    return (cmnMessageBox(hwndOwner, szTitle, szMessage, flStyle));
}

/*
 *@@ cmnSetHelpPanel:
 *      sets help panel before calling fnwpDlgGeneric.
 */

VOID cmnSetHelpPanel(ULONG ulHelpPanel)
{
    ulCurHelpPanel = ulHelpPanel;
}

/*
 *@@  fnwpDlgGeneric:
 *          this is the dlg procedure for XFolder dlg boxes;
 *          it can process WM_HELP messages
 */

MRESULT EXPENTRY fnwpDlgGeneric(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    HOBJECT hobjRef = 0;
    HWND    hwndItem;
    MRESULT mrc = NULL;

    switch (msg) {
        case WM_INITDLG:
            if (hwndItem = WinWindowFromID(hwnd, ID_XFDI_XFLDVERSION)) {
                CHAR    szTemp[100];
                WinQueryWindowText(hwndItem, sizeof(szTemp), szTemp);
                strcpy(strstr(szTemp, "%a"), XFOLDER_VERSION);
                WinSetWindowText(hwndItem, szTemp);
            }
            mrc = WinDefDlgProc(hwnd, msg, mp1, mp2);
        break;

        case WM_HELP: {
            // HMODULE hmodResource = cmnQueryNLSModuleHandle(FALSE);
            /* WM_HELP is received by this function when F1 or a "help" button
               is pressed in a dialog window. */
            if (ulCurHelpPanel > 0) {
                WPObject    *pHelpSomSelf = _wpclsQueryActiveDesktop(_WPDesktop);
                /* ulCurHelpPanel is set by instance methods before creating a
                   dialog box in order to link help topics to the displayed
                   dialog box. Possible values are:
                        0: open online reference ("<XFOLDER_REF>", INF book)
                      > 0: open help topic in xfldr.hlp
                       -1: ignore WM_HELP */
                if (pHelpSomSelf) {
                    PSZ pszHelpLibrary;
                    BOOL fProcessed = FALSE;
                    if (pszHelpLibrary = cmnQueryHelpLibrary())
                        // path found: display help panel
                        if (_wpDisplayHelp(pHelpSomSelf, ulCurHelpPanel, pszHelpLibrary))
                            fProcessed = TRUE;

                    if (!fProcessed)
                        cmnMessageBoxMsg(HWND_DESKTOP, 104, 134, MB_OK);
                }
            } else if (ulCurHelpPanel == 0)
            { // open online reference
                ulCurHelpPanel = -1; // ignore further WM_HELP messages: this one suffices
                hobjRef = WinQueryObject("<XFOLDER_REF>");
                if (hobjRef) {
                    WinOpenObject(hobjRef, OPEN_DEFAULT, TRUE);
                } else {
                    cmnMessageBoxMsg(HWND_DESKTOP, 104, 137, MB_OK);

                    /* WinDlgBox(HWND_DESKTOP,
                              hwnd,                     // destroy with first msg box
                              (PFNWP)fnwpDlgGeneric,
                              hmodResource, // load from resource file
                              ID_XFD_NOREF,             // dialog resource id
                              (PVOID)NULL);             // no dialog parameters
                              */
                }
            } // end else; if ulCurHelpPanel is < 0, nothing happens
            mrc = NULL;
        break; } // end case WM_HELP

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

    return (mrc);
}

/*-------------------------------------------------------------------
 *                                                                  *
 *   System sounds                                                  *
 *                                                                  *
 ********************************************************************/

/*
 *@@ cmnQuerySystemSound:
 *      this gets a system sound from the MMPM.INI file.
 *      usIndex must be the sound to query.
 *      Default system sounds:
 *      --  MMSOUND_WARNING         0
 *      --  MMSOUND_INFORMATION     1
 *      --  MMSOUND_ERROR           2
 *      --  MMSOUND_ANIMATEOPEN     3
 *      --  MMSOUND_ANIMATECLOSE    4
 *      --  MMSOUND_DRAG            5
 *      --  MMSOUND_DROP            6
 *      --  MMSOUND_SYSTEMSTARTUP   7
 *      --  MMSOUND_SHUTDOWN        8
 *      --  MMSOUND_SHREDDER        9
 *      --  MMSOUND_LOCKUP          10
 *      --  MMSOUND_ALARMCLOCK      11
 *      --  MMSOUND_PRINTERROR      12
 *
 *      New XFolder system sounds:
 *      --  MMSOUND_XFLD_SHUTDOWN   555
 *      --  MMSOUND_XFLD_RESTARTWPS 556
 *      --  MMSOUND_XFLD_SORT       557
 *
 *      The string buffers are recommended to be at least
 *      CCHMAXPATH in size.
 */

BOOL cmnQuerySystemSound(USHORT usIndex,    // in: sound index to query
                         PSZ pszDescr,      // out: address of buffer where to
                                // copy the sound description to, as displayed
                                // in the "Sound" object (may be NULL)
                         PSZ pszFile,
                                // address adress of buffer where to copy the
                                // sound file to (may be NULL)
                         PULONG pulVolume)
                                // address of ULONG where to copy the volume
                                // to (0-100). If the "Global volume" flag is
                                // set in MMPM.INI, this will return the global
                                // volume instead. May be NULL also.
{
    // We inline this func because it's only used once.
    HAB     habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
    HINI    hiniMMPM;
    CHAR    szMMPM[CCHMAXPATH];
    BOOL    rc = FALSE;

    #ifdef DEBUG_SOUNDS
        _Pmpf(("cmnQuerySystemSound index %d", usIndex));
    #endif

    sprintf(szMMPM, "%c:\\MMOS2\\MMPM.INI", doshQueryBootDrive());

    // _Pmpf(( "cmnQuerySystemSound: %s", szMMPM ));
    hiniMMPM = PrfOpenProfile(habDesktop, szMMPM);
    #ifdef DEBUG_SOUNDS
        _Pmpf(("  hiniMMPM: 0x%lX", hiniMMPM));
    #endif
    if (hiniMMPM) {
        CHAR szData[1000];
        CHAR szData2[100];
        CHAR szKey[10];
        sprintf(szKey, "%d", usIndex);
        PrfQueryProfileString(hiniMMPM,
                MMINIKEY_SOUNDSETTINGS, "EnableSounds",
                "TRUE",    // default string
                szData2, sizeof(szData2));
        #ifdef DEBUG_SOUNDS
            _Pmpf(("  sounds enabled: %s", szData2));
        #endif
        if (strcmp(szData2, "TRUE") == 0)
            // sounds enabled at all?
            if (PrfQueryProfileString(hiniMMPM,
                    MMINIKEY_SYSSOUNDS, szKey,
                    ".",
                    szData, sizeof(szData)-1) > 3)
            {
                // each key data has the following format:
                // <soundfile>#<description>#<volume>
                PSZ p1 = szData, p2;

                p2 = strchr(p1, '#');
                if (pszFile) {
                    strncpy(pszFile, p1, p2-p1);
                    pszFile[p2-p1] = '\0';
                    #ifdef DEBUG_SOUNDS
                        _Pmpf(("  found soundfile: %s", pszFile));
                    #endif
                }
                p1 = p2+1;

                // _Pmpf(( "  %s", p1 ));
                p2 = strchr(p1, '#');
                if (pszDescr) {
                    strncpy(pszDescr, p1, p2-p1);
                    pszDescr[p2-p1] = '\0';
                }
                p1 = p2+1;

                if (pulVolume) {
                    // if "global volume" has been enabled,
                    // we do not return the value specified
                    // here, but the global value
                    PrfQueryProfileString(hiniMMPM,
                            MMINIKEY_SOUNDSETTINGS, "ApplyVolumeToAll",
                            "FALSE",
                            szData2, sizeof(szData2));
                    if (strcmp(szData2, "FALSE") == 0) {
                        // individual volume settings per sound
                        p2 = strchr(p1, '#');
                        sscanf(p1, "%d", pulVolume);
                    } else {
                        // global volume setting for all sounds
                        PrfQueryProfileString(hiniMMPM,
                                MMINIKEY_SOUNDSETTINGS, "Volume",
                                "100",
                                szData2, sizeof(szData2));
                        sscanf(szData2, "%d", pulVolume);
                    }
                }

                rc = TRUE;
            }

        PrfCloseProfile(hiniMMPM);
    }

    return (rc);
}

/*
 *@@ cmnSetSystemSound:
 *      this sets a system sound in MMPM.INI.
 *      See cmnQuerySystemSound for the parameters.
 */

BOOL cmnSetSystemSound(USHORT usIndex, PSZ pszDescr, PSZ pszFile, BYTE bVolume)
{
    HAB     habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
    HINI    hiniMMPM;
    CHAR    szMMPM[CCHMAXPATH];

    sprintf(szMMPM, "%c\\MMOS2\\MMPM.INI", doshQueryBootDrive());

    hiniMMPM = PrfOpenProfile(habDesktop, szMMPM);
    if (hiniMMPM) {
        CHAR szData[1000];
        CHAR szKey[10];
        sprintf(szKey, "%d", usIndex);
        sprintf(szData, "%s#%s#%d", pszFile, pszDescr, bVolume);
        PrfWriteProfileString(hiniMMPM,
                MMINIKEY_SYSSOUNDS, szKey,
                szData);
        PrfCloseProfile(hiniMMPM);
        return (TRUE);
    }
    return (FALSE);
}


