
/*
 *@@sourcefile statbars.c:
 *      this file contains the status bar info translation logic.
 *      It does not contain the status bar's window proc (fnwpStatusBar),
 *      which is in xfldr.c.
 *
 *      All functions in this file are prefixed with "stb*".
 *
 *      This file is new with XFolder 0.81. V0.80 used
 *      SOM multiple inheritance to introduce new methods
 *      with XFldObject, which proved to cause too many problems.
 *      This is now all done in this file using SOM kernel functions
 *      to determine the class of a selected object.
 *
 *      It is thus now much easier to add support for new classes
 *      also, because no new class replacements have to be introduced.
 *      In order to do so, go through all the funcs below and add new
 *      "if" statements. No changes in other files should be necessary.
 *      You will have to add a #include below for that class though
 *      to be able to access the SOM class object for that new class.
 *
 *@@include #define INCL_WINWINDOWMGR
 *@@include #define INCL_DOSMODULEMGR
 *@@include #include <os2.h>
 *@@include #include "xfldr.h"  // for common.h
 *@@include #include "common.h"
 *@@include #include "statbars.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_DOSEXCEPTIONS      // needed for except.h
#define INCL_DOSERRORS

#define INCL_WINSHELLDATA       // profile funcs
#define INCL_WINPROGRAMLIST     // needed for WPProgram
#define INCL_WINSTDCNR          // needed for winh.h
#include <os2.h>

// C library headers
#include <stdio.h>              // needed for except.h
#include <setjmp.h>             // needed for except.h
#include <assert.h>             // needed for except.h

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

#include "stringh.h"            // string helper routines

// SOM headers which don't crash with prec. header files
#include "xfldr.h"
#include "xfobj.h"
#include "xfdisk.h"

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

#include "except.h"             // XFolder exception handling

// other SOM headers
#pragma hdrstop                 // VAC++ keeps crashing otherwise
#include <wppgm.h>              // WPProgram

#include "xwps.h"               // XFolder pseudo SOM functions

// finally, our own header file
#include "statbars.h"           // status bar translation logic

// global variables for storing
CHAR    szXFldObjectStatusBarMnemonics[CCHMAXMNEMONICS] = "";
CHAR    szWPProgramStatusBarMnemonics[CCHMAXMNEMONICS] = "";
CHAR    szXFldDiskStatusBarMnemonics[CCHMAXMNEMONICS] = "";
CHAR    szWPFileSystemStatusBarMnemonics[CCHMAXMNEMONICS] = "";
CHAR    szWPUrlStatusBarMnemonics[CCHMAXMNEMONICS] = "";

// WPUrl class object; to preserve compatibility with Warp 3,
// where this class does not exist, we call the SOM kernel
// explicitly to get the class object.
// The initial value of -1 means that we have not queried
// this class yet. After the first query, this either points
// to the class object or is NULL if the class does not exist.
SOMClass    *_WPUrl = (SOMClass*)-1;

/* *******************************************************
*                                                        *
*  funcs which operate on a WPS class                    *
*                                                        *
*********************************************************/

/*
 *  The following functions all take a SOM class object as
 *  an argument. Presently, the following class objects may
 *  be passed as a parameter:
 *      _XFldObject     (or any descendant)
 *      _WPProgram      (or any descendant)
 *      _XFldDisk       (or any descendant)
 *      _WPFileSystem   (or any descendant)
 *      _WPUrl          (if installed)
 *
 *  Some SOM tricks: _somDescendedFrom(A, B) performed
 *  on two class objects returns TRUE if B is a descendant
 *  of A.
 */

/*
 *@@ stbClassAddsNewMnemonics:
 *      returns TRUE if a class introduces new status bar
 *      mnemonics. Used by the "Status bars" notebook page
 *      in the "Select class" dialog to enable/disable
 *      classes which may be selected to set new status
 *      bar single-object information.
 */

BOOL stbClassAddsNewMnemonics(SOMClass *pClassObject)
{
    return (    (pClassObject == _XFldObject)
             || (pClassObject == _WPProgram)
             || (pClassObject == _XFldDisk)
             || (pClassObject == _WPFileSystem)
             || ( (_WPUrl != NULL) && (pClassObject == _WPUrl) )
           );
}

/*
 *@@ stbSetClassMnemonics:
 *      this changes the status bar mnemonics for "single object"
 *      info for objects of this class and subclasses to
 *      pszText. If *pszText points to null-length string, no
 *      status bar info will be displayed; if pszText is NULL,
 *      the menmonics will be reset to the default value.
 *      This is called by the "Status bars" notebook page
 *      only when the user changes status bar mnemonics.
 */

ULONG stbSetClassMnemonics(SOMClass *pClassObject,
                                      PSZ pszText)
{
    if (_WPUrl == (SOMClass*)-1)
    {
        // WPUrl class object not queried yet: do it now
        somId    somidWPUrl = somIdFromString("WPUrl");
        _WPUrl = _somFindClass(SOMClassMgrObject, somidWPUrl, 0, 0);
        // _WPUrl now either points to the WPUrl class object
        // or is NULL if the class is not installed (Warp 3!).
        // In this case, the object will be treated as a regular
        // file-system object.
    }

    if (_WPUrl) {
        if (_somDescendedFrom(pClassObject, _WPUrl))
        {
            // provoke a reload of the settings
            // in stbQueryClassMnemonics
            szWPUrlStatusBarMnemonics[0] = '\0';

            // set the class mnemonics in OS2.INI; if
            // pszText == NULL, the key will be deleted,
            // and stbQueryClassMnemonics will use
            // the default value
            return (ULONG)PrfWriteProfileString(HINI_USERPROFILE,
                    INIAPP_XFOLDER, INIKEY_SBTEXT_WPURL,
                    pszText);
        }
    }

    // no WPUrl or WPUrl not installed: continue
    if (_somDescendedFrom(pClassObject, _WPFileSystem))
    {
        // provoke a reload of the settings
        // in stbQueryClassMnemonics
        szWPFileSystemStatusBarMnemonics[0] = '\0';

        // set the class mnemonics in OS2.INI; if
        // pszText == NULL, the key will be deleted,
        // and stbQueryClassMnemonics will use
        // the default value
        return (ULONG)PrfWriteProfileString(HINI_USERPROFILE,
                INIAPP_XFOLDER, INIKEY_SBTEXT_WPFILESYSTEM,
                pszText);
    }
    else if (_somDescendedFrom(pClassObject, _XFldDisk))
    {
        // provoke a reload of the settings
        // in stbQueryClassMnemonics
        szXFldDiskStatusBarMnemonics[0] = '\0';

        // set the class mnemonics in OS2.INI; if
        // pszText == NULL, the key will be deleted,
        // and stbQueryClassMnemonics will use
        // the default value
        return (ULONG)PrfWriteProfileString(HINI_USERPROFILE,
                INIAPP_XFOLDER, INIKEY_SBTEXT_WPDISK,
                pszText);
    }
    else if (pClassObject == _WPProgram)
    {
        // provoke a reload of the settings
        // in stbQueryClassMnemonics
        szWPProgramStatusBarMnemonics[0] = '\0';

        // set the class mnemonics in OS2.INI; if
        // pszText == NULL, the key will be deleted,
        // and stbQueryClassMnemonics will use
        // the default value
        return (ULONG)PrfWriteProfileString(HINI_USERPROFILE,
                INIAPP_XFOLDER, INIKEY_SBTEXT_WPPROGRAM,
                pszText);
    }
    else if (_somDescendedFrom(pClassObject, _XFldObject))
    {
        // provoke a reload of the settings
        // in stbQueryClassMnemonics
        szXFldObjectStatusBarMnemonics[0] = '\0';

        // set the class mnemonics in OS2.INI; if
        // pszText == NULL, the key will be deleted,
        // and stbQueryClassMnemonics will use
        // the default value
        return ((ULONG)PrfWriteProfileString(HINI_USERPROFILE,
                INIAPP_XFOLDER, INIKEY_SBTEXT_WPOBJECT,
                pszText));
    }
    return (0);
}

/*
 *@@ stbQueryClassMnemonics:
 *      this returns the status bar mnemonics for "single object"
 *      info for objects of pClassObject and subclasses. This string
 *      is either what has been specified on the "Status bar"
 *      notebook page or a default string from the XFolder NLS DLL.
 *      This is called every time the status bar needs to be updated
 *      (stbComposeText) and by the "Status bars" notebook page.
 */

ULONG stbQueryClassMnemonics(SOMClass *pClassObject,    // in: class object of selected object
                                      PSZ pszText,      // out: text buffer
                                      ULONG cbText)     // in: sizeof(*pszText)
{
    if (_WPUrl == (SOMClass*)-1)
    {
        // WPUrl class object not queried yet: do it now
        somId    somidWPUrl = somIdFromString("WPUrl");
        _WPUrl = _somFindClass(SOMClassMgrObject, somidWPUrl, 0, 0);
        // _WPUrl now either points to the WPUrl class object
        // or is NULL if the class is not installed (Warp 3!).
    }

    if (_WPUrl)
        if (_somDescendedFrom(pClassObject, _WPUrl))
        {
            if (szWPUrlStatusBarMnemonics[0] == '\0')
                // load string if this is the first time
                if (PrfQueryProfileString(HINI_USERPROFILE,
                            INIAPP_XFOLDER, INIKEY_SBTEXT_WPURL,
                            NULL, &(szWPUrlStatusBarMnemonics),
                            sizeof(szWPUrlStatusBarMnemonics))
                        == 0)
                    // string not found in profile: set default
                    strcpy(szWPUrlStatusBarMnemonics, "\"$U\"$x(70%)$D $T");

            strncpy(pszText, szWPUrlStatusBarMnemonics, cbText);
            pszText[cbText-1] = '\0';
            // get out of here
            return (cbText);
        }

    if (_somDescendedFrom(pClassObject, _WPFileSystem))
    {
        if (szWPFileSystemStatusBarMnemonics[0] == '\0')
            // load string if this is the first time
            if (PrfQueryProfileString(HINI_USERPROFILE,
                        INIAPP_XFOLDER, INIKEY_SBTEXT_WPFILESYSTEM,
                        NULL, &(szWPFileSystemStatusBarMnemonics),
                        sizeof(szWPFileSystemStatusBarMnemonics))
                    == 0)
                // string not found in profile: load default from NLS resources
                WinLoadString(WinQueryAnchorBlock(HWND_DESKTOP), NLS_MODULE,
                            ID_XSSI_SBTEXTWPDATAFILE,
                            sizeof(szWPFileSystemStatusBarMnemonics),
                            szWPFileSystemStatusBarMnemonics);

        strncpy(pszText, szWPFileSystemStatusBarMnemonics, cbText);
        pszText[cbText-1] = '\0';
    }
    //
    else if (_somDescendedFrom(pClassObject, _XFldDisk))
    {
        if (szXFldDiskStatusBarMnemonics[0] == '\0')
            // load string if this is the first time
            if (PrfQueryProfileString(HINI_USERPROFILE,
                        INIAPP_XFOLDER, INIKEY_SBTEXT_WPDISK,
                        NULL, &(szXFldDiskStatusBarMnemonics), sizeof(szXFldDiskStatusBarMnemonics))
                    == 0)
                // string not found in profile: load default from NLS resources
                WinLoadString(WinQueryAnchorBlock(HWND_DESKTOP), NLS_MODULE,
                            ID_XSSI_SBTEXTWPDISK,
                            sizeof(szXFldDiskStatusBarMnemonics),
                            szXFldDiskStatusBarMnemonics);

        strncpy(pszText, szXFldDiskStatusBarMnemonics, cbText);
        pszText[cbText-1] = '\0';
    }
    //
    else if (_somDescendedFrom(pClassObject, _WPProgram))
    {
        if (szWPProgramStatusBarMnemonics[0] == '\0')
            // load string if this is the first time
            if (PrfQueryProfileString(HINI_USERPROFILE,
                        INIAPP_XFOLDER, INIKEY_SBTEXT_WPPROGRAM,
                        NULL, &(szWPProgramStatusBarMnemonics), sizeof(szWPProgramStatusBarMnemonics))
                    == 0)
                // string not found in profile: load default from NLS resources
                WinLoadString(WinQueryAnchorBlock(HWND_DESKTOP), NLS_MODULE,
                            ID_XSSI_SBTEXTWPPROGRAM,
                            sizeof(szWPProgramStatusBarMnemonics),
                            szWPProgramStatusBarMnemonics);

        strncpy(pszText, szWPProgramStatusBarMnemonics, cbText);
        pszText[cbText-1] = '\0';
    }
    // subsidiarily: XFldObject
    else if (_somDescendedFrom(pClassObject, _XFldObject))
    {
        // should always be TRUE
        if (szXFldObjectStatusBarMnemonics[0] == '\0')
            // load string if this is the first time
            if (PrfQueryProfileString(HINI_USERPROFILE,
                        INIAPP_XFOLDER, INIKEY_SBTEXT_WPOBJECT,
                        NULL, &(szXFldObjectStatusBarMnemonics),
                                sizeof(szXFldObjectStatusBarMnemonics))
                    == 0)
                // string not found in profile: load default from NLS resources
                WinLoadString(WinQueryAnchorBlock(HWND_DESKTOP), NLS_MODULE,
                            ID_XSSI_SBTEXTWPOBJECT,
                            sizeof(szXFldObjectStatusBarMnemonics),
                            szXFldObjectStatusBarMnemonics);

        strncpy(pszText, szXFldObjectStatusBarMnemonics, cbText);
        pszText[cbText-1] = '\0';
    } else
        strcpy(pszText, "???");     // should not occur
    return (cbText);
}

/* *******************************************************
*                                                        *
*  funcs which operate on a WPS object                   *
*                                                        *
*********************************************************/

/*
 *@@ stbTranslateSingleMnemonics:
 *      this method is called on an object by stbComposeText
 *      after the status bar mnemonics have been queried
 *      for the object's class using stbQueryClassMnemonics,
 *      which is passed to this function in pszText.
 *      As opposed to the functions above, this does not
 *      take a class object, but an _instance_ as a parameter,
 *      because the info has to be displayed for each object
 *      differently.
 *      Note that stbComposeText has changed all the '$' characters
 *      in pszText to the tabulator char ('\t') to avoid
 *      unwanted results if text inserted during the translations
 *      contains '$' chars. So we search for '\t' keys in this
 *      function.
 *      This returns the number of keys that were translated.
 */

ULONG  stbTranslateSingleMnemonics(SOMClass *pObject,
                                      PSZ pszText,      // in/out: status bar text
                                      ULONG cbText,     // in: size of *pszText
                                      CHAR cThousands)  // in: thousands separator
{
    ULONG       ulrc = 0;
    CHAR        szTemp[300];
    PSZ         p;

    /*
     * WPUrl:
     *
     */

    /* first check if the thing is a URL object;
       in addition to the normal WPFileSystem mnemonics,
       URL objects also support the $U mnemonic for
       displaying the URL */

    if (_WPUrl == (SOMClass*)-1)
    {
        // WPUrl class object not queried yet: do it now
        somId    somidWPUrl = somIdFromString("WPUrl");
        _WPUrl = _somFindClass(SOMClassMgrObject, somidWPUrl, 0, 0);
        // _WPUrl now either points to the WPUrl class object
        // or is NULL if the class is not installed (Warp 3!).
    }

    if (_WPUrl)
        if (_somIsA(pObject, _WPUrl))
        {
            // yes, we have a URL object:
            if (p = strstr(pszText, "\tU")) // URL mnemonic
            {
                CHAR szFilename[CCHMAXPATH];
                PSZ pszFilename = _wpQueryFilename(pObject, szFilename, TRUE);

                // read in the contents of the file, which
                // contain the URL
                PSZ pszURL = NULL;
                if (pszFilename)
                    pszURL = doshReadTextFile(pszFilename, 0);
                    if (pszURL) {
                        if (strlen(pszURL) > 100)
                            strcpy(pszURL+97, "...");
                        strhReplace(pszText, "\tU", pszURL);
                        free(pszURL);
                    }
                if (!pszURL)
                    strhReplace(pszText, "\tU", "?");
                ulrc++;
            }
        }

    /*
     * WPFileSystem:
     *
     */

    if (_somIsA(pObject, _WPFileSystem))
    {
        /* single-object status bar text mnemonics understood by WPFileSystem
           (in addition to those introduced by XFldObject):

             $r      object's real name

             $y      object type (.TYPE EA)
             $D      object creation date
             $T      object creation time
             $a      object attributes

             $Eb     EA size in bytes
             $Ek     EA size in kBytes
             $EK     EA size in KBytes
         */

        if (p = strstr(pszText, "\ty")) // attribs
        {
            PSZ p2 = NULL;
            p2 = _wpQueryType(pObject);
            strhReplace(pszText, "\ty", (p2) ? p2 : "?");
            ulrc++;
        }

        if (p = strstr(pszText, "\tD"))  // date
        {
            FILEFINDBUF4 ffb4;
            ULONG ulDateFormat =
                PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
            CHAR szDateSep[10];
            PrfQueryProfileString(HINI_USER, "PM_National", "sDate", "/",
                szDateSep, sizeof(szDateSep)-1);

            strcpy(szTemp, "?");
            _wpQueryDateInfo(pObject, &ffb4);
            strhFileDate(szTemp, &(ffb4.fdateLastWrite), ulDateFormat, szDateSep[0]);
            strhReplace(pszText, "\tD", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tT"))  // time
        {
            FILEFINDBUF4 ffb4;
            ULONG ulTimeFormat =
                PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
            CHAR szTimeSep[10];
            PrfQueryProfileString(HINI_USER, "PM_National", "sTime", ":",
                szTimeSep, sizeof(szTimeSep)-1);

            strcpy(szTemp, "?");
            _wpQueryDateInfo(pObject, &ffb4);
            strhFileTime(szTemp, &(ffb4.ftimeLastWrite), ulTimeFormat, szTimeSep[0]);
            strhReplace(pszText, "\tT", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\ta")) // attribs
        {
            ULONG fAttr = _wpQueryAttr(pObject);
            szTemp[0] = (fAttr & FILE_ARCHIVED) ? 'A' : 'a';
            szTemp[1] = (fAttr & FILE_HIDDEN  ) ? 'H' : 'h';
            szTemp[2] = (fAttr & FILE_READONLY) ? 'R' : 'r';
            szTemp[3] = (fAttr & FILE_SYSTEM  ) ? 'S' : 's';
            szTemp[4] = '\0';
            strhReplace(pszText, "\ta", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tEb")) // easize
        {
            ULONG ulEASize;
            ulEASize = _wpQueryEASize(pObject);
            strhThousandsDouble(szTemp, ulEASize, cThousands);
            strhReplace(pszText, "\tEb", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tEk"))
        {
            ULONG ulEASize;
            ulEASize = _wpQueryEASize(pObject);
            strhThousandsDouble(szTemp, ((ulEASize+500)/1000), cThousands);
            strhReplace(pszText, "\tEk", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tEK"))
        {
            ULONG ulEASize;
            ulEASize = _wpQueryEASize(pObject);
            strhThousandsDouble(szTemp, ((ulEASize+512)/1024), cThousands);
            strhReplace(pszText, "\tEK", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tr")) // real name
        {
            strcpy(szTemp, "?");
            _wpQueryFilename(pObject, szTemp, FALSE);
            strhReplace(pszText, "\tr", szTemp);
            ulrc++;
        }
    }

    /*
     * XFldDisk:
     *
     */

    else if (_somIsA(pObject, _XFldDisk))
    {
        ULONG ulLogicalDrive = -1;

        /* single-object status bar text mnemonics understood by XFldDisk:

             $F      file system type (HPFS, FAT, CDFS, ...)

             $fb     free space on drive in bytes
             $fk     free space on drive in kBytes
             $fK     free space on drive in KBytes
             $fm     free space on drive in mBytes
             $fM     free space on drive in MBytes

            NOTE: the $f keys are also handled by stbComposeText, but
                  those only work properly for file-system objects, so we need
                  to calculate these values for these (abstract) disk objects

         */

        // the following are for free space on drive
        if (p = strstr(pszText, "\tfb"))
        {
            double dbl;

            if (ulLogicalDrive == -1) {
                ulLogicalDrive = _wpQueryLogicalDrive(pObject);
                if (doshAssertDrive(ulLogicalDrive,
                                                TRUE)  // prohibit popups
                            != NO_ERROR)
                    ulLogicalDrive = 0;
            }

            dbl = doshQueryDiskFree(ulLogicalDrive);
            if (dbl == -1)
                strcpy(szTemp, "?");
            else
                strhThousandsDouble(szTemp, dbl,
                    cThousands);
            strhReplace(pszText, "\tfb", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tfk"))
        {
            double dbl;

            if (ulLogicalDrive == -1) {
                ulLogicalDrive = _wpQueryLogicalDrive(pObject);
                if (doshAssertDrive(ulLogicalDrive,
                                                TRUE)  // prohibit popups
                            != NO_ERROR)
                    ulLogicalDrive = 0;
            }

            dbl = doshQueryDiskFree(ulLogicalDrive);
            if (dbl == -1)
                strcpy(szTemp, "?");
            else
                strhThousandsDouble(szTemp,
                    ((dbl + 500) / 1000),
                    cThousands);
            strhReplace(pszText, "\tfk", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tfK"))
        {
            double dbl;

            if (ulLogicalDrive == -1) {
                ulLogicalDrive = _wpQueryLogicalDrive(pObject);
                if (doshAssertDrive(ulLogicalDrive,
                                                TRUE)  // prohibit popups
                            != NO_ERROR)
                    ulLogicalDrive = 0;
            }

            dbl = doshQueryDiskFree(ulLogicalDrive);
            if (dbl == -1)
                strcpy(szTemp, "?");
            else
                strhThousandsDouble(szTemp,
                    ((dbl + 512) / 1024),
                    cThousands);
            strhReplace(pszText, "\tfK", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tfm"))
        {
            double dbl;

            if (ulLogicalDrive == -1) {
                ulLogicalDrive = _wpQueryLogicalDrive(pObject);
                if (doshAssertDrive(ulLogicalDrive,
                                                TRUE)  // prohibit popups
                            != NO_ERROR)
                    ulLogicalDrive = 0;
            }

            dbl = doshQueryDiskFree(ulLogicalDrive);
            if (dbl == -1)
                strcpy(szTemp, "?");
            else
                strhThousandsDouble(szTemp,
                    ((dbl +500000) / 1000000),
                    cThousands);
            strhReplace(pszText, "\tfm", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tfM"))
        {
            double dbl;

            if (ulLogicalDrive == -1) {
                ulLogicalDrive = _wpQueryLogicalDrive(pObject);
                if (doshAssertDrive(ulLogicalDrive,
                                                TRUE)  // prohibit popups
                            != NO_ERROR)
                    ulLogicalDrive = 0;
            }

            dbl = doshQueryDiskFree(ulLogicalDrive);
            if (dbl == -1)
                strcpy(szTemp, "?");
            else
               strhThousandsDouble(szTemp,
                    ((dbl + (1024*1024/2)) / (1024*1024)),
                    cThousands);
            strhReplace(pszText, "\tfM", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tF"))  // file-system type (HPFS, ...)
        {
            CHAR szBuffer[200];

            if (ulLogicalDrive == -1) {
                ulLogicalDrive = _wpQueryLogicalDrive(pObject);
                if (doshAssertDrive(ulLogicalDrive,
                                                TRUE)  // prohibit popups
                            != NO_ERROR)
                    ulLogicalDrive = 0;
            }

            if (doshQueryDiskFSType(ulLogicalDrive, szBuffer) == NO_ERROR)
                strhReplace(pszText, "\tF", szBuffer);
            else
                strhReplace(pszText, "\tF", "?");
            ulrc++;
        }
    }

    /*
     * WPProgram:
     *
     */

    else if (_somIsA(pObject, _WPProgram))
    {
        PPROGDETAILS pProgDetails = NULL;
        ULONG       ulSize;

        /* single-object status bar text mnemonics understood by WPFileSystem
           (in addition to those introduced by XFldObject):

            $p      executable program file (as specified in the Settings)
            $P      parameter list (as specified in the Settings)
            $d      working directory (as specified in the Settings)
         */

        if (p = strstr(pszText, "\tp"))  // program executable
        {
            strcpy(szTemp, "?");
            if (!pProgDetails)
                if ((_wpQueryProgDetails(pObject, (PPROGDETAILS)NULL, &ulSize)))
                    if ((pProgDetails = (PPROGDETAILS)_wpAllocMem(pObject, ulSize, NULL)) != NULL)
                        _wpQueryProgDetails(pObject, pProgDetails, &ulSize);

            if (pProgDetails)
                if (pProgDetails->pszExecutable)
                    strcpy(szTemp, pProgDetails->pszExecutable);
                else strcpy(szTemp, "");

            strhReplace(pszText, "\tp", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\tP"))  // program executable
        {
            strcpy(szTemp, "?");
            if (!pProgDetails)
                if ((_wpQueryProgDetails(pObject, (PPROGDETAILS)NULL, &ulSize)))
                    if ((pProgDetails = (PPROGDETAILS)_wpAllocMem(pObject, ulSize, NULL)) != NULL)
                        _wpQueryProgDetails(pObject, pProgDetails, &ulSize);

            if (pProgDetails)
                if (pProgDetails->pszParameters)
                    strcpy(szTemp, pProgDetails->pszParameters);
                else strcpy(szTemp, "");

            strhReplace(pszText, "\tP", szTemp);
            ulrc++;
        }

        if (p = strstr(pszText, "\td"))  // startup dir
        {
            strcpy(szTemp, "?");
            if (!pProgDetails)
                if ((_wpQueryProgDetails(pObject, (PPROGDETAILS)NULL, &ulSize)))
                    if ((pProgDetails = (PPROGDETAILS)_wpAllocMem(pObject, ulSize, NULL)) != NULL)
                        _wpQueryProgDetails(pObject, pProgDetails, &ulSize);

            if (pProgDetails)
                if (pProgDetails->pszStartupDir)
                    strcpy(szTemp, pProgDetails->pszStartupDir);
                else strcpy(szTemp, "");

            strhReplace(pszText, "\td", szTemp);
            ulrc++;
        }

        if (pProgDetails)
            _wpFreeMem(pObject, (PBYTE)pProgDetails);
    }

    /*
     * XFldObject:
     *
     */

    if (_somIsA(pObject, _XFldObject))      // should always be TRUE
    {
        CHAR        szTemp[300];
        PSZ         p;

        /* single-object status bar text mnemonics understood by XFldObject:
             $t      object title
             $w      WPS class default title (e.g. "Data file")
             $W      WPS class name (e.g. WPDataFile)
         */

        if (p = strstr(pszText, "\tw"))     // class default title
        {
            SOMClass *pClassObject = _somGetClass(pObject);
            if (pClassObject)
                strhReplace(pszText, "\tw", _wpclsQueryTitle(pClassObject));
            else strhReplace(pszText, "\tw", "?");
            ulrc++;
        }

        if (p = strstr(pszText, "\tW"))     // class name
        {
            strcpy(szTemp, "?");
            strhReplace(pszText, "\tW", _somGetClassName(pObject));
            ulrc++;
        }

        if (p = strstr(pszText, "\tt"))          // object title
        {
            strcpy(szTemp, "?");
            strcpy(szTemp, _wpQueryTitle(pObject));
            strhBeautifyTitle(szTemp);
            strhReplace(pszText, "\tt", szTemp);
            ulrc++;
        }
    }

    return (ulrc);
}

/*
 *@@ stbComposeText:
 *      this is the main entry point to the status bar
 *      logic which gets called by the status bar wnd
 *      proc (fnwpStatusBar, xfldr.c) when the status
 *      bar text needs updating.
 *         This func first finds out which mode the status
 *      bar should operate in (depending on how many objects
 *      are currently selected in hwndCnr), and then selects
 *      the matching status bar mnemonic string and does the
 *      translation into meaningful information.
 *         If only one object is selected (one-object mode),
 *      stbTranslateSingleMnemonics (above) is called to
 *      do translations for one-object mode.
 *         No-object and many-objects modes are handled
 *      by this func directly, because these only use
 *      the mnemonics which are valid for all modes.
 */

BOOL stbComposeText(WPFolder* somSelf,      // in:  open folder with status bar
                    HWND hwndCnr,           // in:  cnr hwnd of that folder's open view
                    PSZ pszText,            // out: text buffer
                    ULONG cbText)           // in:  sizeof(*pszText)
{
   /* Generic status bar mnemonics handled here (for other than
      "single object" mode too):

        $c      no. of selected objects
        $C      total object count

        $fb     free space on drive in bytes
        $fk     free space on drive in kBytes
        $fK     free space on drive in KBytes
        $fm     free space on drive in mBytes
        $fM     free space on drive in MBytes

        $sb     size of selected objects in bytes
        $sk     size of selected objects in kBytes
        $sK     size of selected objects in KBytes
        $sm     size of selected objects in mBytes
        $sM     size of selected objects in MBytes

        $Sb     size of folder content in bytes
        $Sk     size of folder content in kBytes
        $SK     size of folder content in KBytes
        $Sm     size of folder content in mBytes
        $SM     size of folder content in MBytes

       The "single object" mode mnemonics are translated using
       the above functions afterwards.
    */

    BOOL        brc = FALSE;

    CNRINFO     CnrInfo;
    PMINIRECORDCORE pmrcSelected, pmrc2;
    ULONG       ulSelectedCount = 0,
                ulSizeSelected = 0,
                ulSizeTotal = 0;
    WPObject    *pObject = NULL,
                *pObject2 = NULL;

    CHAR        szThousand[10],
                szTemp[300];
    PSZ         p;
    CHAR        *p2;

    // go thru all the selected objects in the container
    // and sum up the size of the corresponding objects in
    // ulSizeSelected
    pmrcSelected = (PMINIRECORDCORE)CMA_FIRST;
    do {
        pmrcSelected =
            (PMINIRECORDCORE)WinSendMsg(hwndCnr,
                    CM_QUERYRECORDEMPHASIS,
                    (MPARAM)pmrcSelected,       // CMA_FIRST at first loop
                    (MPARAM)CRA_SELECTED);
        if (pmrcSelected) {
            ulSelectedCount++;
            pObject = OBJECT_FROM_PREC(pmrcSelected);
            if (pObject)
                if (xwpsCheckObject(pObject)) {
                    if (_somIsA(pObject, _WPFileSystem))
                        ulSizeSelected += _wpQueryFileSize(pObject);
                } else pObject = NULL;
        }
    } while (pmrcSelected);

    // get thousands separator from "Country" object
    PrfQueryProfileString(HINI_USER, "PM_Default_National", "sThousand",
            ",", szThousand, sizeof(szThousand)-1);

    // now get the mnemonics which have been set by the
    // user on the "Status bar" page, depending on how many
    // objects are selected
    if ( (ulSelectedCount == 0) || (pObject == NULL) )
        // "no object" mode
        strcpy(pszText,
                cmnQueryStatusBarSetting(SBS_TEXTNONESEL));
    else if (ulSelectedCount == 1) {
        // "single-object" mode: query the text to translate
        // from the object, because we can implement
        // different mnemonics for different WPS classes
        stbQueryClassMnemonics(
                _somGetClass(pObject), // object's class object
                pszText, cbText);
    } else
        // "multiple objects" mode
        strcpy(pszText,
                cmnQueryStatusBarSetting(SBS_TEXTMULTISEL));

    // before actually translating any "$" keys, all the
    // '$' characters in the mnemonic string are changed to
    // the tabulator character ('\t') to avoid having '$'
    // characters translated which might only have been
    // inserted during the translation, e.g. because a
    // filename contains a '$' character.
    // All the translation logic will then only search for
    // those tab characters.
    while (p2 = strchr(pszText, '$'))
        *p2 = '\t';

    if (ulSelectedCount == 1)
        // "single-object" mode: translate
        // object-specific mnemonics first
        stbTranslateSingleMnemonics(
                pObject,               // the object itself
                pszText, cbText,
                szThousand[0]);

    // query total object count (CnrInfo.cRecords)
    winhQueryCnrInfo(hwndCnr, CnrInfo);

    // if we have a "total size" query, also sum up the size of
    // the whole folder into ulSizeTotal, which will be handled
    // in detail below
    if (strstr(pszText, "\tS"))
    {
        #ifdef DEBUG_STATUSBARS
            _Pmpf(("Calculating total size"));
        #endif
        pmrc2 = NULL;
        do {
            pmrc2 =
                (PMINIRECORDCORE)WinSendMsg(hwndCnr,
                        CM_QUERYRECORD,
                        (MPARAM)pmrc2,
                        MPFROM2SHORT(
                            (pmrc2)
                                    ? CMA_NEXT
                                    : CMA_FIRST,    // for first loop
                            CMA_ITEMORDER)          // doesn't matter
                        );
            if (pmrc2) {
                pObject2 = OBJECT_FROM_PREC(pmrc2);
                if (pObject2)
                    if (xwpsCheckObject(pObject2)) {
                        if (_somIsA(pObject2, _WPFileSystem)) {
                            ulSizeTotal += _wpQueryFileSize(pObject2);
                        }
                    }
            }
        } while (pmrc2);

        #ifdef DEBUG_STATUSBARS
            _Pmpf(("  Result: %d", ulSizeTotal));
        #endif
    }

    if (p = strstr(pszText, "\tc")) // selected objs count
    {
        sprintf(szTemp, "%d", ulSelectedCount);
        strhReplace(pszText, "\tc", szTemp);
    }

    if (p = strstr(pszText, "\tC")) // total obj count
    {
        sprintf(szTemp, "%d", CnrInfo.cRecords);
        strhReplace(pszText, "\tC", szTemp);
    }

    // the following are for free space on drive

    if (p = strstr(pszText, "\tfb"))
    {
        strhThousandsDouble(szTemp,
                xwpsQueryDiskFreeFromFolder(somSelf),
                szThousand[0]);
        strhReplace(pszText, "\tfb", szTemp);
    }

    if (p = strstr(pszText, "\tfk"))
    {
        strhThousandsDouble(szTemp,
                ((xwpsQueryDiskFreeFromFolder(somSelf) + 500) / 1000),
                szThousand[0]);
        strhReplace(pszText, "\tfk", szTemp);
    }

    if (p = strstr(pszText, "\tfK"))
    {
        strhThousandsDouble(szTemp,
                ((xwpsQueryDiskFreeFromFolder(somSelf) + 512) / 1024),
                szThousand[0]);
        strhReplace(pszText, "\tfK", szTemp);
    }

    if (p = strstr(pszText, "\tfm"))
    {
        strhThousandsDouble(szTemp,
                ((xwpsQueryDiskFreeFromFolder(somSelf) +500000) / 1000000),
                szThousand[0]);
        strhReplace(pszText, "\tfm", szTemp);
    }

    if (p = strstr(pszText, "\tfM"))
    {
        strhThousandsDouble(szTemp,
                ((xwpsQueryDiskFreeFromFolder(somSelf) + (1024*1024/2)) / (1024*1024)),
                szThousand[0]);
        strhReplace(pszText, "\tfM", szTemp);
    }

    // the following are for SELECTED size
    if (p = strstr(pszText, "\tsb"))
    {
        strhThousandsULong(szTemp, ulSizeSelected, szThousand[0]);
        strhReplace(pszText, "\tsb", szTemp);
    }

    if (p = strstr(pszText, "\tsk"))
    {
        strhThousandsULong(szTemp, ((ulSizeSelected+500) / 1000), szThousand[0]);
        strhReplace(pszText, "\tsk", szTemp);
    }

    if (p = strstr(pszText, "\tsK"))
    {
        strhThousandsULong(szTemp, ((ulSizeSelected+512) / 1024), szThousand[0]);
        strhReplace(pszText, "\tsK", szTemp);
    }

    if (p = strstr(pszText, "\tsm"))
    {
        strhThousandsULong(szTemp, ((ulSizeSelected+500000) / 1000000), szThousand[0]);
        strhReplace(pszText, "\tsm", szTemp);
    }

    if (p = strstr(pszText, "\tsM"))
    {
        strhThousandsULong(szTemp, ((ulSizeSelected+(1024*1024/2)) / (1024*1024)), szThousand[0]);
        strhReplace(pszText, "\tsM", szTemp);
    }

    // the following are for TOTAL folder size
    if (p = strstr(pszText, "\tSb"))
    {
        strhThousandsULong(szTemp, ulSizeTotal, szThousand[0]);
        strhReplace(pszText, "\tSb", szTemp);
    }

    if (p = strstr(pszText, "\tSk"))
    {
        strhThousandsULong(szTemp, ((ulSizeTotal+500) / 1000), szThousand[0]);
        strhReplace(pszText, "\tSk", szTemp);
    }

    if (p = strstr(pszText, "\tSK"))
    {
        strhThousandsULong(szTemp, ((ulSizeTotal+512) / 1024), szThousand[0]);
        strhReplace(pszText, "\tSK", szTemp);
    }

    if (p = strstr(pszText, "\tSm"))
    {
        strhThousandsULong(szTemp, ((ulSizeTotal+500000) / 1000000), szThousand[0]);
        strhReplace(pszText, "\tSm", szTemp);
    }

    if (p = strstr(pszText, "\tSM"))
    {
        strhThousandsULong(szTemp, ((ulSizeTotal+(1024*1024/2)) / (1024*1024)), szThousand[0]);
        strhReplace(pszText, "\tSM", szTemp);
    }

    // now translate remaining '\t' characters back into
    // '$' characters; this might happen if the user actually
    // wanted to see a '$' character displayed
    while (p2 = strchr(pszText, '\t'))
        *p2 = '$';

    brc = TRUE;

    return (brc);
}


