
/*
 *@@sourcefile xfldr.c:
 *      This file contains the following major XFolder parts:
 *
 *      --  XFolder class
 *      --  XFldStartup (startup folder) class
 *      --  XFldShutdown (shutdown folder) class
 *
 *      Check the other files starting with xf* for the
 *      other XFolder classes.
 *
 *      XFolder is probably the most complex class of this
 *      package. Not only are context menus manipulated, but
 *      open folder view windows are also subclassed to
 *      introduce additional functionality (fnwpSubclassedFolderFrame).
 *
 *      Most of the code in this file is "responsive" in that
 *      it is only called upon invocation of folder SOM methods.
 *      However, this also hooks a lot of functionality into
 *      the WPS using the subclassed folder frame proc.
 *
 *      The XFolder class must always be installed.
 *      Installation of XFldStartup and XFldShutdown is
 *      optional.
 *
 *@@somclass XFolder xf_
 *@@somclass M_XFolder xfM_
 *@@somclass XFldStartup xfstup_
 *@@somclass M_XFldStartup xfstupM_
 *@@somclass XFldShutdown xfshut_
 *@@somclass M_XFldShutdown xfshutM_
 */

/*
 *      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.
 */

/*
 *  This file was generated by the SOM Compiler and Emitter Framework.
 *  Generated using:
 *      SOM Emitter emitctm: 2.41
 */

#ifndef SOM_Module_xfldr_Source
#define SOM_Module_xfldr_Source
#endif
#define XFolder_Class_Source
#define M_XFolder_Class_Source

/*
 *  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_DOSFILEMGR
#define INCL_DOSMODULEMGR
#define INCL_DOSMEMMGR
#define INCL_DOSPROCESS         // DosSleep, priorities, PIDs etc.
#define INCL_DOSSEMAPHORES      // needed for xthreads.h
#define INCL_DOSEXCEPTIONS
#define INCL_DOSERRORS
#define INCL_DOSMISC            // DosGetMessage etc.

#define INCL_WINWINDOWMGR
#define INCL_WINFRAMEMGR        // WM_FORMATFRAME, SC_CLOSE etc.
#define INCL_WINSYS             // presparams, WinQuerySysValue()
#define INCL_WININPUT           // Focus, WM_BUTTON1DOWN, WM_CONTEXTMENU etc.

#define INCL_WINSHELLDATA       // profile funcs
#define INCL_WINPROGRAMLIST     // needed for WPProgram
#define INCL_WINTIMER
#define INCL_WINPOINTERS
#define INCL_WINERRORS
#define INCL_WINRECTANGLES      // rcl functions
#define INCL_WINMESSAGEMGR      // WinQueryMsgPos

#define INCL_WINDIALOGS
#define INCL_WINSTATICS
#define INCL_WINMENUS           // needed for menus.h
#define INCL_WINENTRYFIELDS
#define INCL_WINBUTTONS
#define INCL_WINLISTBOXES
#define INCL_WINSTDCNR          // needed for winh.h
#define INCL_WINSTDBOOK         // notebooks
#define INCL_WINSTDFILE         // file dialog
#define INCL_WINMLE             // multi-line entry field

#define INCL_WINWORKPLACE

#define INCL_GPILOGCOLORTABLE
#define INCL_GPIPRIMITIVES

#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

#pragma hdrstop                 // VAC++ keeps crashing otherwise

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

#include "eas.h"                // extended attributes helper routines
#include "linklist.h"           // linked list helper routines
#include "wphandle.h"           // Henk Kelder's HOBJECT handling

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

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

#include "common.h"             // the majestic XFolder include file
#include "cnrsort.h"            // container sort comparison functions
#include "except.h"             // XFolder exception handling
#include "menus.h"              // common XFolder context menu logic
#include "module.h"             // XFolder main DLL information
#include "notebook.h"           // generic XFolder notebook handling
#include "sound.h"              // declarations for SOUND.DLL
#include "statbars.h"           // status bar translation logic
#include "xthreads.h"           // XFolder threads; this includes threads.h

// other SOM headers
#include <wppgm.h>              // WPProgram
#include <wprootf.h>            // WPRootFolder
#include <wpshadow.h>           // WPShadow
#include "xfobj.h"
#include "xfdesk.h"
#include "xfdisk.h"

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

/* ******************************************************************
 *                                                                  *
 *   Global variables                                               *
 *                                                                  *
 ********************************************************************/

// roots of linked lists for favorite/quick-open folders
PCONTENTMENULISTITEM pcmliFavoriteFolders = NULL,
                    pcmliQuickOpenFolders = NULL;
// mutex semaphores for these lists
HMTX                hmtxFavoriteFolders = NULLHANDLE,
                    hmtxQuickOpenFolders = NULLHANDLE;
CHAR                szXFolderVersion[100];

// forward declarations
MRESULT EXPENTRY fnwpSupplObject(HWND hwndObject, ULONG msg, MPARAM mp1, MPARAM mp2);

// SORTBYICONPOS:
// structure for GetICONPOS
typedef struct _SORTBYICONPOS {
    CHAR    szRealName[CCHMAXPATH];
    PBYTE   pICONPOS;
    USHORT  usICONPOSSize;
} SORTBYICONPOS, *PSORTBYICONPOS;

/* ******************************************************************
 *                                                                  *
 *   Common routines used by several XFolder methods                *
 *                                                                  *
 ********************************************************************/

/*
 *@@ QuerySortFunc:
 *      this returns the sort comparison function for
 *      the specified sort criterion. See XFolder::xfSortViewOnce
 *      for details.
 */

PFN QuerySortFunc(USHORT usSort)
{
    USHORT usSort2 = usSort;

    if (usSort == SET_DEFAULT)
    {
        PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
        usSort2 =  pGlobalSettings->DefaultSort;
    }

    switch (usSort2) {
        case SV_TYPE:           return ((PFN)fnCompareType);
        case SV_CLASS:          return ((PFN)fnCompareClass);
        case SV_REALNAME:       return ((PFN)fnCompareRealName);
        case SV_SIZE:           return ((PFN)fnCompareSize);
        case SV_LASTWRITEDATE:  return ((PFN)fnCompareLastWriteDate);
        case SV_LASTACCESSDATE: return ((PFN)fnCompareLastAccessDate);
        case SV_CREATIONDATE:   return ((PFN)fnCompareCreationDate);
        case SV_EXT:            return ((PFN)fnCompareExt);
        case SV_FOLDERSFIRST:   return ((PFN)fnCompareFoldersFirst);
    };

    // default:
    return ((PFN)fnCompareName);
}

/*
 *@@ SetOneFrameWndTitle:
 *           this changes the window title of a given folder frame window
 *           to the full path of the folder; this method does NOT check
 *           the respective XFolder settings any more, so this has to be
 *           done by the caller.
 */

BOOL SetOneFrameWndTitle(XFolder *somSelf, HWND hwndFrame)
{
    PSZ                 pFirstSlash, pSrchSlash, pNextSlash;
    CHAR                szTemp[CCHMAXPATH];
    XFolderData         *somThis = XFolderGetData(somSelf);
    PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();
    BOOL                brc;

    if (    (_bFullPath == 1)
         || ((_bFullPath == 2) && (pGlobalSettings->FullPath))
       )
    {
        // settings allow full path in title for this folder:

        // get real name (= full path), if allowed)
        _wpQueryFilename(somSelf, szTemp, TRUE);

        // now truncate path if it's longer than allowed by user
        pFirstSlash = strchr(szTemp, '\\');
        if ((pFirstSlash) && (pGlobalSettings->MaxPathChars > 10)) {
            pSrchSlash = pFirstSlash+3;
            while (strlen(szTemp) > pGlobalSettings->MaxPathChars) {
                pNextSlash = strchr(pSrchSlash, '\\');
                if (pNextSlash) {
                    strcpy(pFirstSlash+4, pNextSlash);
                    pFirstSlash[1] = '.';
                    pFirstSlash[2] = '.';
                    pFirstSlash[3] = '.';
                    pSrchSlash = pFirstSlash+5;
                }
                else break;
            }
        }

        // now either append the full path in brackets to or replace window title
        if (pGlobalSettings->KeepTitle) {
            CHAR szFullPathTitle[CCHMAXPATH*2];
            sprintf(szFullPathTitle, "%s (%s)",
                    _wpQueryTitle(somSelf),
                    szTemp);
            WinSetWindowText(hwndFrame, szFullPathTitle);
        }
        else
            WinSetWindowText(hwndFrame, szTemp);

        brc = TRUE;
    } else {
        // settings DON'T allow full path in title for this folder:
        // set to title only
        WinSetWindowText(hwndFrame, _wpQueryTitle(somSelf));
        brc = FALSE;
    }
    return (brc);
}

/* ******************************************************************
 *                                                                  *
 *   here come the XFolder instance methods                         *
 *                                                                  *
 ********************************************************************/

/*
 *@@ xfUpdateAllFrameWndTitles:
 *           this method sets the frame wnd titles for all currently
 *           open views of a given folder to the full path;
 *           it is called on the Worker thread after XFolder's replacements
 *           of wpMoveObject, wpSetTitle, or wpRefresh have been called.
 *           It does check the respective Global / folder settings.
 */

SOM_Scope BOOL     SOMLINK xf_xfUpdateAllFrameWndTitles(XFolder *somSelf)
{
    HWND        hwndFrame;
    BOOL        brc = FALSE;

    if (hwndFrame = xwpsQueryFrameFromView(somSelf, OPEN_CONTENTS)) {
        SetOneFrameWndTitle(somSelf, hwndFrame);
        brc = TRUE;
    }
    if (hwndFrame = xwpsQueryFrameFromView(somSelf, OPEN_DETAILS)) {
        SetOneFrameWndTitle(somSelf, hwndFrame);
        brc = TRUE;
    }
    if (hwndFrame = xwpsQueryFrameFromView(somSelf, OPEN_TREE)) {
        SetOneFrameWndTitle(somSelf, hwndFrame);
        brc = TRUE;
    }

    ntbUpdateVisiblePage(somSelf, SP_XFOLDER_FLDR);

    return (brc);
}

/*
 *@@ xfSnapToGrid:
 *           makes all objects in the folder "snap" on a grid whose
 *           coordinates are to be defined in the "system" object.
 *           This method checks if an Icon view of the folder is
 *           currently open; if not and fNotify == TRUE, it displays
 *           a message box.
 */

SOM_Scope BOOL  SOMLINK xf_xfSnapToGrid(XFolder *somSelf,
                                           BOOL fNotify)
{
    // WPObject            *obj;
    HWND                hwndFrame = 0,
                        hwndCnr = 0;
    PMINIRECORDCORE     pmrc;
    LONG                lNewX, lNewY;
    BOOL                brc = FALSE;
    // if Shift is pressed, move all the objects, otherwise
    // only the selected ones
    BOOL                fShiftPressed = doshQueryShiftState();
    BOOL                fMoveThisObject = FALSE;

    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfSnapToGrid");

    // first we need the frame handle of a currently open icon view;
    // all others don't make sense */
    hwndFrame = xwpsQueryFrameFromView(somSelf, OPEN_CONTENTS);

    if (hwndFrame) {

        // now get the container handle
        hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);

        if (hwndCnr)
        {
            // now begin iteration over the folder's objects; we don't
            // use the WPS method (wpQueryContent) because this is too
            // slow. Instead, we query the container directly.

            pmrc = NULL;
            do {
                if (fShiftPressed)
                    // shift pressed: move all objects, so loop
                    // thru the whole container content
                    pmrc =
                        (PMINIRECORDCORE)WinSendMsg(hwndCnr,
                                CM_QUERYRECORD,
                                (MPARAM)pmrc,           // NULL at first loop
                                MPFROM2SHORT(
                                    (pmrc)
                                        ? CMA_NEXT      // not first loop: get next object
                                        : CMA_FIRST,    // first loop: get first objecct
                                    CMA_ITEMORDER)
                                );
                else
                    // shift _not_ pressed: move selected objects
                    // only, so loop thru these objects
                    pmrc =
                        (PMINIRECORDCORE)WinSendMsg(hwndCnr,
                                CM_QUERYRECORDEMPHASIS,
                                (pmrc)                  // NULL at first loop
                                    ? (MPARAM)pmrc
                                    : (MPARAM)CMA_FIRST, // flag for getting first selected
                                (MPARAM)(CRA_SELECTED)
                                );
                if (pmrc) {
                    // record found:
                    // the WPS shares records among views, so we need
                    // to update the record core info first
                    WinSendMsg(hwndCnr,
                                CM_QUERYRECORDINFO,
                                (MPARAM)&pmrc,
                                (MPARAM)1);         // one record only
                    // un-display the new object at the old (default) location
                    WinSendMsg(hwndCnr,
                                CM_ERASERECORD,
                                    // this only changes the visibility of the
                                    // record without changing the recordcore;
                                    // this msg is intended for drag'n'drop and such
                                (MPARAM)pmrc,
                                NULL);

                    // now play with the objects coordinates
                    lNewX =
                        ( (
                            ( (pmrc->ptlIcon.x - pGlobalSettings->GridX)
                              + (pGlobalSettings->GridCX / 2)
                            )
                        / pGlobalSettings->GridCX ) * pGlobalSettings->GridCX )
                        + pGlobalSettings->GridX;
                    lNewY =
                        ( (
                            ( (pmrc->ptlIcon.y - pGlobalSettings->GridY)
                              + (pGlobalSettings->GridCY / 2)
                            )
                        / pGlobalSettings->GridCY ) * pGlobalSettings->GridCY )
                        + pGlobalSettings->GridY;

                    // update the record core
                    if ( (lNewX) && (lNewX != pmrc->ptlIcon.x) ) {
                        pmrc->ptlIcon.x = lNewX;         // X
                    }
                    if ( (lNewY) && (lNewY != pmrc->ptlIcon.y) ) {
                        pmrc->ptlIcon.y = lNewY;         // Y
                    }

                    // repaint at new position
                    WinSendMsg(hwndCnr,
                                CM_INVALIDATERECORD,
                                (MPARAM)&pmrc,
                                MPFROM2SHORT(1,     // one record only
                                    CMA_REPOSITION | CMA_ERASE));
                }
            } while (pmrc);

            brc = TRUE; // "OK" flag
        } // end if (hwndCnr)
    } // end if (hwndFrame)
    else { // no open icon view: complain
        if (fNotify) {
            cmnSetHelpPanel(-1);                   // disable F1
            WinDlgBox(HWND_DESKTOP,
                         HWND_DESKTOP,             // owner is desktop
                         (PFNWP)fnwpDlgGeneric,    // common.c
                         NLS_MODULE,               // load from resource file
                         ID_XFD_NOICONVIEW,        // dialog resource id
                         (PVOID)NULL);
        }
    }
    return (brc);
}

/*
 *@@ xfQueryFldrSort:
 *           this returns the folder's sort settings into the specified
 *           USHORT variables. These are set to SET_DEFAULT if no instance
 *           data has been defined; you will then need to query the Global
 *           Settings' values.
 */

SOM_Scope BOOL  SOMLINK xf_xfQueryFldrSort(XFolder *somSelf,
                                              PUSHORT pusDefaultSort,
                                              PUSHORT pusAlwaysSort)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfQueryFldrSort");

    if ((pusDefaultSort) && (pusAlwaysSort)) {
        *pusDefaultSort = (USHORT)_DefaultSort;
        *pusAlwaysSort  = (USHORT)_AlwaysSort;
        return (TRUE);
    } else
        return (FALSE);
}

/*
 * fncbSortAllViews:
 *      callback function for sorting all folder views.
 *      This is called by xf(cls)ForEachOpenView, which also passes
 *      the parameters to this func.
 */

MRESULT EXPENTRY fncbSortAllViews(HWND hwndView,    // open folder view frame hwnd
                                  ULONG ulSort,     // sort flag
                                  MPARAM mpView,    // OPEN_xxx flag
                                  MPARAM mpFolder)  // XFolder*
{
    XFolder     *somSelf = (XFolder*)mpFolder;
    MRESULT     mrc = (MPARAM)FALSE;

    if (   ((ULONG)mpView == OPEN_CONTENTS)
        || ((ULONG)mpView == OPEN_TREE)
        || ((ULONG)mpView == OPEN_DETAILS)
       )
    {
        _xfSortViewOnce(somSelf, hwndView, ulSort);
        mrc = (MPARAM)TRUE;
    }
    return (mrc);
}

/*
 * fncbUpdateFolderSorts:
 *      callback function for updating all folder sorts.
 *      This is called by xf(cls)ForEachOpenView, which also passes
 *      the parameters to this func.
 */

MRESULT EXPENTRY fncbUpdateFolderSorts(HWND hwndView,   // frame wnd handle
                        ULONG ulDummy,
                        MPARAM mpView,                  // OPEN_xxx flag for this view
                        MPARAM mpFolder)                // somSelf
{
    XFolder     *somSelf = (XFolder*)mpFolder;
    MRESULT     mrc = (MPARAM)FALSE;

    #ifdef DEBUG_SORT
        _Pmpf(( "fncbUpdateFolderSorts: %s", _wpQueryTitle(somSelf) ));
    #endif

    if (   ((ULONG)mpView == OPEN_CONTENTS)
        || ((ULONG)mpView == OPEN_TREE)
        || ((ULONG)mpView == OPEN_DETAILS)
       )
    {
        _xfSetCnrSort(somSelf, xwpsQueryCnrFromFrame(hwndView), FALSE);
        mrc = (MPARAM)TRUE;
    }
    return (mrc);
}

/*
 *@@ xfSetFldrSort:
 *           this is the new XFolder method for setting the sort data
 *           for a certain folder.
 *
 *           usDefaultSort should be one of the SV_ constants as in
 *           XFolder::xfSortViewOnce or SET_DEFAULT for resetting the folder's
 *           default sort criterion to the Global Settings's value.
 *
 *           usAlwaysSort can be 0 or 1 or SET_DEFAULT also.
 *
 *           This method updates all open folder views with the new
 *           sort settings.
 *           This method returns TRUE if any visible change occured as
 *           a result to the new settings.
 */

SOM_Scope BOOL  SOMLINK xf_xfSetFldrSort(XFolder *somSelf,
                                USHORT usDefaultSort,
                                USHORT usAlwaysSort)
{
    BOOL Update = FALSE;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfSetFldrSort");

    #ifdef DEBUG_SORT
        _Pmpf(("xfSetFldrSort %s", _wpQueryTitle(somSelf)));
        _Pmpf(("  Old: Default %d, Always %d", _DefaultSort, _AlwaysSort));
        _Pmpf(("  New: Default %d, Always %d", usDefaultSort, usAlwaysSort));
    #endif

    if (usDefaultSort != _DefaultSort) {
        _DefaultSort = usDefaultSort;
        Update = TRUE;
    }
    if (usAlwaysSort != _AlwaysSort) {
        _AlwaysSort = usAlwaysSort;
        // if _wpRestoreState has found the pointer to the
        // WPFOlder-internal sort structure, we will update
        // this one also, because otherwise the WPS keeps
        // messing with the container attributes
        if (_pFldrSortInfo)
            (_pFldrSortInfo)->fAlwaysSort = ALWAYS_SORT;
        Update = TRUE;
    }

    if (Update) {
        // update open views of this folder
        _xfForEachOpenView(somSelf, 0, &fncbUpdateFolderSorts);
        _wpSaveDeferred(somSelf);
        // update folder "Sort" notebook page, if open
        ntbUpdateVisiblePage(somSelf, SP_FLDRSORT_FLDR);
    }

    return (Update);
}

/*
 *@@ xfSetCnrSort:
 *           this is the most central function of XFolder's
 *           extended sorting capabilities. This is called
 *           every time the container in an open folder view
 *           needs to have its sort settings updated. In other
 *           words, this function evaluates the current folder
 *           sort settings and finds out the corresponding
 *           container sort comparison functions and other
 *           settings.
 *
 *           Parameters:
 *           --  HWND hwndCnr     cnr of open view of somSelf
 *           --  BOOL fForce      TRUE: always update the cnr
 *                                settings, even if they have
 *                                not changed
 *
 *           This function gets called:
 *           1)  from our wpOpen override to set folder sort
 *               when a new folder view opens;
 *           2)  from our wpSetFldrSort override (see notes
 *               there);
 *           3)  after folder sort settings have been changed
 *               using xfSetFldrSort.
 *
 *           It is usually not necessary to call this method
 *           directly. To sort folders, you should call
 *           XFolder::xfSetFldrSort or XFolder::xfSortViewOnce
 *           instead.
 *
 *           Note that XFolder does not use the WPS's sort
 *           mechanisms at all. Instead, we set our own
 *           container sort comparison functions directly
 *           _always_. Those functions are all in cnrsort.c.
 *
 *           See the XFolder Programming Guide for an
 *           overview of how XFolder sorting works.
 */

SOM_Scope void  SOMLINK xf_xfSetCnrSort(XFolder *somSelf, HWND hwndCnr,
                                        BOOL fForce)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfSetCnrSort");

    if (hwndCnr) {
        PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
        XFolderData     *somThis = XFolderGetData(somSelf);

        // use our macro for determining this folder's always-sort flag;
        // this is TRUE if "Always sort" is on either locally or globally
        BOOL            AlwaysSort = ALWAYS_SORT;

        // get our sort comparison func
        PFN             pfnSort =  (AlwaysSort)
                                       ? QuerySortFunc(DEFAULT_SORT)
                                       : NULL
                                   ;

        CNRINFO         CnrInfo;
        BOOL            Update = FALSE;

        winhQueryCnrInfo(hwndCnr, CnrInfo);

        #ifdef DEBUG_SORT
            _Pmpf(( "_xfSetCnrSort: %s", _wpQueryTitle(somSelf) ));
            _Pmpf(( "  _Always: %d, Global->Always: %d", _AlwaysSort, pGlobalSettings->AlwaysSort ));
            _Pmpf(( "  _Default: %d, Global->Default: %d", _DefaultSort, pGlobalSettings->DefaultSort ));
        #endif

        // for icon views, we need extra precautions
        if ((CnrInfo.flWindowAttr & (CV_ICON | CV_TREE)) == CV_ICON)
        {
            // for some reason, cnr icon views need to have "auto arrange" on
            // when sorting, or the cnr will allow to drag'n'drop icons freely
            // within the same cnr, which is not useful when auto-sort is on

            ULONG       ulStyle = WinQueryWindowULong(hwndCnr, QWL_STYLE);

            if (AlwaysSort) {
                // always sort: we need to set CCS_AUTOPOSITION, if not set
                if ((ulStyle & CCS_AUTOPOSITION) == 0) {
                    #ifdef DEBUG_SORT
                        _Pmpf(( "  New ulStyle = %lX", ulStyle | CCS_AUTOPOSITION ));
                    #endif
                    WinSetWindowULong(hwndCnr, QWL_STYLE, (ulStyle | CCS_AUTOPOSITION));
                    Update = TRUE;
                }
            } else {
                // NO always sort: we need to unset CCS_AUTOPOSITION, if set
                if ((ulStyle & CCS_AUTOPOSITION) != 0) {
                    #ifdef DEBUG_SORT
                        _Pmpf(( "  New ulStyle = %lX", ulStyle & (~CCS_AUTOPOSITION) ));
                    #endif
                    WinSetWindowULong(hwndCnr, QWL_STYLE, (ulStyle & (~CCS_AUTOPOSITION)));
                    Update = TRUE;
                }
            }
        }

        // now also update the internal WPFolder sort info, because otherwise
        // the WPS will keep reverting the cnr attrs; we have obtained the pointer
        // to this structure in wpRestoreData
        if (_pFldrSortInfo)
            _pFldrSortInfo->fAlwaysSort = AlwaysSort;

        // finally, set the cnr sort function: we perform these checks
        // to avoid cnr flickering
        if (    // sort function changed?
                (CnrInfo.pSortRecord != (PVOID)pfnSort)
                // CCS_AUTOPOSITION flag changed above?
             || (Update)
             || (fForce)
           )
        {
            #ifdef DEBUG_SORT
                _Pmpf(( "  Resetting pSortRecord to %lX", CnrInfo.pSortRecord ));
            #endif

            // set the cnr sort function; if this is != NULL, the
            // container will always sort the records. If auto-sort
            // is off, pfnSort has been set to NULL above.
            CnrInfo.pSortRecord = (PVOID)pfnSort;

            // now update the CnrInfo, which will repaint the cnr also
            WinSendMsg(hwndCnr,
                        CM_SETCNRINFO,
                        (MPARAM)&CnrInfo,
                        (MPARAM)CMA_PSORTRECORD);
        }
    }
}

/*
 *@@ xfSortViewOnce:
 *           sorts the content of given folder.
 *           As opposed to xfSetFldrSort, this does not change the
 *           folder sort settings, but only sorts the view once.
 *           This is used by the context menu entries in the "Sort"
 *           menu.
 *
 *           ulSort must be one of the following:
 *           --  SV_NAME
 *           --  SV_TYPE
 *           --  SV_CLASS                (new: sort by object class)
 *           --  SV_REALNAME
 *           --  SV_SIZE
 *           --  SV_LASTWRITEDATE
 *           --  SV_LASTACCESSDATE
 *           --  SV_CREATIONDATE
 *           --  SV_EXT                  (new: sort by extension)
 *           --  SV_FOLDERSFIRST         (new: sort folders first)
 */

/*
 *@@ xfSortViewOnce:
 *           sorts the content of given folder.
 *           As opposed to XFolder::xfSetFldrSort, this does not
 *           change the folder sort settings, but only sorts the view
 *           once.
 *           This is used by the context menu entries in the "Sort"
 *           menu.
 *
 *           ulSort must be one of the following:
 *           --  SV_NAME
 *           --  SV_TYPE
 *           --  SV_CLASS                (new: sort by object class)
 *           --  SV_REALNAME
 *           --  SV_SIZE
 *           --  SV_LASTWRITEDATE
 *           --  SV_LASTACCESSDATE
 *           --  SV_CREATIONDATE
 *           --  SV_EXT                  (new: sort by extension)
 *           --  SV_FOLDERSFIRST         (new: sort folders first)
 */

SOM_Scope BOOL  SOMLINK xf_xfSortViewOnce(XFolder *somSelf, HWND hwndFrame,
                                         USHORT usSort)
{
    BOOL rc = FALSE;
    HWND hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfSortByExt");

    if (hwndCnr) {
        CNRINFO CnrInfo;
        ULONG   ulStyle = 0;

        winhQueryCnrInfo(hwndCnr, CnrInfo);

        if ((CnrInfo.flWindowAttr & (CV_ICON | CV_TREE)) == CV_ICON) {
            // for some reason, icon views need to have "auto arrange" on,
            // or nothing will happen
            ulStyle = WinQueryWindowULong(hwndCnr, QWL_STYLE);
            WinSetWindowULong(hwndCnr, QWL_STYLE, ulStyle | CCS_AUTOPOSITION);
        }

        // send sort msg with proper sort (comparison) func
        WinSendMsg(hwndCnr, CM_SORTRECORD,
                (MPARAM)QuerySortFunc(usSort),
                MPNULL);

        if ((CnrInfo.flWindowAttr & (CV_ICON | CV_TREE)) == CV_ICON)
            // restore old cnr style
            WinSetWindowULong(hwndCnr, QWL_STYLE, ulStyle);

        rc = TRUE;
    }
    return (rc);
}

/*
 *@@ xfQueryIconPos:
 *           this new instance method retrieves the icon position of an
 *           object in a currently populated folder; you need to initialize
 *           the pICONPOS structure first, whose size you need to pass in
 *           ulICONPOSSize; the ICONPOS data will be copied to pipReturn.
 *           The method returns FALSE if something went wrong.
 */

SOM_Scope BOOL  SOMLINK xf_xfGetIconPos(XFolder *somSelf,
                                           WPObject *pObject,
                                           PBYTE pICONPOS, USHORT usICONPOSSize,
                                           PICONPOS pipReturn)
{
    USHORT   usStartPos;
    PICONPOS pip;

    CHAR     szKey[100],
             szPath[CCHMAXPATH];

    HOBJECT  hObject = _wpQueryHandle(pObject);
    PSZ      pszClass = _somGetClassName(pObject);

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfGetIconPos");

    usStartPos = 21; // with OS/2 2.1 and above, Henk Kelder says

    /* first step: the icon position of each object within a given
       .ICONPOS EA starts with a string identifying the object; so
       first, we need to compose this string depending on the type
       of the passed object */
    if (IsObjectAbstract(hObject))
        sprintf(szKey, "%s:A%lX", pszClass, LOUSHORT(hObject));
    else
    {   // file system object
        WPFileSystem    *pobjFile;
        if (pobjFile = _wpclsQueryObject(_WPObject, hObject)) {
            if (_wpQueryFilename(pobjFile, szPath, FALSE)) {
                sprintf(szKey, "%s:%c%s", pszClass,
                   (_somIsA(pobjFile, _WPFolder) ? 'D' : 'F'),
                   szPath);
            } else
                return FALSE;
        } else
            return FALSE;

    }

    // now we have the key to search for within the .ICONPOS EA

    if ((pICONPOS) && (pipReturn))
    {
        /* now we go through the .ICONPOS data that was given to us
           and check each item in there if it matches the key we
           composed above */
        for (pip = (PICONPOS)( pICONPOS + usStartPos );
            (PBYTE)pip < pICONPOS + usICONPOSSize; )
        {
            if (!stricmp(pip->szIdentity, szKey))
            {
                *pipReturn = *pip;
                return TRUE;
            }
            pip = (PICONPOS)( (PBYTE)pip + sizeof(POINTL) + strlen(pip->szIdentity) + 1 );
        }
    }

    return FALSE;
}

/*
 * GetICONPOS:
 *
 */

PICONPOS GetICONPOS(PORDEREDLISTITEM poli, PSORTBYICONPOS psip)
{
    PICONPOS                    pip;
    CHAR                        *p;
    USHORT usStartPos = 21;  // OS/2 2.1 and above, says Henk

    // ICONPOS is defined in PMWP.H as folllows:
    //     typedef struct _ICONPOS
    //     {
    //        POINTL  ptlIcon;
    //        CHAR    szIdentity[1];
    //     } ICONPOS;
    //     typedef ICONPOS *PICONPOS;

    // now compare all the objects in the .ICONPOS structure
    // to the identity string of the search object

    for (   pip = (PICONPOS)( psip->pICONPOS + usStartPos );
            (PBYTE)pip < (psip->pICONPOS + psip->usICONPOSSize);
        )
    {   // pip now points to an ICONPOS structure

        // go beyond the class name
        p = strchr(pip->szIdentity, ':');
        if (p) {

            /* #ifdef DEBUG_ORDEREDLIST
                _Pmpf(("      Identities: %s and %s...", p, poli->szIdentity));
            #endif */

            if (stricmp(p, poli->szIdentity) == 0)
                // object found: return the ICONPOS address
                return (pip);
            else
                // not identical: go to next ICONPOS structure
                pip = (PICONPOS)( (PBYTE)pip + sizeof(POINTL) + strlen(pip->szIdentity) + 1 );
        } else
            break;
    }
    return (NULL);
}

/*
 * fnSortByICONPOS:
 *      callback sort function for lstSort to sort the
 *      menu items according to a folder's ICONPOS EAs.
 *      pICONPOS points to the ICONPOS data.
 */

SHORT fnSortByICONPOS(PVOID pItem1, PVOID pItem2, PVOID psip)
{
    /* #ifdef DEBUG_ORDEREDLIST
        _Pmpf(("    Comparing %s and %s...",
            _wpQueryTitle(((PORDEREDLISTITEM)pItem1)->pObj),
            _wpQueryTitle(((PORDEREDLISTITEM)pItem2)->pObj)
        ));
    #endif */
    if ((pItem1) && (pItem2)) {
        PICONPOS pip1 = GetICONPOS(((PORDEREDLISTITEM)pItem1), psip),
                 pip2 = GetICONPOS(((PORDEREDLISTITEM)pItem2), psip);

        if ((pip1) && (pip2))
            if (pip1 < pip2)
                return (-1);
            else return (1);
        else if (pip1)
            return (-1);
        else if (pip2)
            return (1);
    }

    return (0);
}

/*
 * InvalidateOrderedContent:
 *
 */

BOOL InvalidateOrderedContent(XFolderData *somThis)
{
    BOOL brc = FALSE;

    if (_hmtxOrderedContent == NULLHANDLE) {
        DosCreateMutexSem(NULL, // unnamed
            &(_hmtxOrderedContent),
            0,                  // unshared
            FALSE);             // unowned
    }

    if (_pliOrderedContentFirst)
    {
        BOOL fSemOwned = FALSE;
        TRY_LOUD(excpt1)
        {
            fSemOwned = (DosRequestMutexSem(_hmtxOrderedContent, 4000) == NO_ERROR);

            if (fSemOwned)
                lstClear((PLISTITEM*)&(_pliOrderedContentFirst),
                         (PLISTITEM*)&(_pliOrderedContentLast));
            else
                _Pmpf(("Could not request sem, InvalidateOrderedContent"));

            brc = TRUE;
        }
        CATCH(excpt1) { } END_CATCH;

        if (fSemOwned) {
            DosReleaseMutexSem(_hmtxOrderedContent);
            fSemOwned = FALSE;
        }
    }
    return (brc);
}

/*
 *@@ xfInvalidateOrderedContent:
 *           this deletes the ordered content list so that it will
 *           be rebuilt when it's needed. This is necessary if
 *           objects in the folder have changed.
 */

SOM_Scope BOOL  SOMLINK xf_xfInvalidateOrderedContent(XFolder *somSelf)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfInvalidateOrderedContent");

    return (InvalidateOrderedContent(somThis));
}

/*
 * UpdateOrderedContent:
 *      this is called by the xfUpdateOrderedContent method
 *      just below. We needed to extract this code because
 *      it is accessed from xfQueryOrderedContent without
 *      the semaphore handling.
 */

BOOL UpdateOrderedContent(XFolder *somSelf, XFolderData *somThis, BOOL fReqSem)
{
    BOOL                 brc = FALSE;
    PORDEREDLISTITEM     poliNew;
    PEABINDING           peab;
    WPObject             *pObj;
    SORTBYICONPOS        sip;
    ULONG                ulrc;

    BOOL fSemOwned = FALSE;

    if (_hmtxOrderedContent == NULLHANDLE) {
        DosCreateMutexSem(NULL, // unnamed
            &(_hmtxOrderedContent),
            0,                  // unshared
            FALSE);             // unowned
    }

    TRY_LOUD(excpt1)
    {
        if (fReqSem)
            fSemOwned = (DosRequestMutexSem(_hmtxOrderedContent, 4000) == NO_ERROR);

        if ((fSemOwned) || (!fReqSem))
        {
            // first clean up
            if (_pliOrderedContentFirst)
                lstClear((PLISTITEM*)&(_pliOrderedContentFirst),
                         (PLISTITEM*)&(_pliOrderedContentLast));

            if (_pICONPOS)
            {
                _wpFreeMem(somSelf, _pICONPOS);
                _pICONPOS = NULL;
            }

            // build new list:

            // get the folder's content as the WPS delivers it.
            // This is unsorted. Apparently, the WPS returns items
            // in the following order:
            // a)   first: file-system objects in the order returned
            //      by the file system (i.e. alphabetically on HPFS)
            // b)   then all abstract objects in the order they were
            //      placed in this folder.

            for (   pObj = _wpQueryContent(somSelf, NULL, (ULONG)QC_FIRST);
                    (pObj);
                    pObj = _wpQueryContent(somSelf, pObj, (ULONG)QC_NEXT)
                )
            {
                poliNew = malloc(sizeof(ORDEREDLISTITEM));
                poliNew->pObj = pObj;

                // Each ICONPOS struct's identity string has the following format:
                // <class>:<t><identity>
                // with: <class> being the class of the object
                //       <t> == A for abstracts, D for folders, F for files
                //       <identity> for abstract objects: the handle
                //                  for file-system objects: the filename

                // now create the identity string for the search object
                if (_somIsA(pObj, _WPAbstract)) {
                    // for abstract objects, this is the low word
                    // of the object handle
                    HOBJECT hobjSearch = _wpQueryHandle(pObj);
                    sprintf(poliNew->szIdentity, ":A%lX", (hobjSearch & 0xFFFF));
                } else {
                    // for file-system objects, this is the object's real name
                    ULONG   ulSize = sizeof(poliNew->szIdentity)-2;
                    if (_somIsA(pObj, _WPFolder))
                        strcpy(poliNew->szIdentity, ":D");
                    else
                        strcpy(poliNew->szIdentity, ":F");
                    // append real name
                    _wpQueryRealName(pObj, (poliNew->szIdentity)+2, &ulSize, FALSE);
                }

                lstAppendItem((PLISTITEM*)&(_pliOrderedContentFirst),
                         (PLISTITEM*)&(_pliOrderedContentLast),
                         (PLISTITEM)poliNew);
            }

            // read .ICONPOS extended attributes of this folder
            _wpQueryFilename(somSelf, sip.szRealName, TRUE);
            if (peab = eaPathReadOneByName(sip.szRealName, ".ICONPOS"))
            {
                //  typedef struct
                //  {
                //    BYTE bFlags;
                //    BYTE bNameLength;
                //    USHORT usValueLength;
                //    PSZ pszName;
                //    PSZ pszValue;
                //  } EABINDING, *PEABINDING;

                _pICONPOS = _wpAllocMem(somSelf, peab->usValueLength+100, &ulrc);

                if (_pICONPOS) {
                    memcpy(_pICONPOS, peab->pszValue+4, peab->usValueLength-3);
                    _ulICONPOSSize = (peab->usValueLength)-5;
                    eaFreeBinding(peab);

                    // finally, we have the ICONPOS data in _pICONPOS;
                    // now we pass the ICONPOS data to the sort function
                    // defined above

                    #ifdef DEBUG_ORDEREDLIST
                        _Pmpf(("  Sorting..."));
                    #endif

                    sip.pICONPOS = _pICONPOS;
                    sip.usICONPOSSize = _ulICONPOSSize;

                    lstQuickSort((PLISTITEM*)&(_pliOrderedContentFirst),
                             (PLISTITEM*)&(_pliOrderedContentLast),
                             fnSortByICONPOS,
                             &sip);

                }
            }

            brc = TRUE;
        } else
            _Pmpf(("Could not request sem, UpdateOrderedContent, Req: %d, hmtx: 0x%lX",
                            fReqSem,
                            _hmtxOrderedContent));
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(_hmtxOrderedContent);
        fSemOwned = FALSE;
    }

    return (brc);
}

/*
 *@@ xfUpdateOrderedContent:
 *           this new instance methods builds or updates a list of the
 *           folder contents in memory. The root of the list is saved
 *           in the folder instance data.
 *
 *           This method is called by XFolder::xfQueryOrderedContent if the
 *           list in memory has not been built yet. You do not need to call this
 *           method manually.
 */

SOM_Scope BOOL  SOMLINK xf_xfUpdateOrderedContent(XFolder *somSelf)
{
    BOOL            fSemOwned = FALSE;

    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfUpdateOrderedContent");

    #ifdef DEBUG_ORDEREDLIST
        _Pmpf(("Entering xf_xfUpdateOrderedContent for %s", _wpQueryTitle(somSelf) ));
    #endif

    return (UpdateOrderedContent(somSelf, somThis,
            TRUE)); // request semaphore
}

/*
 *@@ xfQueryOrderedContent:
 *           this new instance method has the same parameters als wpQueryContent,
 *           but it returns items from the list built by
 *           XFolder::xfUpdateOrderedContent.
 *
 *           As opposed to the list returned by wpQueryContent, the objects
 *           in this list are in the same order as in the name or details views,
 *           because the list is built according to the .ICONPOS EAs.
 */

SOM_Scope WPObject*  SOMLINK xf_xfQueryOrderedContent(XFolder *somSelf,
                                                         WPObject* Object,
                                                         ULONG ulOption)
{
    WPObject                    *pobjReturn = NULL;
    PORDEREDLISTITEM            poli;
    BOOL                        fSemOwned = FALSE;

    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfQueryOrderedContent");

    #ifdef DEBUG_ORDEREDLIST
        _Pmpf(("xfQueryOrderedContent for %s, ulOption: %d",
                _wpQueryTitle(somSelf),
                ulOption));
    #endif

    xwpsCheckIfPopulated(somSelf);

    if (    (ulOption == QC_FIRST)
         && (_pliOrderedContentFirst == NULL)
       )
    {
        UpdateOrderedContent(somSelf, somThis,
                TRUE); // request semaphore
    }

    TRY_LOUD(excpt1)
    {
        if (_pliOrderedContentFirst)
        {
            fSemOwned = (DosRequestMutexSem(_hmtxOrderedContent, 4000) == NO_ERROR);
            if (fSemOwned)
            {
                poli = _pliOrderedContentFirst;
                if (poli) {
                    switch (ulOption) {
                        case QC_FIRST: {
                            #ifdef DEBUG_ORDEREDLIST
                                _Pmpf(("  returning first"));
                            #endif
                            pobjReturn = poli->pObj;
                        break; }

                        case QC_NEXT: {
                            while (poli)
                                if (poli->pObj == Object)
                                {
                                    BOOL fValid = TRUE;
                                    #ifdef DEBUG_ORDEREDLIST
                                        _Pmpf(("  found just-before"));
                                        _Pmpf(("    Title: %s",
                                                _wpQueryTitle(poli->pObj) ));
                                    #endif
                                    if (poli->pNext) {
                                        pobjReturn = (poli->pNext->pObj);

                                        // check validity of object
                                        fValid = xwpsCheckObject(pobjReturn);
                                        if (fValid)
                                            fValid = (_wpQueryTitle(pobjReturn) != NULL);

                                    }

                                    if (fValid) {
                                        #ifdef DEBUG_ORDEREDLIST
                                            _Pmpf(("  found valid next"));
                                            if (pobjReturn)
                                                _Pmpf(("    Title: %s",
                                                    _wpQueryTitle(pobjReturn) ));
                                            else
                                                _Pmpf(("    NULL"));
                                            _Pmpf(("  Returning."));
                                        #endif
                                        break; // while
                                    } else {
                                        // invalid object: restart search, since
                                        // we've changed the list
                                        #ifdef DEBUG_ORDEREDLIST
                                            _Pmpf(("  rebuilding list"));
                                        #endif
                                        // if the object we found is not a valid SOM object
                                        // any more, we will delete it from the list
                                        /* lstRemoveItem((PLISTITEM*)&(_pliOrderedContentFirst),
                                                 (PLISTITEM*)&(_pliOrderedContentLast),
                                                 (PLISTITEM)poli,
                                                 0, 0); // we already have the semaphore
                                                 */

                                        UpdateOrderedContent(somSelf, somThis,
                                                FALSE);     // we already have the semaphore

                                        poli = _pliOrderedContentFirst;
                                    }
                                } // end if (poli->pObj == Object)
                                else {
                                    #ifdef DEBUG_ORDEREDLIST
                                        _Pmpf(("  searching next"));
                                    #endif
                                    poli = poli->pNext;
                                }
                        break; }
                    }
                }
            }
            else
                _Pmpf(("Could not request sem, xfQueryOrderedContent"));
        }
        else
            pobjReturn = _wpQueryContent(somSelf, Object, ulOption);
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(_hmtxOrderedContent);
        fSemOwned = FALSE;
    }

    return (pobjReturn);
}

/*
 *@@ xfBeginProcessOrderedContent:
 *           this new instance method uses XFolder::xfQueryOrderedContent to open
 *           all the objects in the given folder. This method returns immediately;
 *           further processing is done in the background Worker thread.
 *
 *           Parameters:
 *           -- ULONG ulTiming:     the time to wait between starting objects
 *                                  in milliseconds; if set to 0, this function
 *                                  will work in "Wait" mode, i.e. the next
 *                                  object will open the next object only if
 *                                  the previous one has been closed again
 *                                  (e.g. for XShutdown folder)
 *           -- PFNWP pfnwpCallback: a callback routine which will be called on
 *                                  every object which is started;
 *           -- ULONG ulCallbackParam: the first parameter to pass to this proc.
 *
 *           pfnwpCallback will be passed the following parameters:
 *           -- HWND  hwnd:     will not be a hwnd, but ulCallbackParam;
 *           -- ULONG msg:      will contain the current object of the
 *                              folder on which this mthd is invoked;
 *                              this must be cast manually to (WPObject*)
 *           -- MPARAM mp1:     contains the current object count
 *                              (starting with 1 and increasing for each
 *                              subsequent object);
 *           -- MPARAM mp2:     the (constant) number of objects in the folder.
 *
 *           This callback func may be used for implementing a progress bar.
 *           You can use ulCallbackParam for the msg window handle and display
 *           the progress according to mp1 and mp2; the object title may be
 *           obtained by calling _wpQueryTitle((WPObject*)msg).
 *
 *           After all objects have been processed, the callback will called
 *           once more with msg == 0 to allow cleaning up. If the folder does
 *           not contain any objects, callback will only be called this one time.
 *
 *           If you're running in "Wait" mode, the callback must return the
 *           hwnd of the object which you must have opened with wpViewObject,
 *           so that XFolder can wait for this window to be closed.
 *
 *           Note that even though the processing of the folder is done in the
 *           Worker thread, the callback func is actually called on the main
 *           (Workplace) thread, so you can create your window there and have
 *           work done on it without having to worry about thread safety. This
 *           is implemented because wpViewObject is having problems when not
 *           being called in the Workplace thread.
 *
 *           This method returns a handle which may be used in
 *           XFolder::xfCancelProcessOrderedContent to abort processing,
 *           or 0 upon errors.
 */

SOM_Scope ULONG  SOMLINK xf_xfBeginProcessOrderedContent(XFolder *somSelf,
                                                           ULONG ulTiming,
                                                           PFNWP pfnwpCallback,
                                                           ULONG ulCallbackParam)
{
    PPROCESSCONTENTINFO pPCI;
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfBeginProcessOrderedContent");

    pPCI = (PPROCESSCONTENTINFO)malloc(sizeof(PROCESSCONTENTINFO));

    if (pPCI) {
        pPCI->ulObjectNow = 0;
        pPCI->ulTiming = ulTiming;
        pPCI->pfnwpCallback = pfnwpCallback;
        pPCI->ulCallbackParam = ulCallbackParam;
        pPCI->fCancelled = FALSE;

        xthrPostWorkerMsg(WOM_PROCESSORDEREDCONTENT, (MPARAM)somSelf, pPCI);

        return ((ULONG)pPCI);
    }
    return (0);
}

/*
 *@@ xfCancelProcessOrderedContent:
 *           this method cancels a folder content process started with
 *           XFolder::xfBeginProcessOrderedContent; hPOC must the handle
 *           returned by that function.
 *
 *           Warning: After folder content processing has finished, hPOC is
 *           no longer valid, and calling this function then will simply
 *           crash the WPS. As a result, your callback function MUST keep
 *           track if hPOC is still valid; after the callback func has been
 *           called with msg == 0, calling this function is no longer allowed.
 */

SOM_Scope BOOL  SOMLINK xf_xfCancelProcessOrderedContent(XFolder *somSelf,
                                                            ULONG hPOC)
{
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfCancelProcessOrderedContent");

    ((PPROCESSCONTENTINFO)hPOC)->fCancelled = TRUE;

    return (TRUE);
}

/*
 *@@ xfMakeFavoriteFolder:
 *           if fInsert is TRUE, this folder will be made a "favorite folder",
 *           i.e. added to all context menus; if FALSE, it will be removed.
 */

SOM_Scope ULONG  SOMLINK xf_xfMakeFavoriteFolder(XFolder *somSelf,
                                                    BOOL fInsert)
{
    BOOL    fSemOwned = FALSE;
    CHAR    szFavoriteFolders[1000] = "";
    PCONTENTMENULISTITEM pcmli, pFound = NULL;

    // XFolderData     *somThis = XFolderGetData(somSelf);
    M_XFolderData   *somThat = M_XFolderGetData(_XFolder);

    XFolderMethodDebug("XFolder","xf_xfMakeFavoriteFolder");

    TRY_LOUD(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxFavoriteFolders, 4000) == NO_ERROR);
        if (fSemOwned)
        {
            pcmli = pcmliFavoriteFolders;
            while (pcmli)
                if (pcmli->pFolder == somSelf) {
                    pFound = pcmli;
                    break;
                } else
                    pcmli = pcmli->pNext;

            if (fInsert)
            {
                if (!pFound) {
                    pcmli = malloc(sizeof(CONTENTMENULISTITEM));
                    pcmli->pFolder = somSelf;
                    lstAppendItem((PLISTITEM*)&(pcmliFavoriteFolders), NULL,
                        (PLISTITEM)pcmli);
                }
            }
            else
                lstRemoveItem((PLISTITEM*)&(pcmliFavoriteFolders), NULL,
                        (PLISTITEM)pFound);

            // write list to INI as a string of handles
            pcmli = pcmliFavoriteFolders;
            if (pcmli)
            {
                while (pcmli) {
                    sprintf(szFavoriteFolders+strlen(szFavoriteFolders),
                        "%lX ", _wpQueryHandle(pcmli->pFolder));
                    pcmli = pcmli->pNext;
                }

                PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_FAVORITEFOLDERS,
                            szFavoriteFolders);
            } else
            {
                PrfWriteProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_FAVORITEFOLDERS,
                            NULL, 0);
            }
        }
    }
    CATCH(excpt1) {} END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(hmtxFavoriteFolders);
        fSemOwned = FALSE;
    }

    return 0;
}

/*
 *@@ xfIsFavoriteFolder:
 *           returns TRUE if somSelf is on the list of "favorite" folders.
 */

SOM_Scope BOOL  SOMLINK xf_xfIsFavoriteFolder(XFolder *somSelf)
{
    PCONTENTMENULISTITEM pcmli;
    BOOL                 rc = FALSE,
                         fSemOwned = FALSE;

    // XFolderData     *somThis = XFolderGetData(somSelf);
    M_XFolderData   *somThat = M_XFolderGetData(_XFolder);
    XFolderMethodDebug("XFolder","xf_xfIsFavoriteFolder");

    TRY_LOUD(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxFavoriteFolders, 4000) == NO_ERROR);
        if (fSemOwned)
        {
            pcmli = pcmliFavoriteFolders;
            while (pcmli) {
                if (pcmli->pFolder == somSelf) {
                    rc = TRUE;
                    break;
                } else
                    pcmli = pcmli->pNext;
            }
        }
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(hmtxFavoriteFolders);
        fSemOwned = FALSE;
    }
    return (rc);
}

/*
 *@@ xfSetQuickOpen:
 *           if fQuickOpen == TRUE, somSelf will automatically be
 *           populated at WPS bootup.
 */

SOM_Scope ULONG  SOMLINK xf_xfSetQuickOpen(XFolder *somSelf,
                                           BOOL fQuickOpen)
{
    BOOL    fSemOwned = FALSE;
    CHAR    szQuickOpenFolders[1000] = "";
    PCONTENTMENULISTITEM pcmli, pFound = NULL;

    // XFolderData     *somThis = XFolderGetData(somSelf);
    M_XFolderData   *somThat = M_XFolderGetData(_XFolder);
    XFolderMethodDebug("XFolder","xf_xfSetQuickOpen");

    TRY_LOUD(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxQuickOpenFolders, 4000) == NO_ERROR);
        if (fSemOwned)
        {
            pcmli = pcmliQuickOpenFolders;
            while (pcmli)
                if (pcmli->pFolder == somSelf) {
                    pFound = pcmli;
                    break;
                } else
                    pcmli = pcmli->pNext;

            if (fQuickOpen)
            {
                if (!pFound) {
                    pcmli = malloc(sizeof(CONTENTMENULISTITEM));
                    pcmli->pFolder = somSelf;
                    lstAppendItem((PLISTITEM*)&(pcmliQuickOpenFolders), NULL,
                        (PLISTITEM)pcmli);
                }
            }
            else
                lstRemoveItem((PLISTITEM*)&(pcmliQuickOpenFolders), NULL,
                        (PLISTITEM)pFound);

            // write list to INI as a string of handles
            pcmli = pcmliQuickOpenFolders;
            if (pcmli) {
                while (pcmli) {
                    sprintf(szQuickOpenFolders+strlen(szQuickOpenFolders),
                        "%lX ", _wpQueryHandle(pcmli->pFolder));
                    pcmli = pcmli->pNext;
                }
                PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_QUICKOPENFOLDERS,
                            szQuickOpenFolders);
            } else {
                PrfWriteProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_QUICKOPENFOLDERS,
                            NULL, 0);
            }
        }
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(hmtxQuickOpenFolders);
        fSemOwned = FALSE;
    }

    return 0;
}

/*
 *@@ xfQueryQuickOpen:
 *           returns TRUE if somSelf has the QuickOpen feature ON.
 */

SOM_Scope BOOL  SOMLINK xf_xfQueryQuickOpen(XFolder *somSelf)
{
    PCONTENTMENULISTITEM pcmli;
    BOOL                 rc = FALSE,
                         fSemOwned = FALSE;

    // XFolderData     *somThis = XFolderGetData(somSelf);
    M_XFolderData   *somThat = M_XFolderGetData(_XFolder);
    XFolderMethodDebug("XFolder","xf_xfQueryQuickOpen");

    TRY_LOUD(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxQuickOpenFolders, 4000) == NO_ERROR);
        if (fSemOwned)
        {
            pcmli = pcmliQuickOpenFolders;
            while (pcmli) {
                if (pcmli->pFolder == somSelf) {
                    rc = TRUE;
                    break;
                } else
                    pcmli = pcmli->pNext;
            }
        }
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(hmtxQuickOpenFolders);
        fSemOwned = FALSE;
    }

    return (rc);
}

/*
 *@@ xfShowStatusBar:
 *           depending on fShow, this shows or hides the folder status
 *           bar for a certain folder.
 *
 *           Parameters:
 *           --  psli:      list item of this folder frame from the global
 *                          linked list (of PSUBCLASSEDLISTITEM's); this
 *                          item contains the folder frame window handle
 *           --  fShow:     show / hide flag
 *
 *           This func returns the hwnd of the status bar or NULL if calling
 *           the func was useless, because the status bar was already
 *           (de)activated.
 *           Note that this does _not_ change the folder's visibility
 *           settings, but only shows or hides the status bar "factually"
 *           by reformatting the folder frame's PM windows.
 *           This function _must_ be called only from the same thread
 *           where the folder frame window is running (normally TID 1),
 *           otherwise the WPS will crash or PM will hang.
 *
 *           Because of all this, call XFolder::xfSetStatusBarVisibility
 *           instead if you wish to change folder status bars. That function
 *           will do the display also.
 */

SOM_Scope HWND  SOMLINK xf_xfShowStatusBar(XFolder *somSelf,
                                           PVOID psli,
                                           BOOL fShow)
{
    HWND hrc = NULLHANDLE;
    BOOL fUpdateNotebook = FALSE;

    CHAR    szFolderPosKey[50];
    ULONG   ulIni;
    ULONG   cbIni;

    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfShowStatusBar");

    #ifdef DEBUG_STATUSBARS
        _Pmpf(("xfShowStatusBar %d", fShow));
    #endif

    if (psli) {
        // we had to declare psli as a PVOID because
        // SOM does not know the PSUBCLASSEDLISTITEM struct;
        // so we do the conversion per macro
        #define psli2 ((PSUBCLASSEDLISTITEM)psli)
        // CHAR szINIKey[50];
        // HOBJECT hObj = _wpQueryHandle(somSelf);
        // ULONG ulDummy;

        if (fShow)
        {
            // show status bar

            if (psli2->hwndStatusBar) { // already activated: update only
                WinPostMsg(psli2->hwndStatusBar, STBM_UPDATESTATUSBAR, MPNULL, MPNULL);
                // and quit
            }
            // else create status bar as a static control
            // (which will be subclassed below)
            else if (psli2->hwndStatusBar = WinCreateWindow(
                            psli2->hwndFrame,        // parent
                            WC_STATIC,              // wnd class
                            (cmnQueryNLSStrings())->pszPopulating, // title
                            (SS_TEXT | DT_LEFT | DT_VCENTER // wnd style flags
                                | DT_ERASERECT
                                | WS_VISIBLE | WS_CLIPSIBLINGS),
                            0L, 0L, -1L, -1L,
                            psli2->hwndFrame,        // owner
                            HWND_BOTTOM,
                            0x9001,                 // ID
                            (PVOID)NULL,
                            (PVOID)NULL))
            {
                BOOL    fInflate = FALSE;
                SWP     swp;
                PSZ     pszStatusBarFont = cmnQueryStatusBarSetting(SBS_STATUSBARFONT);

                // set up window data (QWL_USER) for status bar
                PSTATUSBARDATA psbd = malloc(sizeof(STATUSBARDATA));
                psbd->somSelf    = somSelf;
                psbd->psli       = psli2;
                psbd->idTimer    = 0;
                psbd->fDontBroadcast = TRUE;
                            // prevents broadcasting of WM_PRESPARAMSCHANGED
                psbd->fFolderPopulated = FALSE;
                            // suspends updating until folder is populated;
                WinSetWindowULong(psli2->hwndStatusBar, QWL_USER, (ULONG)psbd);

                // subclass static control to make it a status bar
                psbd->pfnwpStatusBarOriginal = WinSubclassWindow(psli2->hwndStatusBar,
                        fnwpStatusBar);
                WinSetPresParam(psli2->hwndStatusBar, PP_FONTNAMESIZE,
                     (ULONG)strlen(pszStatusBarFont) + 1, (PVOID)pszStatusBarFont);

                // now "inflate" the folder frame window if this is the first
                // time that this folder view has been opened with a status bar;
                // if we didn't do this, the folder frame would be too small
                // for the status bar and scroll bars would appear. We store
                // a flag in the folder's instance data for each different view
                // that we've inflated the folder frame, so that this happens
                // only once per view.

                // From wpobject.h:
                // #define VIEW_CONTENTS      0x00000001
                // #define VIEW_SETTINGS      0x00000002
                // #define VIEW_HELP          0x00000004
                // #define VIEW_RUNNING       0x00000008
                // #define VIEW_DETAILS       0x00000010
                // #define VIEW_TREE          0x00000020

                sprintf(szFolderPosKey, "%d@XFSB",
                            _wpQueryHandle(psli2->pRealObject));
                cbIni = sizeof(ulIni);
                if (PrfQueryProfileData(HINI_USER,
                            WPINIAPP_FOLDERPOS,     // "PM_Workplace:FolderPos"
                            szFolderPosKey,
                            &ulIni,
                            &cbIni) == FALSE)
                    ulIni = 0;

                if (ulIni & (   (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                              : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                              : VIEW_DETAILS
                            ))
                    fInflate = FALSE;
                else
                    fInflate = TRUE;

                // _Pmpf(( "Old _ulSBInflatedFrame: %lX", _ulSBInflatedFrame ));
                /* if (   (  _ulSBInflatedFrame
                          & (   (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                              : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                              : VIEW_DETAILS
                            )
                   ) == 0) */

                // the folder pos entries in OS2.INI have the following
                // format:
                // <handle>@<view>
                // with:
                //      handle being the decimal string of the five-digit
                //             object handle (2xxxx for abstract objects);
                //      key    being some code for the view. I'm not
                //             perfectly sure about this, but from my testing
                //             I got the following:
                //               @10    Icon view folder pos
                //               @1010  Tree view folder pos
                //               @1020  Details view folder pos
                //               @      if any fonts were changed;
                //                      might be a WPS bug (?!?)

                // so now we check OS2.INI if the key for our view exists
                // already, ie. the WPS has already saved a folder pos for
                // this folder view
                /* sprintf(szFolderPosKey, "%d@%s",
                            _wpQueryHandle(psli2->pRealObject),
                            (psli2->ulView == OPEN_CONTENTS) ? "10"
                            : (psli2->ulView == OPEN_TREE) ? "1010"
                            : "1020"
                        );
                if (PrfQueryProfileSize(HINI_USER,
                            WPINIAPP_FOLDERPOS,     // "PM_Workplace:FolderPos"
                            szFolderPosKey,
                            &ulDummy)
                        == FALSE)
                    // key does not exist: inflate!
                    fInflate = TRUE;
                else
                    fInflate = FALSE; */

                if (fInflate)
                {
                    // this view has not been inflated yet:
                    // inflate now and set flag for this view

                    ULONG ulStatusBarHeight = cmnQueryStatusBarHeight();
                    WinQueryWindowPos(psli2->hwndFrame, &swp);
                    // inflate folder frame
                    WinSetWindowPos(psli2->hwndFrame, 0,
                                swp.x, (swp.y - ulStatusBarHeight),
                                swp.cx, (swp.cy + ulStatusBarHeight),
                                SWP_MOVE | SWP_SIZE);

                    // mark this folder view as "inflated" in OS2.INI
                    ulIni |= (   (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                               : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                               : VIEW_DETAILS
                             );
                    PrfWriteProfileData(HINI_USER,
                            WPINIAPP_FOLDERPOS,     // "PM_Workplace:FolderPos"
                            szFolderPosKey,
                            &ulIni,
                            sizeof(ulIni));

                    // finally, set a flag for the subclassed folder frame
                    // window proc that this folder view needs no additional scrolling
                    // (this is evaluated in WM_FORMATFRAME msgs)
                    psli2->fNeedCnrScroll = FALSE;
                }
                else
                    // if the folder frame has been inflated already,
                    // WM_FORMATFRAME _will_ have to scroll the container.
                    // We do this for icon views only.
                    if (psli2->ulView == OPEN_CONTENTS)
                        psli2->fNeedCnrScroll = TRUE;

                // enforce reformatting / repaint of frame window
                WinSendMsg(psli2->hwndFrame, WM_UPDATEFRAME, (MPARAM)0, MPNULL);

                // update status bar contents
                WinPostMsg(psli2->hwndStatusBar, STBM_UPDATESTATUSBAR, MPNULL, MPNULL);

                hrc = psli2->hwndStatusBar;
                fUpdateNotebook = TRUE;
            }
        } else {
            // hide status bar:
            if (psli2->hwndStatusBar) {
                SWP     swp;
                HWND    hwndStatus = psli2->hwndStatusBar;
                BOOL    fDeflate = FALSE;

                psli2->hwndStatusBar = 0;
                WinSendMsg(psli2->hwndFrame, WM_UPDATEFRAME, (MPARAM)0, MPNULL);
                WinDestroyWindow(hwndStatus);

                // decrease the size of the frame window by the status bar height,
                // if we did this before
                // _Pmpf(( "Old _ulSBInflatedFrame: %lX", _ulSBInflatedFrame ));
                /* if ( (  _ulSBInflatedFrame
                      & (   (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                          : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                          : VIEW_DETAILS
                        )
                     ) != 0) */
                sprintf(szFolderPosKey, "%d@XFSB",
                            _wpQueryHandle(psli2->pRealObject));
                cbIni = sizeof(ulIni);
                if (PrfQueryProfileData(HINI_USER,
                            WPINIAPP_FOLDERPOS,     // "PM_Workplace:FolderPos"
                            szFolderPosKey,
                            &ulIni,
                            &cbIni) == FALSE)
                    ulIni = 0;

                if (ulIni & (   (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                              : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                              : VIEW_DETAILS
                            ))
                    fDeflate = TRUE;
                else
                    fDeflate = TRUE;

                if (fDeflate)
                {
                    ULONG ulStatusBarHeight = cmnQueryStatusBarHeight();
                    WinQueryWindowPos(psli2->hwndFrame, &swp);
                    WinSetWindowPos(psli2->hwndFrame, 0,
                                swp.x, (swp.y + ulStatusBarHeight),
                                swp.cx, (swp.cy - ulStatusBarHeight),
                                SWP_MOVE | SWP_SIZE);

                    ulIni &= ~(   (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                                : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                                : VIEW_DETAILS
                              );
                    PrfWriteProfileData(HINI_USER,
                            WPINIAPP_FOLDERPOS,     // "PM_Workplace:FolderPos"
                            szFolderPosKey,
                            &ulIni,
                            sizeof(ulIni));



                    // now unset the "inflated" flag for this view
                    /* _ulSBInflatedFrame &= ~(    (psli2->ulView == OPEN_CONTENTS) ? VIEW_CONTENTS
                                              : (psli2->ulView == OPEN_TREE) ? VIEW_TREE
                                              : VIEW_DETAILS
                                           );
                    _wpSaveDeferred(somSelf); */
                    // _Pmpf(( "New _ulSBInflatedFrame: %lX", _ulSBInflatedFrame ));
                }

                hrc = psli2->hwndStatusBar;
                fUpdateNotebook = TRUE;
            }
        }
    }

    return (hrc);
}

/*
 * fncbUpdateStatusBars:
 *      callback func for WOM_UPDATEALLSTATUSBARS in
 *      XFolder Worker thread (xfdesk.c); should not
 *      be called directly (it must reside in this file
 *      to be able to access folder instance data).
 *      This is called by xf(cls)ForEachOpenView, which also passes
 *      the parameters to this func.
 */

MRESULT EXPENTRY fncbUpdateStatusBars(HWND hwndView,        // folder frame
                        ULONG ulActivate,
                                // 1: show/hide status bars according to
                                //    folder/Global settings
                                // 2: reformat status bars (e.g. because
                                //    fonts have changed)
                        MPARAM mpView,                      // OPEN_xxx flag
                        MPARAM mpFolder)                    // folder object
{
    MRESULT             mrc = (MPARAM)FALSE;

    #ifdef DEBUG_STATUSBARS
        _Pmpf(("fncbUpdateStatusBars ulActivate = %d", ulActivate));
    #endif

    if (mpFolder != _wpclsQueryActiveDesktop(_WPDesktop))
    {
        PSUBCLASSEDLISTITEM psli = cmnQueryPSLI(hwndView);
        if (psli)
        {
            if (ulActivate == 2) // "update" flag
                WinPostMsg(psli->hwndSupplObject,
                        SOM_ACTIVATESTATUSBAR,
                        (MPARAM)2,
                        (MPARAM)hwndView);
            else {
                // show/hide flag:
                PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();
                XFolderData *somThis = XFolderGetData(mpFolder);
                BOOL fVisible = (
                                (    (_bStatusBar == STATUSBAR_ON)
                                  || (    (_bStatusBar == STATUSBAR_DEFAULT)
                                       && (pGlobalSettings->StatusBar)
                                     )
                                )
                            &&
                                (   (   ((ULONG)mpView == OPEN_CONTENTS)
                                     && (pGlobalSettings->SBForViews & SBV_ICON)
                                    )
                                 || (   ((ULONG)mpView == OPEN_TREE)
                                     && (pGlobalSettings->SBForViews & SBV_TREE)
                                    )
                                 || (   ((ULONG)mpView == OPEN_DETAILS)
                                     && (pGlobalSettings->SBForViews & SBV_DETAILS)
                                    )
                                )
                            );

                if ( fVisible != (psli->hwndStatusBar != NULLHANDLE) )
                {
                    // visibility changed:
                    // we now post a message to the folder frame's
                    // supplementary object window;
                    // because we cannot mess with the windows on
                    // the Worker thread, we need to have the
                    // status bar created/destroyed/changed on the
                    // folder's thread
                    WinPostMsg(psli->hwndSupplObject,
                            SOM_ACTIVATESTATUSBAR,
                            (MPARAM)( (fVisible) ? 1 : 0 ),      // show or hide
                            (MPARAM)hwndView);
                    mrc = (MPARAM)TRUE;
                }
            }
        }
    }

    return (mrc);
}

/*
 *@@ xfSetStatusBarVisibility:
 *           this new instance method sets the status bar visibility of
 *           a folder.
 *           ulVisibility may be:
 *           -- STATUSBAR_ON:        show status bars
 *           -- STATUSBAR_OFF:       hide status bars
 *           -- STATUSBAR_DEFAULT:   use Global Setting for this folder
 *
 *           If fUpdate is TRUE, XFolder will have the Worker thread search
 *           for open folder views and update their frame controls accordingly.
 *           Otherwise the status bar setting will only be changed internally
 *           for the next time the folder is opened. Might lead to errors.
 *           Returns TRUE if successful.
 */

SOM_Scope BOOL  SOMLINK xf_xfSetStatusBarVisibility(XFolder *somSelf,
                                                    ULONG ulVisibility,
                                                    BOOL fUpdate)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfSetStatusBarVisibility");

    if (_bStatusBar != ulVisibility) {
        #ifdef DEBUG_STATUSBARS
            _Pmpf(( "xfSetStatusBarVisibility: %d", ulVisibility));
        #endif
        _bStatusBar = ulVisibility;

        if (fUpdate) {
            // update open folder views in Worker thread;
            // this will call fncbUpdateStatusBars for each
            // open folder window
            xthrPostWorkerMsg(WOM_UPDATEALLSTATUSBARS,
                    (MPARAM)1,      // show/hide flag
                    MPNULL);
            // update "XFolder" notebook page, if open
            ntbUpdateVisiblePage(somSelf, SP_XFOLDER_FLDR);
        }
    }
    return (TRUE);
}

/*
 *@@ xfQueryStatusBarVisibility:
 *           this new instance method returns the status bar visibility of
 *           a folder:
 *           -- STATUSBAR_ON:        status bars visible
 *           -- STATUSBAR_OFF:       status bars invisible
 *           -- STATUSBAR_DEFAULT:   Global Setting used for this folder
 */

SOM_Scope BOOL  SOMLINK xf_xfQueryStatusBarVisibility(XFolder *somSelf)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfQueryStatusBarVisibility");

    return (_bStatusBar);
}

/*
 *@@ xfForEachOpenView:
 *           this instance method goes through all open views of a folder and calls
 *           pfnwpCallback for each them (as opposed to xfclsForEachOpenView, which
 *           goes through all open folders on the system).
 *
 *           The following params are then passed to pfnwpCallback:
 *           --   hwnd    HWND       hwndView   the hwnd of the view frame window;
 *           --   mp1     ULONG      ulView     the view type (as def'd in wpOpen)
 *           --   mp2     XFolder*   pFolder    somSelf.
 *
 *           This method does not return until all views have been processed.
 *           You might want to call this method in a different thread if the task
 *           will take long.
 *           This method returns TRUE if the callback returned TRUE at least once.
 *           Note on disk objects/root folders: the WPS does not maintain an open
 *           view list for root folders, but only for the corresponding disk object.
 *           xfForEachOpenView will call open disk views also, but the callback
 *           will still be passed the root folder in pFolder!
 */

SOM_Scope BOOL  SOMLINK xf_xfForEachOpenView(XFolder *somSelf,
                                                ULONG ulMsg,
                                                PFNWP pfnwpCallback)
{
    BOOL brc = FALSE;
    WPObject *somSelf2;
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_xfForEachOpenView");

    if (_somIsA(somSelf, _WPRootFolder)) {
        // for disk/root folder views: root folders have no
        // open view, instead the disk object is registered
        // to have the open view. Duh. So we need to find
        // the disk object first
        somSelf2 = _xfclsQueryDiskObject(_XFldDisk, somSelf);
    } else
        somSelf2 = somSelf;

    if (somSelf2) {
        if (_wpFindUseItem(somSelf2, USAGE_OPENVIEW, NULL))
        {   // folder has an open view;
            // now we go search the open views of the folder and get the
            // frame handle of the desired view (ulView) */
            PVIEWITEM   pViewItem;
            for (pViewItem = _wpFindViewItem(somSelf2, VIEW_ANY, NULL);
                pViewItem;
                pViewItem = _wpFindViewItem(somSelf2, VIEW_ANY, pViewItem))
            {
                if ((*pfnwpCallback)(
                        pViewItem->handle,
                        ulMsg,
                        (MPARAM)pViewItem->view,
                        // but even if we have found a disk object
                        // above, we need to pass it the root folder
                        // pointer, because otherwise the callback
                        // might get into trouble
                        (MPARAM)somSelf)
                    == (MPARAM)TRUE)
                brc = TRUE;
            } // end for
        } // end if
    }
    return (brc);
}

/*
 * fncbXFolderInitPage:
 *      "XFolder" page notebook callback function (notebook.c).
 *      Sets the controls on the page according to a folder's
 *      instance settings.
 */

VOID fncbXFolderInitPage(PVOID pnbi,           // notebook info struct
                         ULONG ulExtra)        // INIT_* flags (notebook.h)
{
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    PCREATENOTEBOOKPAGE pcnbp = (PCREATENOTEBOOKPAGE)pnbi;
    XFolderData *somThis = XFolderGetData(pcnbp->somSelf);

    if (ulExtra & CBI_INIT)
    {
        if (pcnbp->pBackup == NULL)
        {
            // first call: backup instance data for "Undo" button;
            // this memory will be freed automatically by the
            // common notebook window function (notebook.c) when
            // the notebook page is destroyed
            pcnbp->pBackup = malloc(sizeof(XFolderData));
            memcpy(pcnbp->pBackup, somThis, sizeof(XFolderData));
        }
    }

    if (ulExtra & CBI_SET)
    {
        winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_FAVORITEFOLDER,
                _xfIsFavoriteFolder(pcnbp->somSelf));

        winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_QUICKOPEN,
                _xfQueryQuickOpen(pcnbp->somSelf));

        winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_SNAPTOGRID,
               (MPARAM)( (_bSnapToGridAllowed == 2)
                        ? pGlobalSettings->AddSnapToGridItem
                        : _bSnapToGridAllowed ));
        winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_FULLPATH,
               (MPARAM)( (_bFullPath == 2)
                        ? pGlobalSettings->FullPath
                        : _bFullPath ));
        winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_ACCELERATORS,
               (MPARAM)( (_bAcceleratorsAllowed == 2)
                        ? pGlobalSettings->Accelerators
                        : _bAcceleratorsAllowed ));
        winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_ENABLESTATUSBAR,
               (MPARAM)( ( (_bStatusBar == STATUSBAR_DEFAULT)
                                    ? pGlobalSettings->StatusBar
                                    : _bStatusBar )
                         // always uncheck for Desktop
                         && (pcnbp->somSelf != _wpclsQueryActiveDesktop(_WPDesktop))
                       ));
    }

    if (ulExtra & CBI_ENABLE)
    {
        // disable items
        winhEnableDlgItem(pcnbp->hwndPage, ID_XSDI_ACCELERATORS,
                    (!(pGlobalSettings->NoSubclassing)));
        // disable for Desktop
        winhEnableDlgItem(pcnbp->hwndPage, ID_XSDI_ENABLESTATUSBAR,
                (   (pcnbp->somSelf != _wpclsQueryActiveDesktop(_WPDesktop))
                 && (!(pGlobalSettings->NoSubclassing))
                ));
    }
}

/*
 * fncbXFolderItemChanged:
 *      "XFolder" page notebook callback function (notebook.c).
 *      Reacts to changes of any of the dialog controls.
 */

MRESULT fncbXFolderItemChanged(PVOID pnbi,  // notebook info
                USHORT usItemID, USHORT usNotifyCode,
                ULONG ulExtra)      // for checkboxes: contains new state
{
    PCREATENOTEBOOKPAGE pcnbp = (PCREATENOTEBOOKPAGE)pnbi;
    XFolderData *somThis = XFolderGetData(pcnbp->somSelf);
    BOOL fUpdate = TRUE;

    switch (usItemID)
    {
        case ID_XSDI_SNAPTOGRID:
            _bSnapToGridAllowed = ulExtra;
        break;

        case ID_XSDI_FULLPATH:
            _bFullPath = ulExtra;
            _xfUpdateAllFrameWndTitles(pcnbp->somSelf);
        break;

        case ID_XSDI_ACCELERATORS:
            _bAcceleratorsAllowed = ulExtra;
        break;

        case ID_XSDI_ENABLESTATUSBAR:
            _xfSetStatusBarVisibility(pcnbp->somSelf,
                        ulExtra,
                        TRUE);  // update open folder views
        break;

        case ID_XSDI_FAVORITEFOLDER:
            _xfMakeFavoriteFolder(pcnbp->somSelf, ulExtra);
        break;

        case ID_XSDI_QUICKOPEN:
            _xfSetQuickOpen(pcnbp->somSelf, ulExtra);
        break;

        case DID_UNDO:
            if (pcnbp->pBackup) {
                XFolderData *Backup = (pcnbp->pBackup);
                // "Undo" button: restore backed up instance data
                _bFullPath = Backup->bFullPath;
                _bSnapToGridAllowed = Backup->bSnapToGridAllowed;
                _bAcceleratorsAllowed = Backup->bAcceleratorsAllowed;
                _xfSetStatusBarVisibility(pcnbp->somSelf,
                            Backup->bStatusBar,
                            TRUE);  // update open folder views
                // have the page updated by calling the callback above
                fncbXFolderInitPage(pcnbp, CBI_SHOW | CBI_ENABLE);
                _xfUpdateAllFrameWndTitles(pcnbp->somSelf);
            }
        break;

        case DID_DEFAULT:
            // "Default" button:
            _bFullPath = 2;
            _bSnapToGridAllowed = 2;
            _bAcceleratorsAllowed = 2;
            _xfSetStatusBarVisibility(pcnbp->somSelf,
                        STATUSBAR_DEFAULT,
                        TRUE);  // update open folder views
            // have the page updated by calling the callback above
            fncbXFolderInitPage(pcnbp, CBI_SET | CBI_ENABLE);
            _xfUpdateAllFrameWndTitles(pcnbp->somSelf);
        break;

        default:
            fUpdate = FALSE;
        break;
    }

    if (fUpdate)
        _wpSaveDeferred(pcnbp->somSelf);

    return ((MPARAM)-1);
}

/*
 *@@ xfAddXFolderPages:
 *           this actually adds the "XFolder" pages into all folder notebooks.
 */

SOM_Scope ULONG  SOMLINK xf_xfAddXFolderPages(XFolder *somSelf,
                                              HWND hwndDlg)
{
    PCREATENOTEBOOKPAGE pcnbp = malloc(sizeof(CREATENOTEBOOKPAGE));
    memset(pcnbp, 0, sizeof(CREATENOTEBOOKPAGE));

    XFolderMethodDebug("XFolder","xf_xfAddXFolderPages");

    strcpy(szXFolderVersion, "~XFolder ");
    strcat(szXFolderVersion, XFOLDER_VERSION);

    pcnbp->somSelf = somSelf;
    pcnbp->hwndNotebook = hwndDlg;
    pcnbp->hmod = NLS_MODULE;
    pcnbp->ulDlgID = ID_XSD_SETTINGS_FLDR1;
    pcnbp->ulPageID = SP_XFOLDER_FLDR;
    pcnbp->fMajorTab = TRUE;
    pcnbp->pszName = szXFolderVersion;
    pcnbp->ulDefaultHelpPanel  = ID_XSH_SETTINGS_FLDR1;

    pcnbp->pfncbInitPage    = fncbXFolderInitPage;
    pcnbp->pfncbItemChanged = fncbXFolderItemChanged;

    return (ntbInsertPage(pcnbp));
}

/*
 *@@ wpInitData:
 *           here XFolder will initialize its instance data
 */

SOM_Scope void  SOMLINK xf_wpInitData(XFolder *somSelf)
{
    // PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpInitData");

    XFolder_parent_WPFolder_wpInitData(somSelf);

    // set all the instance variables to safe defaults
    _pliOrderedContentFirst = NULL;
    _pliOrderedContentLast = NULL;
    _hmtxOrderedContent = NULLHANDLE;
    _pICONPOS = NULL;
    _ulICONPOSSize = 0;
    _bSnapToGridAllowed = 2;
    _bFullPath = 2;
    _bAcceleratorsAllowed = 2;
    _bStatusBar = STATUSBAR_DEFAULT;
    _AlwaysSort = SET_DEFAULT;
    _DefaultSort = SET_DEFAULT;
    // _ulSBInflatedFrame = 0;

    _pFldrSortInfo = NULL;
    _pFldrLongArray = NULL;
    _pszFldrStrArray = NULL;
    _pusFldrBackground = NULL;
    _cbFldrStrArray = 0;
    _cbFldrLongArray = 0;

    _pulShowAllInTreeView = NULL;

    _fUnInitCalled = FALSE;
    _hwndCnrSaved = NULLHANDLE;
}

/*
 *@@ wpUnInitData:
 *           clean up when object is deleted or made dormant
 */

SOM_Scope void  SOMLINK xf_wpUnInitData(XFolder *somSelf)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpUnInitData");

    // make sure we only do this once, because we
    // seem to get called several times sometimes
    if (!_fUnInitCalled)
    {
        _fUnInitCalled = TRUE;

        if (_pliOrderedContentFirst)
            InvalidateOrderedContent(somThis);

        if (_hmtxOrderedContent)
            DosCloseMutexSem(_hmtxOrderedContent);

        if (_pICONPOS) {
            _wpFreeMem(somSelf, _pICONPOS);
            _pICONPOS = NULL;
        }

        XFolder_parent_WPFolder_wpUnInitData(somSelf);
    }
}

/*
 *@@ wpFree:
 *           this WPObject method destroys the persistent form of the object
 *           and then frees the memory that represented that object. For
 *           WPFolders, this is called when a folder is actually to be
 *           deleted. We will call the parent method and then also remove
 *           those darn PMWorkplace:FolderPos entries which the WPS never
 *           deletes
 */

SOM_Scope BOOL  SOMLINK xf_wpFree(XFolder *somSelf)
{
    BOOL brc;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    HOBJECT hObj;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpFree");

    if (pGlobalSettings->CleanupINIs)
    {
        // "clean up INI files": get object handle for
        // folderpos deletion later. This doesn't hurt
        // because every folder has a handle once it has
        // been opened
        hObj = _wpQueryHandle(somSelf);
    }

    // according to WPS docs, the parent method should be called
    // AFTER additional processing; probably somSelf becomes invalid
    // after this
    brc = XFolder_parent_WPFolder_wpFree(somSelf);

    if (brc)
        if (pGlobalSettings->CleanupINIs)
            // have FOLDERPOS entries
            // deleted by Worker thread; we only pass the
            // object HANDLE and not somSelf because somSelf
            // is no longer valid after having called the parent
            xthrPostWorkerMsg(WOM_DELETEFOLDERPOS,
                        (MPARAM)hObj, NULL);

    return (brc);
}

/*
 *@@ wpSetup:
 *           this instance method is called to allow a
 *           newly created object to initialize itself.
 *           XFolder will examine its setup strings here
 */

SOM_Scope BOOL  SOMLINK xf_wpSetup(XFolder *somSelf, PSZ pszSetupString)
{
    BOOL        rc,
                fChanged = FALSE;
    CHAR        szValue[CCHMAXPATH+1];
    ULONG       cbValue;
    USHORT      usDefaultSort, usAlwaysSort;

    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpSetup");

    rc = (XFolder_parent_WPFolder_wpSetup(somSelf, pszSetupString));

    if (rc) {
        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "SNAPTOGRID", szValue, &cbValue))
        {
            if (strnicmp(szValue, "NO", 2) == 0)
                _bSnapToGridAllowed = 0;
            else if (strnicmp(szValue, "YES", 3) == 0)
                _bSnapToGridAllowed = 1;
            else if (strnicmp(szValue, "DEFAULT", 7) == 0)
                _bSnapToGridAllowed = 2;
            else if (strnicmp(szValue, "EXEC", 4) == 0)
                _xfSnapToGrid(somSelf, FALSE);
            fChanged = TRUE;
        }

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "FULLPATH", szValue, &cbValue))
        {
            if (strnicmp(szValue, "NO", 2) == 0)
                _bFullPath = 0;
            else if (strnicmp(szValue, "YES", 3) == 0)
                _bFullPath = 1;
            else if (strnicmp(szValue, "DEFAULT", 7) == 0)
                _bFullPath = 2;

            _xfUpdateAllFrameWndTitles(somSelf);
            fChanged = TRUE;
        }

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "ACCELERATORS", szValue, &cbValue))
        {
            if (strnicmp(szValue, "NO", 2) == 0)
                _bAcceleratorsAllowed = 0;
            else if (strnicmp(szValue, "YES", 3) == 0)
                _bAcceleratorsAllowed = 1;
            else if (strnicmp(szValue, "DEFAULT", 7) == 0)
                _bAcceleratorsAllowed = 2;
            fChanged = TRUE;
        }

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "FAVORITEFOLDER", szValue, &cbValue))
        {
            if (strnicmp(szValue, "NO", 2) == 0)
                _xfMakeFavoriteFolder(somSelf, FALSE);
            else if (strnicmp(szValue, "YES", 3) == 0)
                _xfMakeFavoriteFolder(somSelf, TRUE);
            fChanged = TRUE;
        }

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "QUICKOPEN", szValue, &cbValue))
        {
            if (strnicmp(szValue, "NO", 2) == 0)
                _xfSetQuickOpen(somSelf, FALSE);
            else if (strnicmp(szValue, "YES", 3) == 0)
                _xfSetQuickOpen(somSelf, TRUE);
            fChanged = TRUE;
        }

        if (somSelf != _wpclsQueryActiveDesktop(_WPDesktop))
        {
            cbValue = sizeof(szValue);
            if (_wpScanSetupString(somSelf, pszSetupString,
                        "STATUSBAR", szValue, &cbValue))
            {
                if (strnicmp(szValue, "NO", 2) == 0)
                    _bStatusBar = STATUSBAR_OFF;
                else if (strnicmp(szValue, "YES", 3) == 0)
                    _bStatusBar = STATUSBAR_ON;
                else if (strnicmp(szValue, "DEFAULT", 7) == 0)
                    _bStatusBar = STATUSBAR_DEFAULT;
            }
            xthrPostWorkerMsg(WOM_UPDATEALLSTATUSBARS,
                    (MPARAM)1,  // show/hide flag
                    MPNULL);
            fChanged = TRUE;
        }

        _xfQueryFldrSort(somSelf, &usDefaultSort, &usAlwaysSort);

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "ALWAYSSORT", szValue, &cbValue))
        {
            if (strnicmp(szValue, "NO", 2) == 0)
                usAlwaysSort = 0;
            else if (strnicmp(szValue, "YES", 3) == 0)
                usAlwaysSort = 1;
            else if (strnicmp(szValue, "DEFAULT", 7) == 0)
                usAlwaysSort = SET_DEFAULT;
            _xfSetFldrSort(somSelf, usDefaultSort, usAlwaysSort);
            fChanged = TRUE;
        }

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "DEFAULTSORT", szValue, &cbValue))
        {
            LONG lValue;
            sscanf(szValue, "%d", &lValue);
            if ( (lValue >=0) && (lValue <= SV_LAST) )
                usDefaultSort = lValue;
            else
                usDefaultSort = SET_DEFAULT;
            _xfSetFldrSort(somSelf, usDefaultSort, usAlwaysSort);
            fChanged = TRUE;
        }

        cbValue = sizeof(szValue);
        if (_wpScanSetupString(somSelf, pszSetupString,
                    "SORTNOW", szValue, &cbValue))
        {
            LONG lValue;
            sscanf(szValue, "%d", &lValue);
            if ( (lValue >=0) && (lValue <= SV_LAST) )
                usDefaultSort = lValue;
            else
                usDefaultSort = SET_DEFAULT;
            _xfclsForEachOpenView(_XFolder, 0, &fncbSortAllViews);
        }

        if (fChanged)
            _wpSaveDeferred(somSelf);
    }
    return (rc);
}

/*
 *@@ wpSaveState:
 *           this instance method is called to allow an
 *           an object to save its state; XFolder will
 *           save its instance settings (NB page) here
 */

SOM_Scope BOOL  SOMLINK xf_wpSaveState(XFolder *somSelf)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpSaveState");

    // we will now save all our instance data; in order
    // not to blow up the EA size too much, we will only
    // save data which is different from the "transparent"
    // (i.e. Global) setting

    if (_bSnapToGridAllowed != 2)
        _wpSaveLong(somSelf, "XFolder", 1, (ULONG)_bSnapToGridAllowed);
    if (_bFullPath != 2)
        _wpSaveLong(somSelf, "XFolder", 2, (ULONG)_bFullPath);
    if (_bAcceleratorsAllowed != 2)
        _wpSaveLong(somSelf, "XFolder", 3, (ULONG)_bAcceleratorsAllowed);
    if (_bStatusBar != STATUSBAR_DEFAULT)
        _wpSaveLong(somSelf, "XFolder", 4, (ULONG)_bStatusBar);
    /* if (_ulSBInflatedFrame)
        _wpSaveLong(somSelf, "XFolder", 5, (ULONG)_ulSBInflatedFrame); */
    if (_AlwaysSort != SET_DEFAULT)
        _wpSaveLong(somSelf, "XFolder", 6, (ULONG)_AlwaysSort);
    if (_DefaultSort != SET_DEFAULT)
        _wpSaveLong(somSelf, "XFolder", 7, (ULONG)_DefaultSort);

    return (XFolder_parent_WPFolder_wpSaveState(somSelf));
}

/*
 *@@ wpRestoreState:
 *           this instance method is called to allow an
 *           an object to restore its state; XFolder will
 *           restore its instance settings (NB page) here
 */

SOM_Scope BOOL  SOMLINK xf_wpRestoreState(XFolder *somSelf,
                                             ULONG ulReserved)
{
    ULONG   ul;
    BOOL    brc;
    XFolderData *somThis = XFolderGetData(somSelf);
    // XFolderMethodDebug("XFolder","xf_wpRestoreState");

    #ifdef DEBUG_SOMMETHODS
        _Pmpf(("XFolder::wpRestoreState for %s", _wpQueryTitle(somSelf) ));
    #endif

    // we will now restore all the different XFolder settings
    // into the instance data; note that if _wpRestoreLong
    // returns FALSE (i.e. setting not found), we always use
    // the "transparent" value which makes this folder use
    // the corresponding Global Setting

    if (_wpRestoreLong(somSelf, "XFolder", 1, &ul))
        _bSnapToGridAllowed = (BYTE)ul;
    else _bSnapToGridAllowed = 2;

    if (_wpRestoreLong(somSelf, "XFolder", 2, &ul))
        _bFullPath = (BYTE)ul;
    else _bFullPath = 2;

    if (_wpRestoreLong(somSelf, "XFolder", 3, &ul))
        _bAcceleratorsAllowed = (BYTE)ul;
    else _bAcceleratorsAllowed = 2;

    if (_wpRestoreLong(somSelf, "XFolder", 4, &ul))
        _bStatusBar = (BYTE)ul;
    else _bStatusBar = STATUSBAR_DEFAULT;

    /* if (_wpRestoreLong(somSelf, "XFolder", 5, &ul))
        _ulSBInflatedFrame = (BYTE)ul;
    else _ulSBInflatedFrame = 0; */

    if (_wpRestoreLong(somSelf, "XFolder", 6, &ul))
        _AlwaysSort = (USHORT)ul;

    if (_wpRestoreLong(somSelf, "XFolder", 7, &ul))
        _DefaultSort = (USHORT)ul;

    brc = (XFolder_parent_WPFolder_wpRestoreState(somSelf, ulReserved));

    #ifdef DEBUG_SOMMETHODS
        _Pmpf(("  End of XFolder::wpRestoreState"));
    #endif
    return (brc);
}

/*
 *@@ wpRestoreLong:
 *           this instance method restores a 32-bit data
 *           value from the folder EAs upon object awakening.
 *           We check the "ulKey" value after having called
 *           the parent to be able to intercept the pointer
 *           to certain WPS-internal folder data, which
 *           we cannot access otherwise. That's a real ugly
 *           kludge, but there's no other way to get certain
 *           folder settings. ;-)
 *           On Warp 4, the WPS queries the
 *           IDKEY_FDRTREEVIEWCONTENTS key here, which is
 *           == 1 if the "SHOWALLINTREEVIEW" flag is on.
 */

SOM_Scope BOOL  SOMLINK xf_wpRestoreLong(XFolder *somSelf, PSZ pszClass,
                                         ULONG ulKey, PULONG pulValue)
{
    BOOL        brc;
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpRestoreLong");

    brc = XFolder_parent_WPFolder_wpRestoreLong(somSelf, pszClass,
                                                  ulKey, pulValue);

    if (strcmp(pszClass, "WPFolder") == 0)
    {
        switch (ulKey)
        {
            // Warp 4
            #ifndef IDKEY_FDRTREEVIEWCONTENTS
                #define IDKEY_FDRTREEVIEWCONTENTS 2939
            #endif

            case IDKEY_FDRTREEVIEWCONTENTS: {
                // then the pointer given to this method (pValue) must
                // be the pointer to the WPFolder-internal SHOWALLINTREEVIEW
                // flag
                    if (pulValue) {
                        XFolderData *somThis = XFolderGetData(somSelf);
                        _pulShowAllInTreeView = pulValue;
                    }
            break; }

            #ifdef DEBUG_RESTOREDATA
                default: {
                    _Pmpf(("Long IDKEY_xxx (%s %d) --> 0x%lX",
                            pszClass, ulKey,
                            *pulValue));        // data returned
                break; }
            #endif
        }
    }
    return (brc);
}

/*
 *@@ wpRestoreData:
 *           this instance method restores binary instance
 *           data from the folder EAs upon object awakening.
 *           We check the "ulKey" value after having called
 *           the parent to be able to intercept the pointer
 *           to certain WPS-internal folder data, which
 *           we cannot access otherwise. That's a real ugly
 *           kludge, but there's no other way to get certain
 *           folder settings. ;-)
 *           On Warp 4, the WPS queries lots of sort and
 *           folder view settings here, whose pointers we
 *           can store in XFolder's instance data.
 */

SOM_Scope BOOL  SOMLINK xf_wpRestoreData(XFolder *somSelf,
                                            PSZ pszClass, ULONG ulKey,
                                            PBYTE pValue, PULONG pcbValue)
{
    BOOL        brc;
    ULONG       cbOrigValue = 0;
    // PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    XFolderMethodDebug("XFolder","xf_wpRestoreData");

    // get the size of the buffer which was given to us
    if (pValue)
        // pValue given:
        cbOrigValue = *pcbValue;
    // else: caller is requesting the size of the data

    // always call parent, even for the sort data, or
    // the WPFolder original gets confused
    brc = XFolder_parent_WPFolder_wpRestoreData(somSelf, pszClass,
                                              ulKey, pValue,
                                              pcbValue);

    // after we have restored the setting by calling the
    // default WPFolder method, we check for a few flags
    // which we might be interested in; we can then store
    // the pointer to WPS-internal data in XFolder instance
    // data
    if (strcmp(pszClass, "WPFolder") == 0)
    {
        switch (ulKey)
        {
            case IDKEY_FDRSORTINFO:
            {
                // then the pointer given to this method (pValue) must
                // be the pointer to the WPFolder-internal FDRSORTINFO
                // structure (undocumented, I've declared it in
                // xfldr.idl); we store this pointer in the instance
                // data so that we can manipulate it later
                if (cbOrigValue == sizeof(FDRSORTINFO))
                {
                    if (pValue) {
                        XFolderData *somThis = XFolderGetData(somSelf);
                        _pFldrSortInfo = (PFDRSORTINFO)pValue;

                        if (brc)
                            // if the parent method has found sort data,
                            // and the user had set "Always sort" on for
                            // the folder using the regular WPS "Sort" page,
                            // we need to update the XFolder sort data, but
                            // only update this if this hasn't been set
                            // by wpRestoreState yet; as a result, the
                            // XFolder sort settings will follow the WPFolder
                            // sort settings, but can be overridden
                            if (_AlwaysSort == SET_DEFAULT) // not set yet?
                                _AlwaysSort = ((PFDRSORTINFO)pValue)->fAlwaysSort;
                    }
                }
                // _Pmpf(("IDKEY_FDRSORTINFO size %d -> %d", cbOrigValue, *pcbValue));
            break; }

            case IDKEY_FDRBACKGROUND:       // size: 2 bytes
                if (cbOrigValue == 2)
                {
                    if (pValue) {
                        XFolderData *somThis = XFolderGetData(somSelf);
                        _pusFldrBackground = (PUSHORT)pValue;
                    }
                }
            break;

            case IDKEY_FDRLONGARRAY:        // size: 84 bytes
            {
                XFolderData *somThis = XFolderGetData(somSelf);
                // store the size of the data returned in
                // folder instance data, in case it is not
                // 84 bytes (as it is with Warp 4 fixpak 8)
                _cbFldrLongArray = *pcbValue;
                if (pValue)
                    _pFldrLongArray = (PFDRLONGARRAY)pValue;
            break; }

            case IDKEY_FDRSTRARRAY:         // size: 400 bytes
            {
                XFolderData *somThis = XFolderGetData(somSelf);
                // store the size of the data returned in
                // folder instance data, in case it is not
                // 400 bytes (as it is with Warp 4 fixpak 8)
                _cbFldrStrArray = *pcbValue;
                if (pValue)
                    _pszFldrStrArray = (PSZ)pValue;
            break; }

            #ifdef DEBUG_RESTOREDATA
                default: {
                    _Pmpf(("Data IDKEY_xxx (%s %d) size %d -> %d",
                            pszClass, ulKey,
                            cbOrigValue,    // size in or 0 if size queried
                            *pcbValue));    // size out
                break; }
            #endif

            /*
             * the following others were queried for G:\root\test
             * under Warp 4 fixpak 8:
             */

            /*
                IDKEY_xxx (WPObject 11) size 32 -> 32           WPOBJECT_DATA
                IDKEY_xxx (WPObject 12) size 400 -> 400         WPOBJECT_STRINGS
                IDKEY_xxx (WPObject 4) size 8 -> 8              ?!?
                IDKEY_xxx (WPFolder 2924) size 542 -> 542       IDKEY_CNRBACKGROUND
                IDKEY_xxx (WPFolder 2920) size 542 -> 542       IDKEY_FDRINCCLASS
                IDKEY_xxx (WPFolder 2925) size 542 -> 542       IDKEY_FDRINCCRITERIA
                IDKEY_xxx (WPFolder 2938) size 8 -> 8           IDKEY_FDRGRIDINFO

            and these under Warp 3, no fixpaks:
                IDKEY_xxx (WPObject 11) size 28 -> 28           (+) four bytes less
                IDKEY_xxx (WPObject 12) size 400 -> 400         (+)
                IDKEY_xxx (WPObject 4) size 8 -> 8              (+)
                IDKEY_xxx (WPFolder 2924) size 542 -> 542       (+)
                IDKEY_xxx (WPFolder 2920) size 542 -> 542       (+)
                IDKEY_xxx (WPFolder 2925) size 542 -> 542       (+)
                i.e. the same, except the grid info

            but if we had a folder background set (Warp 4):
                IDKEY_xxx (WPObject 11) size 32 -> 32
                IDKEY_xxx (WPObject 12) size 400 -> 400
                IDKEY_xxx (WPObject 4) size 8 -> 8
                IDKEY_xxx (WPFolder 2924) size 542 -> 542       IDKEY_CNRBACKGROUND
                IDKEY_xxx (WPFolder 2920) size 542 -> 542       IDKEY_FDRINCCLASS
                IDKEY_xxx (WPFolder 2925) size 542 -> 107       IDKEY_FDRINCCRITERIA
                IDKEY_xxx (WPFolder 2920) size 260 -> 260       IDKEY_FDRINCCLASS
                IDKEY_xxx (WPFolder 2925) size 260 -> 107       IDKEY_FDRINCCRITERIA
                IDKEY_xxx (WPFolder 2925) size 107 -> 107       IDKEY_FDRINCCRITERIA
                IDKEY_xxx (WPFolder 2938) size 8 -> 8
            and on Warp 3:
                IDKEY_xxx (WPObject 11) size 28 -> 28
                IDKEY_xxx (WPObject 12) size 400 -> 400
                IDKEY_xxx (WPObject 4) size 8 -> 8
                                                            no  IDKEY_CNRBACKGROUND
                IDKEY_xxx (WPFolder 2920) size 259 -> 259       IDKEY_FDRINCCLASS
                IDKEY_xxx (WPFolder 2925) size 259 -> 178       IDKEY_FDRINCCRITERIA
                IDKEY_xxx (WPFolder 2920) size 260 -> 260       IDKEY_FDRINCCLASS
                IDKEY_xxx (WPFolder 2925) size 260 -> 178       IDKEY_FDRINCCRITERIA
                IDKEY_xxx (WPFolder 2925) size 178 -> 178       IDKEY_FDRINCCRITERIA

            */

            /* this is what we got from the "Programme" folder (Warp 4):

                 Long IDKEY_xxx (WPFileSystem 4) --> 0x0
                 Data IDKEY_xxx (WPObject 11) size 32 -> 32         WPOBJECT_DATA
                 Data IDKEY_xxx (WPObject 12) size 400 -> 400       WPOBJECT_STRINGS
                 Data IDKEY_xxx (WPObject 4) size 8 -> 8            ?!?
                 Data IDKEY_xxx (WPFolder 2924) size 542 -> 542     IDKEY_CNRBACKGROUND
                 Strg IDKEY_xxx (WPFolder 2921) --> NULL            IDKEY_FDRINCNAME
                 Data IDKEY_xxx (WPFolder 2920) size 0 -> 542       IDKEY_FDRINCCLASS
                 Data IDKEY_xxx (WPFolder 2925) size 0 -> 107       IDKEY_FDRINCCRITERIA
                 Data IDKEY_xxx (WPFolder 2920) size 0 -> 260       * IDKEY_FDRINCCLASS
                 Data IDKEY_xxx (WPFolder 2925) size 0 -> 107       * IDKEY_FDRINCCRITERIA
                 Data IDKEY_xxx (WPFolder 2925) size 107 -> 107     * IDKEY_FDRINCCRITERIA
                 Strg IDKEY_xxx (WPFolder 2921) -->                 * IDKEY_FDRINCNAME
                 Data IDKEY_xxx (WPFolder 2938) size 8 -> 8         IDKEY_FDRGRIDINFO
                 Long IDKEY_xxx (WPFolder 2939) --> 0x0             IDKEY_FDRTREEVIEWCONTENTS

                 Long IDKEY_xxx (WPFileSystem 4) --> 0x0
                 Data IDKEY_xxx (WPObject 11) size 32 -> 32
                 Data IDKEY_xxx (WPObject 12) size 400 -> 400
                 Data IDKEY_xxx (WPObject 4) size 8 -> 8
                 Strg IDKEY_xxx (WPFolder 2934) --> F:\OS2\BITMAP\PLASTER.BMP
                                                                    IDKEY_FDRCNRBACKGROUND
                 Strg IDKEY_xxx (WPFolder 2921) --> NULL
                 Data IDKEY_xxx (WPFolder 2920) size 0 -> 259
                 Data IDKEY_xxx (WPFolder 2925) size 0 -> 107
                 Data IDKEY_xxx (WPFolder 2920) size 0 -> 260
                 Data IDKEY_xxx (WPFolder 2925) size 0 -> 107
                 Data IDKEY_xxx (WPFolder 2925) size 107 -> 107
                 Strg IDKEY_xxx (WPFolder 2921) --> F:\OS2\BITMAP\PLASTER.BMP
                                                                    IDKEY_FDRCNRBACKGROUND
                 Data IDKEY_xxx (WPFolder 2938) size 8 -> 8
                 Long IDKEY_xxx (WPFolder 2939) --> 0x0             IDKEY_FDRTREEVIEWCONTENTS

            */

            /*
             *  the following are apperently never queried
             *  (Warp 4 FP 8 German):
             */

            /*
                case IDKEY_FDRSORTATTRIBS:
                case IDKEY_FDRSORTCLASS:
                case IDKEY_FDRINVISCOLUMNS:
                case IDKEY_FDRCONTENTATTR:
                case IDKEY_FDRSNEAKYCOUNT:
                case IDKEY_FDRCNRBACKGROUND:
                case IDKEY_FDRBKGNDIMAGEFILE:
            */


        }   // end switch
    }

    return (brc);
}

/*
 *@@ wpAddSettingsPages:
 *           call xfAddXFolderPages
 */

SOM_Scope BOOL  SOMLINK xf_wpAddSettingsPages(XFolder *somSelf,
                                                 HWND hwndNotebook)
{
    BOOL            rc;
    // PAGEINFO        pi;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpAddSettingsPages");

    rc = (XFolder_parent_WPFolder_wpAddSettingsPages(somSelf,
                                                       hwndNotebook));

    if (rc)
        rc = _xfAddXFolderPages(somSelf, hwndNotebook);

    return (rc);
}

/*
 * fncbSortInitPage:
 *      "Sort" page notebook callback function (notebook.c).
 *      Sets the controls on the page.
 *      The "Sort" callbacks are used both for the folder settings notebook page
 *      AND the respective "Sort" page in the "Workplace Shell" object, so we
 *      need to keep instance data and the XFolder Global Settings apart.
 *      We do this by examining the page ID in the notebook info struct.
 */

VOID fncbSortInitPage(PVOID pnbi, ULONG ulExtra)  // notebook info struct
{
    PCREATENOTEBOOKPAGE pcnbp = (PCREATENOTEBOOKPAGE)pnbi;
    PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    HWND        hwndListbox = WinWindowFromID(pcnbp->hwndPage,
                    ID_XSDI_SORTLISTBOX);
    XFolderData *somThis = NULL;

    if (pcnbp->ulPageID == SP_FLDRSORT_FLDR) {
        // if we're being called from a folder's notebook,
        // get instance data
        somThis = XFolderGetData(pcnbp->somSelf);

        if (ulExtra & CBI_INIT)
        {
            if (pcnbp->pBackup == NULL)
            {
                // first call: backup instance data for "Undo" button;
                // this memory will be freed automatically by the
                // common notebook window function (notebook.c) when
                // the notebook page is destroyed
                pcnbp->pBackup = malloc(sizeof(XFolderData));
                memcpy(pcnbp->pBackup, somThis, sizeof(XFolderData));
            }

            // hide the "Enable extended sort" checkbox, which is
            // only visible in the "Workplace Shell" object
            WinShowWindow(WinWindowFromID(pcnbp->hwndPage, ID_XSDI_REPLACESORT), FALSE);
        }
    }

    if (ulExtra & CBI_INIT)
    {
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByName);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByType);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByClass);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByRealName);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortBySize);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByWriteDate);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByAccessDate);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByCreationDate);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortByExt);
        WinInsertLboxItem(hwndListbox, LIT_END, pNLSStrings->pszSortFoldersFirst);
    }

    if (somThis) {
        // "folder" mode:
        if (ulExtra & CBI_SET)
        {
            winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_ALWAYSSORT,
                    ALWAYS_SORT);
            WinSendMsg(hwndListbox,
                    LM_SELECTITEM,
                    (MPARAM)DEFAULT_SORT,
                    (MPARAM)TRUE);
        }
    } else {
        // "global" mode:
        if (ulExtra & CBI_SET)
        {
            winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_REPLACESORT,
                   pGlobalSettings->ReplaceSort);
            winhSetDlgItemChecked(pcnbp->hwndPage, ID_XSDI_ALWAYSSORT,
                   pGlobalSettings->AlwaysSort);

            WinSendMsg(hwndListbox,
                    LM_SELECTITEM,
                    (MPARAM)(pGlobalSettings->DefaultSort),
                    (MPARAM)TRUE);
        }

        if (ulExtra & CBI_ENABLE)
        {
            // disable items if extended sorting is off
            winhEnableDlgItem(pcnbp->hwndPage, ID_XSDI_SORTTEXT,
                    pGlobalSettings->ReplaceSort);
            winhEnableDlgItem(pcnbp->hwndPage, ID_XSDI_SORTLISTBOX,
                    pGlobalSettings->ReplaceSort);
            winhEnableDlgItem(pcnbp->hwndPage, ID_XSDI_ALWAYSSORT,
                    pGlobalSettings->ReplaceSort);
        }
    }
}

/*
 * fncbSortItemChanged:
 *      "Sort" page notebook callback function (notebook.c).
 *      Reacts to changes of any of the dialog controls.
 *      The "Sort" callbacks are used both for the folder settings notebook page
 *      AND the respective "Sort" page in the "Workplace Shell" object, so we
 *      need to keep instance data and the XFolder GLobal Settings apart.
 */

MRESULT fncbSortItemChanged(PVOID pnbi,
                USHORT usItemID, USHORT usNotifyCode,
                ULONG ulExtra)      // for checkboxes: contains new state
{
    PCREATENOTEBOOKPAGE pcnbp = (PCREATENOTEBOOKPAGE)pnbi;
    BOOL fUpdate = TRUE;

    switch (usItemID) {

        case ID_XSDI_ALWAYSSORT:
        case ID_XSDI_SORTLISTBOX: {

            PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
            HWND        hwndListbox = WinWindowFromID(pcnbp->hwndPage,
                            ID_XSDI_SORTLISTBOX);
            // XFolderData *somThis = NULL;

            if (pcnbp->ulPageID == SP_FLDRSORT_FLDR) {
                // if we're being called from a folder's notebook,
                // change instance data
                _xfSetFldrSort(pcnbp->somSelf,
                            // DefaultSort:
                                (USHORT)(WinSendMsg(hwndListbox,
                                        LM_QUERYSELECTION,
                                        (MPARAM)LIT_CURSOR,
                                        MPNULL)),
                            // AlwaysSort:
                                (USHORT)(winhIsDlgItemChecked(pcnbp->hwndPage, ID_XSDI_ALWAYSSORT))
                            );
            } else {
                BOOL bTemp;
                pGlobalSettings->ReplaceSort =
                    winhIsDlgItemChecked(pcnbp->hwndPage, ID_XSDI_REPLACESORT);
                pGlobalSettings->DefaultSort =
                        (BYTE)WinSendMsg(hwndListbox,
                            LM_QUERYSELECTION,
                            (MPARAM)LIT_CURSOR,
                            MPNULL);
                bTemp = winhIsDlgItemChecked(pcnbp->hwndPage, ID_XSDI_ALWAYSSORT);
                if (bTemp) {
                    USHORT usDefaultSort, usAlwaysSort;
                    _xfQueryFldrSort(_wpclsQueryActiveDesktop(_WPDesktop),
                            &usDefaultSort, &usAlwaysSort);
                    if (usAlwaysSort != 0)
                    {
                        // issue warning that this might also sort the Desktop
                        if (cmnMessageBoxMsg(pcnbp->hwndPage, 116, 133, MB_YESNO) == MBID_YES)
                            _xfSetFldrSort(_wpclsQueryActiveDesktop(_WPDesktop),
                                usDefaultSort, 0);
                    }
                }
                pGlobalSettings->AlwaysSort = bTemp;
            }
        break; }

        case ID_XSDI_REPLACESORT: {
            PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
            // "extended sorting on": exists on global page only
            pGlobalSettings->ReplaceSort = ulExtra;
            fncbSortInitPage(pnbi, CBI_ENABLE);
        break; }

        // control other than listbox:
        case DID_UNDO:
            // "Undo" button: restore backed up instance/global data
            if (pcnbp->ulPageID == SP_FLDRSORT_FLDR) {
                // if we're being called from a folder's notebook,
                // restore instance data
                if (pcnbp->pBackup) {
                    XFolderData *Backup = (pcnbp->pBackup);
                    _xfSetFldrSort(pcnbp->somSelf,
                            Backup->DefaultSort,
                            Backup->AlwaysSort);
                }
            } else {
                // global sort page:
                 cmnSetDefaultSettings(SP_FLDRSORT_GLOBAL);
            }
            fncbSortInitPage(pnbi, CBI_SET | CBI_ENABLE);
        break;

        case DID_DEFAULT:
            // "Default" button:
            if (pcnbp->ulPageID == SP_FLDRSORT_FLDR) {
                _xfSetFldrSort(pcnbp->somSelf, SET_DEFAULT, SET_DEFAULT);
            } else {
                cmnSetDefaultSettings(SP_FLDRSORT_GLOBAL);
            }
            fncbSortInitPage(pnbi, CBI_SET | CBI_ENABLE);
        break;

        default:
            fUpdate = FALSE;
        break;
    }

    if (fUpdate) {
        if (pcnbp->ulPageID == SP_FLDRSORT_FLDR) {
            _wpSaveDeferred(pcnbp->somSelf);
            // update our folder only
            _xfForEachOpenView(pcnbp->somSelf, 0,
                    &fncbUpdateFolderSorts);
        } else {
            cmnStoreGlobalSettings();
            // update all open folders
            _xfclsForEachOpenView(_XFolder, 0,
                    &fncbUpdateFolderSorts);
        }
    }
    return ((MPARAM)-1);
}

/*
 *@@ wpAddFolderSortPage:
 *           this normally adds the "Sort" page to the folder
 *           settings notebook; if allowed, we will replace this
 *           by the new XFolder version of it
 */

SOM_Scope ULONG  SOMLINK xf_wpAddFolderSortPage(XFolder *somSelf,
                                                   HWND hwndNotebook)
{
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpAddFolderSortPage");

    if (pGlobalSettings->ReplaceSort)
    {
        // extended sorting enabled:
        // check whether the "sort class" of the folder
        // is WPFileSystem (which is the default); only
        // then replace the "Sort" page with ours. Some
        // WPS extensions define their own Details views,
        // and we don't want to mess with that
        if (_wpQueryFldrSortClass(somSelf) == _WPFileSystem)
        {
            PCREATENOTEBOOKPAGE pcnbp = malloc(sizeof(CREATENOTEBOOKPAGE));
            memset(pcnbp, 0, sizeof(CREATENOTEBOOKPAGE));

            pcnbp->somSelf = somSelf;
            pcnbp->hwndNotebook = hwndNotebook;
            pcnbp->hmod = NLS_MODULE;
            pcnbp->ulDlgID = ID_XSD_SETTINGS_FLDRSORT;
            pcnbp->fMajorTab = TRUE;
            pcnbp->pszName = pNLSStrings->pszSort;
            pcnbp->ulDefaultHelpPanel  = ID_XSH_SETTINGS_FLDRSORT;

            // mark this page as "instance", because both
            // the instance settings notebook and the
            // "Workplace Shell" object use the same
            // callbacks
            pcnbp->ulPageID = SP_FLDRSORT_FLDR;

            pcnbp->pfncbInitPage    = fncbSortInitPage;
            pcnbp->pfncbItemChanged = fncbSortItemChanged;

            ntbInsertPage(pcnbp);

            return (SETTINGS_PAGE_REMOVED);
        }
    }

    return (XFolder_parent_WPFolder_wpAddFolderSortPage(somSelf,
                                                        hwndNotebook));
}

/*
 *@@ wpFilterPopupMenu:
 *           this removes default menu entries according to the
 *           Global Settings.
 */

SOM_Scope ULONG  SOMLINK xf_wpFilterPopupMenu(XFolder *somSelf,
                                                 ULONG ulFlags,
                                                 HWND hwndCnr,
                                                 BOOL fMultiSelect)
{
    ULONG ulMenuFilter = 0;
    // XFolderData *somThis = XFolderGetData(somSelf);
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    XFolderMethodDebug("XFolder","xf_wpFilterPopupMenu");

    ulMenuFilter = XFolder_parent_WPFolder_wpFilterPopupMenu(somSelf,
                                                         ulFlags,
                                                         hwndCnr,
                                                         fMultiSelect);
    #ifdef DEBUG_MENUS
        _Pmpf(("XFolder::wpFilterPopupMenu parent flags:"));
        _Pmpf(("  CTXT_CRANOTHER %d", ulMenuFilter & CTXT_CRANOTHER));
    #endif

    // now suppress default menu items according to
    // Global Settings;
    // the DefaultMenuItems field in pGlobalSettings is
    // ready-made for this function; the "Workplace Shell"
    // notebook page for removing menu items sets this field with
    // the proper CTXT_xxx flags
    return ((ulMenuFilter)
            & ~(pGlobalSettings->DefaultMenuItems)
        );
}

/*
 *@@ wpModifyPopupMenu:
 *           this routine allows an object to modify a context
 *           menu. We add the various XFolder menu entries here
 *           by calling the common XFolder function in menus.c,
 *           which is also used by the XFldDisk class.
 */

SOM_Scope BOOL  SOMLINK xf_wpModifyPopupMenu(XFolder   *somSelf,
                                                    HWND  hwndMenu,
                                                    HWND  hwndCnr,
                                                    ULONG iPosition)
{
    BOOL                rc = TRUE;
    HWND                hwndCnr2 = hwndCnr;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpModifyPopupMenu");

    // call parent
    XFolder_parent_WPFolder_wpModifyPopupMenu(somSelf,hwndMenu,hwndCnr,iPosition);

    if (hwndCnr == NULLHANDLE)
    {
        // bug in Warp 3: if the popup menu is requested
        // on container whitespace, hwndCnr is passed as
        // NULLHANDLE; we therefore use this ugly
        // workaround
        XFolderData     *somThis = XFolderGetData(somSelf);
        hwndCnr2 = _hwndCnrSaved;   // set by WM_INITMENU in fnwpSubclassedFolderFrame
    }

    // call menu manipulator common to XFolder and XFldDisk (menus.c)
    rc = mnuModifyPopupMenu(somSelf, hwndMenu, hwndCnr2, iPosition);

    return (rc);
}

/*
 *@@ wpMenuItemSelected:
 *           process input when any menu item was selected;
 *           we do this by calling the common XFolder function
 *           in menus.c, which is also used by XFldDisk.
 */

SOM_Scope BOOL  SOMLINK xf_wpMenuItemSelected(XFolder *somSelf,
                                                 HWND hwndFrame,
                                                 ULONG ulMenuId)
{
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpMenuItemSelected");

    // call the menu item checker common to XFolder and XFldDisk
    // (menus.c); this returns TRUE if one of the manipulated
    // menu items was selected
    if (mnuMenuItemSelected(somSelf, hwndFrame, ulMenuId))
        return (TRUE);
    else
        // none of our menu items: pass on to parent
        return (XFolder_parent_WPFolder_wpMenuItemSelected(somSelf, hwndFrame, ulMenuId));
}

/*
 *@@ wpMenuItemHelpSelected:
 *           display help for a context menu item.
 */

SOM_Scope BOOL  SOMLINK xf_wpMenuItemHelpSelected(XFolder *somSelf,
                                                     ULONG MenuId)
{
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpMenuItemHelpSelected");

    // call the common help processor in menus.c;
    // if this returns TRUE, help was requested for one
    // of the new menu items
    if (mnuMenuItemHelpSelected(somSelf, MenuId))
        return TRUE;
    else
        // else: none of our menu items, call default
        return (XFolder_parent_WPFolder_wpMenuItemHelpSelected(somSelf,
                                                               MenuId));
}

/*
 *@@ wpOpen:
 *           this instance method opens a new folder view.
 *           This is one of the main hooks where the XFolder
 *           features are inserted into the WPS.
 *           We call the parent method first (which will create
 *           the folder window) and then subclass the
 *           resulting frame window with the new
 *           fnwpSubclassedFolderFrame window procedure.
 */

SOM_Scope HWND  SOMLINK xf_wpOpen(XFolder *somSelf, HWND hwndCnr,
                                     ULONG ulView, ULONG param)
{
    HWND                hwndNewFrame;
    XFolderMethodDebug("XFolder","xf_wpOpen");

    // have parent do the window creation
    hwndNewFrame = XFolder_parent_WPFolder_wpOpen(somSelf, hwndCnr, ulView, param);

    if (   (ulView == OPEN_CONTENTS)
        || (ulView == OPEN_TREE)
        || (ulView == OPEN_DETAILS)
       )
    {
        PSUBCLASSEDLISTITEM    psli;
        PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
        XFolderData *somThis = XFolderGetData(somSelf);

        // subclass the new folder frame window
        psli = cmnSubclassFolderFrame(hwndNewFrame, somSelf, somSelf, ulView);

        // change the window title to full path, if allowed
        if (    (_bFullPath == 1)
             || ((_bFullPath == 2) && (pGlobalSettings->FullPath))
           )
            SetOneFrameWndTitle(somSelf, hwndNewFrame);

        // add status bar, if allowed:
        // 1) status bar only if allowed for the current folder
        if (    (_bStatusBar == STATUSBAR_ON)
             || (   (_bStatusBar == STATUSBAR_DEFAULT)
                 && (pGlobalSettings->StatusBar)
                )
           )
            // 2) no status bar for active Desktop
            if (somSelf != _wpclsQueryActiveDesktop(_WPDesktop))
                // 3) check that subclassed list item is valid
                if (psli)
                    // 4) status bar only if allowed for the current view type
                    if (    (   (ulView == OPEN_CONTENTS)
                             && (pGlobalSettings->SBForViews & SBV_ICON)
                            )
                         || (   (ulView == OPEN_TREE)
                             && (pGlobalSettings->SBForViews & SBV_TREE)
                            )
                         || (   (ulView == OPEN_DETAILS)
                             && (pGlobalSettings->SBForViews & SBV_DETAILS)
                            )
                        )
                        _xfShowStatusBar(somSelf, psli, TRUE);

        // replace sort stuff
        if (pGlobalSettings->ReplaceSort)
        {
            hwndCnr = xwpsQueryCnrFromFrame(hwndNewFrame);
            if (hwndCnr)
                _xfSetCnrSort(somSelf, hwndCnr, FALSE);
        }
    }

    return (hwndNewFrame);
}

/*
 *@@ wpRefresh:
 *           this method updates a folder; after doing
 *           this, we will also update the title of the
 *           window and maybe status bars.
 */

SOM_Scope BOOL  SOMLINK xf_wpRefresh(XFolder *somSelf, ULONG ulView,
                                        PVOID pReserved)
{
    BOOL        rc;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpRefresh");

    rc = XFolder_parent_WPFolder_wpRefresh(somSelf, ulView, pReserved);

    _xfForEachOpenView(somSelf,
                (ULONG)2,           // update
                (PFNWP)fncbUpdateStatusBars);

    xthrPostWorkerMsg(WOM_REFRESHFOLDERVIEWS, (MPARAM)somSelf, MPNULL);

    return rc;
}

/*
 *@@ wpSetFldrAttr:
 *           this sets new container attributes
 *           (those CV_* flags) for the specified folder view
 *           (OPEN_CONTENTS, OPEN_TREE, OPEN_DETAILS)
 */

SOM_Scope BOOL  SOMLINK xf_wpSetFldrAttr(XFolder *somSelf, ULONG Attr,
                                         ULONG ulView)
{
    XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpSetFldrAttr");

    #ifdef DEBUG_SORT
    {
        CHAR szInfo[300] = "";
        _Pmpf(("wpSetFldrAttr for %s", _wpQueryTitle(somSelf)));
        if (Attr & CV_ICON)
            strcpy(szInfo, "CV_ICON ");
        if (Attr & CV_NAME)
            strcat(szInfo, "CV_NAME ");
        if (Attr & CV_TEXT)
            strcat(szInfo, "CV_TEXT ");
        if (Attr & CV_TREE)
            strcat(szInfo, "CV_TREE ");
        if (Attr & CV_DETAIL)
            strcat(szInfo, "CV_DETAIL ");
        if (Attr & CV_MINI)
            strcat(szInfo, "CV_MINI ");
        if (Attr & CV_FLOW)
            strcat(szInfo, "CV_FLOW ");
        if (Attr & CA_OWNERDRAW)
            strcat(szInfo, "CA_OWNERDRAW ");
        if (Attr & CA_OWNERPAINTBACKGROUND)
            strcat(szInfo, "CA_OWNERPAINTBACKGROUND ");

        _Pmpf(("  Flags: %s", szInfo));
    }
    #endif

    return (XFolder_parent_WPFolder_wpSetFldrAttr(somSelf, Attr,
                                                  ulView));
}

/*
 *@@ wpQueryFldrAttr:
 *           this returns the current container attributes
 *           (those CV_* flags) for the specified folder view
 *           (OPEN_CONTENTS, OPEN_TREE, OPEN_DETAILS)
 */

SOM_Scope ULONG  SOMLINK xf_wpQueryFldrAttr(XFolder *somSelf,
                                            ULONG ulView)
{
    ULONG ulAttr = 0;
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpQueryFldrAttr");

    ulAttr = XFolder_parent_WPFolder_wpQueryFldrAttr(somSelf,
                                                    ulView);
    #ifdef DEBUG_SORT
    {
        CHAR szInfo[300] = "";
        _Pmpf(("wpQueryFldrAttr for %s", _wpQueryTitle(somSelf)));
        if (ulAttr & CV_ICON)
            strcpy(szInfo, "CV_ICON ");
        if (ulAttr & CV_NAME)
            strcat(szInfo, "CV_NAME ");
        if (ulAttr & CV_TEXT)
            strcat(szInfo, "CV_TEXT ");
        if (ulAttr & CV_TREE)
            strcat(szInfo, "CV_TREE ");
        if (ulAttr & CV_DETAIL)
            strcat(szInfo, "CV_DETAIL ");
        if (ulAttr & CV_MINI)
            strcat(szInfo, "CV_MINI ");
        if (ulAttr & CV_FLOW)
            strcat(szInfo, "CV_FLOW ");
        if (ulAttr & CA_OWNERDRAW)
            strcat(szInfo, "CA_OWNERDRAW ");
        if (ulAttr & CA_OWNERPAINTBACKGROUND)
            strcat(szInfo, "CA_OWNERPAINTBACKGROUND ");

        _Pmpf(("  Flags: %s", szInfo));
    }
    #endif

    return (ulAttr);
}

/*
 *@@ wpAddToContent:
 *           this method is overridden to intercept the
 *           notification of the "Added an object to a folder"
 *           event for subclasses that define their own folder view.
 *           The parent must always be called.
 */

SOM_Scope BOOL  SOMLINK xf_wpAddToContent(XFolder *somSelf,
                                             WPObject* Object)
{
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpAddToContent");

    #ifdef DEBUG_CNRCONTENT
         _Pmpf(("wpAddToContent, folder: %s, object: %s",
             _wpQueryTitle(somSelf),
             _wpQueryTitle(Object)));
    #endif

    return (XFolder_parent_WPFolder_wpAddToContent(somSelf, Object));
}

/*
 *@@ wpDeleteFromContent:
 *           this method should be overridden to intercept the
 *           notification of the "Removed an object from a folder"
 *           event for subclasses that define their own folder view.
 *           The parent must always be called.
 */

SOM_Scope BOOL  SOMLINK xf_wpDeleteFromContent(XFolder *somSelf,
                                                  WPObject* Object)
{
    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpDeleteFromContent");

    #ifdef DEBUG_CNRCONTENT
         _Pmpf(("wpDeleteFromContent, folder: %s, object: %s",
             _wpQueryTitle(somSelf),
             _wpQueryTitle(Object)));
    #endif

    return (XFolder_parent_WPFolder_wpDeleteFromContent(somSelf,
                                                        Object));
}

/*
 *@@ wpStoreIconPosData:
 *           this method is documented only for Warp 4
 *           (but exists in Warp 3 also, see WPFOLDER.H);
 *           it is called when an open folder in icon or
 *           details view is closed.
 *           The WPS then apparently saves the .ICONPOS
 *           data to disk; we only override this method to be
 *           notified that we need to to update our
 *           icon order data (xfUpdateOrderedContent).
 */

SOM_Scope BOOL  SOMLINK xf_wpStoreIconPosData(XFolder *somSelf,
                                                 PICONPOS pIconPos,
                                                 ULONG cbSize)
{
    BOOL rc;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpStoreIconPosData");

    rc =  (XFolder_parent_WPFolder_wpStoreIconPosData(somSelf,
                                                       pIconPos,
                                                       cbSize));

    #ifdef DEBUG_SORT
        _Pmpf(("wpStoreIconPosData -- pIconPos: 0x%lX, ulSize: 0x%lX",
            pIconPos, cbSize));
    #endif
    xthrPostWorkerMsg(WOM_INVALIDATEORDEREDCONTENT,
        (MPARAM)somSelf, MPNULL);

    return (rc);
}

/*
 *@@ wpMoveObject:
 *           this is called when the folder is moved to a
 *           different location; we then need to update
 *           the titles of this folder AND of possibly open
 *           subfolders with the full path; we pass this
 *           to the Worker thread
 */

SOM_Scope BOOL  SOMLINK xf_wpMoveObject(XFolder *somSelf,
                                           WPFolder* Folder)
{
    BOOL rc;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpMoveObject");

    /* call the parent method first, which will actually move the
       folder */
    rc = XFolder_parent_WPFolder_wpMoveObject(somSelf, Folder);

    xthrPostWorkerMsg(WOM_REFRESHFOLDERVIEWS, (MPARAM)somSelf, MPNULL);

    return rc;
}

/*
 *@@ wpSetTitle:
 *           this is called when the folder is renamed.
 *           We then need to update the titles of this
 *           folder AND of possibly open subfolders with
 *           the full path; we pass this task to the Worker
 *           thread, since it may take a while
 */

SOM_Scope BOOL  SOMLINK xf_wpSetTitle(XFolder *somSelf, PSZ pszNewTitle)
{
    BOOL rc;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpSetTitle");

    rc = XFolder_parent_WPFolder_wpSetTitle(somSelf, pszNewTitle);

    if (_wpFindUseItem(somSelf, USAGE_OPENVIEW, NULL))
        xthrPostWorkerMsg(WOM_REFRESHFOLDERVIEWS, (MPARAM)somSelf, MPNULL);

    return (rc);
}

/*
 *@@ wpSetFldrSort:
 *           apparently, this method normally gets called by the
 *           WPS every time it tries to sort a folder. That is,
 *           when one of the "Sort" menu items is selected or when
 *           the folder is sorted for another reason, e.g. because
 *           "Always sort" is on and a file in the folder was
 *           renamed.
 *
 *           The WPS ref. says about this method:
 *           "This instance method sets the sort attributes on
 *           the folder window and  saves those values in the
 *           instance data. Note:  This method only rearranges a
 *           folder open in icon view. If (pSortRecord == NULL),
 *           the values are reset to the default sort values."
 *
 *           However, the description of this method in the WPS
 *           ref. is complete garbage. The SORTFASTINFO structure
 *           described there is obviously not used, but some
 *           undocumented data instead. (I guess that's why IBM
 *           uses a PVOID here, so they need not apologize.)
 *           Also, this method rearranges _all_ open folders, not
 *           just icons views.
 *
 *           Anyway, if XFolder extended sorting is enabled,
 *           we can intercept this method call to prevent the
 *           WPS from sorting the container. We will then not
 *           call the default method, but our own one instead.
 *           Since XFolder completely takes over the other sort
 *           functions, this method probably only gets called
 *           when files are renamed any more.
 */

SOM_Scope BOOL  SOMLINK xf_wpSetFldrSort(XFolder *somSelf, PVOID pSortRecord,
                                         ULONG ulView, ULONG ulType)
{
    PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();

    if (pGlobalSettings->ReplaceSort) {
        HWND hwndFrame = xwpsQueryFrameFromView(somSelf, ulView);
        if (hwndFrame) {
            HWND hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);
            if (hwndCnr) {
                _xfSetCnrSort(somSelf, hwndCnr,
                            TRUE);  // enfore cnr sort
                return (TRUE);
            }
        }
    }

    return (XFolder_parent_WPFolder_wpSetFldrSort(somSelf,
                                    pSortRecord,
                                    ulView,
                                    ulType));
}

/*
 *@@ wpQueryDefaultHelp:
 *           this instance method specifies the default
 *           help panel for this instance; XFolder will
 *           return something different for the
 *           Config folder and its subfolders.
 */

SOM_Scope BOOL  SOMLINK xf_wpQueryDefaultHelp(XFolder *somSelf,
                                                 PULONG pHelpPanelId,
                                                 PSZ HelpLibrary)
{
    BOOL        rc;

    // XFolderData *somThis = XFolderGetData(somSelf);
    XFolderMethodDebug("XFolder","xf_wpQueryDefaultHelp");

    if (xwpsResidesBelow(somSelf,
                _wpclsQueryFolder(_WPFolder, XFOLDER_CONFIGID, TRUE)))
    {
        // somSelf is in the config folder hierarchy:
        // display help for config folders
        strncpy(HelpLibrary, cmnQueryHelpLibrary(), CCHMAXPATH);
        *pHelpPanelId = ID_XMH_CONFIGFOLDER;
        rc = TRUE;
    } else
        rc = (XFolder_parent_WPFolder_wpQueryDefaultHelp(somSelf,
                                                           pHelpPanelId,
                                                           HelpLibrary));

    return (rc);
}


/* ******************************************************************
 *                                                                  *
 *   here come the XFolder class methods                            *
 *                                                                  *
 ********************************************************************/

/*
 *@@ xfclsForEachOpenView:
 *           this class method goes through all open folder windows and calls
 *           pfnwpCallback for each open view of each open folder.
 *
 *           The following params will be passed to pfnwpCallback:
 *           -- hwnd    HWND       hwndView   the hwnd of the view frame window;
 *           -- mp1     ULONG      ulView     the view type (as def'd in wpOpen)
 *           -- mp2     XFolder*   pFolder    the currently open folder.
 *
 *           This method does not return until all views have been processed.
 *           You might want to call this method in a different thread if the task
 *           will take long.
 */

SOM_Scope BOOL  SOMLINK xfM_xfclsForEachOpenView(M_XFolder *somSelf,
                                                    ULONG ulMsg,
                                                    PFNWP pfnwpCallback)
{
    XFolder     *pFolder;
    // M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xfM_xfclsForEachOpenView");

    for ( pFolder = _wpclsQueryOpenFolders(somSelf, NULL, QC_FIRST, FALSE);
          pFolder;
          pFolder = _wpclsQueryOpenFolders(somSelf, pFolder, QC_NEXT, FALSE))
    {
        if (_somIsA(pFolder, _WPFolder))
        {
            _xfForEachOpenView(pFolder, ulMsg, pfnwpCallback);
        }
    }
    return TRUE;
}

/*
 *@@ xfclsQueryFavoriteFolder:
 *           This returns "favorite" folders.
 *           If pFolder == NULL, the first favorite folder is returned,
 *           otherwise the favorite folder which comes after pFolder
 *           in the favorite folder list.
 *           This returns NULL if no more folders are found.
 */

SOM_Scope XFolder*  SOMLINK xfM_xfclsQueryFavoriteFolder(M_XFolder *somSelf,
                                                            XFolder* pFolder)
{
    BOOL        fSemOwned = FALSE;
    M_XFolderData *somThis = M_XFolderGetData(somSelf);
    PCONTENTMENULISTITEM    pliFavoriteFolders = pcmliFavoriteFolders,
                            pItem;

    M_XFolderMethodDebug("M_XFolder","xfM_xfclsQueryFavoriteFolder");

    TRY_LOUD(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxFavoriteFolders, 4000) == NO_ERROR);
        if (fSemOwned)
        {
            if (pliFavoriteFolders == NULL)
            {
                // if the list of favorite folders has not yet been built
                // in the class data, we will do this now
                CHAR        szFavorites[1000], szDummy[1000];
                PSZ         pszFavorites;
                // ULONG       rc;

                PrfQueryProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_FAVORITEFOLDERS,
                            "", &szFavorites, sizeof(szFavorites));
                pszFavorites = szFavorites;
                do {
                    HOBJECT          hObject;
                    WPFolder         *pFolder2;
                    sscanf(pszFavorites, "%lX %s", &hObject, &szDummy);
                    pFolder2 = _wpclsQueryObject(_WPFolder, hObject);
                    if (pFolder2) {
                        if (xwpsCheckObject(pFolder2))
                            if (_somIsA(pFolder2, _WPFolder))
                            {
                                 PCONTENTMENULISTITEM pNew = malloc(sizeof(CONTENTMENULISTITEM));
                                 pNew->pFolder = pFolder2;
                                 lstAppendItem((PLISTITEM*)&pliFavoriteFolders, NULL,
                                     (PLISTITEM)pNew);
                            }
                    }
                    pszFavorites += 6;
                } while (strlen(pszFavorites) > 0);

                pcmliFavoriteFolders = pliFavoriteFolders;
            }

            pItem = pliFavoriteFolders;
            if (pFolder) {
                // folder given as param: look for this folder
                // and return the following in the list
                while (pItem)
                    if (pItem->pFolder == pFolder) {
                        pItem = pItem->pNext;
                        break;
                    }
                    else
                        pItem = pItem->pNext;
            } // else: pItem == pliFavoriteFolders
        }
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned) {
        DosReleaseMutexSem(hmtxFavoriteFolders);
        fSemOwned = FALSE;
    }

    if (pItem)
        return (pItem->pFolder);
    else
        return (NULL);
}

/*
 *@@ xfclsQueryQuickOpenFolder:
 *           This returns folders which have the "QuickOpen" flag on.
 *           If pFolder == NULL, the first such folder is returned,
 *           otherwise the folder which comes after pFolder
 *           in the quick-open folder list.
 *           This returns NULL if no more folders are found.
 */

SOM_Scope XFolder*  SOMLINK xfM_xfclsQueryQuickOpenFolder(M_XFolder *somSelf,
                                                          XFolder* pFolder)
{
    BOOL fSemOwned = FALSE;
    M_XFolderData *somThis = M_XFolderGetData(somSelf);
    PCONTENTMENULISTITEM    pliQuickOpenFolders = pcmliQuickOpenFolders,
                            pItem;
    M_XFolderMethodDebug("M_XFolder","xfM_xfclsQueryQuickOpenFolder");

    TRY_LOUD(excpt1)
    {
        fSemOwned = (DosRequestMutexSem(hmtxQuickOpenFolders, 4000) == NO_ERROR);
        if (fSemOwned)
        {
            if (pliQuickOpenFolders == NULL)
            {
                // if the list of QuickOpen folders has not yet been built
                // in the class data, we will do this now
                CHAR        szQuickOpen[1000], szDummy[1000];
                PSZ         pszQuickOpen;
                // ULONG       rc;

                PrfQueryProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_QUICKOPENFOLDERS,
                            "", &szQuickOpen, sizeof(szQuickOpen));
                pszQuickOpen = szQuickOpen;
                do {
                    HOBJECT          hObject;
                    WPFolder         *pFolder2;
                    sscanf(pszQuickOpen, "%lX %s", &hObject, &szDummy);
                    pFolder2 = _wpclsQueryObject(_WPFolder, hObject);
                    if (pFolder2) {
                        if (xwpsCheckObject(pFolder2))
                            if (_somIsA(pFolder2, _WPFolder))
                            {
                                 PCONTENTMENULISTITEM pNew = malloc(sizeof(CONTENTMENULISTITEM));
                                 pNew->pFolder = pFolder2;
                                 lstAppendItem((PLISTITEM*)&pliQuickOpenFolders, NULL,
                                     (PLISTITEM)pNew);
                            }
                    }
                    pszQuickOpen += 6;
                } while (strlen(pszQuickOpen) > 0);

                pcmliQuickOpenFolders = pliQuickOpenFolders;
            }

            pItem = pliQuickOpenFolders;
            if (pFolder) {
                // folder given as param: look for this folder
                // and return the following in the list
                while (pItem)
                    if (pItem->pFolder == pFolder) {
                        pItem = pItem->pNext;
                        break;
                    }
                    else
                        pItem = pItem->pNext;
            } // else: pItem == pliQuickOpenFolders
        }
    }
    CATCH(excpt1) { } END_CATCH;

    if (fSemOwned)
    {
        DosReleaseMutexSem(hmtxQuickOpenFolders);
        fSemOwned = FALSE;
    }

    if (pItem)
        return (pItem->pFolder);
    else
        return (NULL);
}

/*
 *@@ xfclsSetDefaultTitle:
 *           changes the default title to pszNewTitle. The default
 *           title appears in the third column in folder Details
 *           views.
 */

SOM_Scope BOOL SOMLINK xfM_xfclsSetDefaultTitle(M_XFolder *somSelf,
                                                   PSZ pszNewTitle)
{
    M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xfM_xfclsSetDefaultTItle");

    strncpy(_szDefaultTitle, pszNewTitle, sizeof(_szDefaultTitle)-1);
    return (PrfWriteProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_DEFAULTTITLE,
                _szDefaultTitle));
}

/*
 *@@ wpclsInitData:
 *           this initializes the WPFolder / XFolder class as a whole;
 *           we need to call the parent and then set some XFolder
 *           data
 */

SOM_Scope void  SOMLINK xfM_wpclsInitData(M_XFolder *somSelf)
{
    PTHREADGLOBALS pThreadGlobals = xthrQueryGlobals();

    M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xfM_wpclsInitData");

    M_XFolder_parent_M_WPFolder_wpclsInitData(somSelf);

    if (hmtxFavoriteFolders == NULLHANDLE) {
        // first call:

        // create mutex semaphores for serialized access

        cmnInitPSLI();

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

        // initialize other data
        pcmliFavoriteFolders = NULL;
        pcmliQuickOpenFolders = NULL;

        PrfQueryProfileString(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_DEFAULTTITLE,
                 "XFolder", &(_szDefaultTitle), sizeof(_szDefaultTitle));

        cmnLoadFolderHotkeys();

        // register class for supplementary object
        // windows, which are created for each folder view
        // which is opened
        WinRegisterClass(WinQueryAnchorBlock(HWND_DESKTOP),
                         WNDCLASS_SUPPLOBJECT,    // class name
                         (PFNWP)fnwpSupplObject,    // Window procedure
                         0,       // class style
                         4);      // extra window words for SUBCLASSEDLISTITEM
                                  // pointer (see cmnSubclassFolderFrame)
    }
}

/*
 *@@ wpclsCreateDefaultTemplates:
 *           this is called by the system to allow a class to
 *           create its default templates. The default WPS
 *           behavior is to create new templates if the class
 *           default title is different from the existing
 *           templates, but since we are replacing the class,
 *           we will have to suppress this in order not to
 *           crowd the Templates folder.
 */

SOM_Scope BOOL  SOMLINK xfM_wpclsCreateDefaultTemplates(M_XFolder *somSelf,
                                                        WPObject* Folder)
{
    // M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xfM_wpclsCreateDefaultTemplates");

    // we only override this class method if it is
    // being called for the XFolder class object itself.
    // If this is being called for a subclass, we use
    // the parent method, because we do not want to
    // break the default behavior for subclasses.
    if (somSelf == _XFolder)
        return (TRUE);
        // means that the Templates folder should _not_ create templates
        // by itself; we pretend that we've done this
    else
        return (M_XFolder_parent_M_WPFolder_wpclsCreateDefaultTemplates(somSelf,
                                                                    Folder));
}

/*
 *@@ wpclsQueryTitle:
 *           tell the WPS the new class name: XFolder or whatever
 *           was specified in the "Workplace Shell" object
 */

SOM_Scope PSZ  SOMLINK xfM_wpclsQueryTitle(M_XFolder *somSelf)
{
    M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xf_wpclsQueryTitle");

    if (somSelf == _XFolder)
        return (_szDefaultTitle);
    else
        return (M_XFolder_parent_M_WPFolder_wpclsQueryTitle(somSelf));
}

/*
 *@@ wpclsQueryIconData:
 *           give folders a new default closed icon, if the
 *           global settings allow this.
 *           This is loaded from /ICONS/ICONS.DLL.
 *           Unfortunately, it appears to be impossible to
 *           dynamically load default icons as specified for
 *           the ICONINFO structure in the WPS reference.
 *           ICON_FILE at least doesn't work, and the format
 *           for ICON_DATA is not explained. So we have to
 *           use a DLL here.
 */

SOM_Scope ULONG  SOMLINK xfM_wpclsQueryIconData(M_XFolder *somSelf,
                                                PICONINFO pIconInfo)
{
    ULONG ulrc;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    // M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xfM_wpclsQueryIconData");

    if (pGlobalSettings->ReplIcons)
    {
        // icon replacements allowed:
        if (pIconInfo) {
            pIconInfo->fFormat = ICON_RESOURCE;
            pIconInfo->hmod = cmnQueryIconsDLL();
            pIconInfo->resid = 100;
        }
        ulrc = sizeof(ICONINFO);
    }
    else
        // icon replacements not allowed: call default
        ulrc = M_XFolder_parent_M_WPFolder_wpclsQueryIconData(somSelf,
                                                                pIconInfo);
    return (ulrc);
}

/*
 *@@ wpclsQueryIconDataN:
 *           give folders a new default open icon, if the
 *           global settings allow this.
 *           See the notes for wpclsQueryIconData.
 */

SOM_Scope ULONG  SOMLINK xfM_wpclsQueryIconDataN(M_XFolder *somSelf,
                                                 ICONINFO* pIconInfo,
                                                 ULONG ulIconIndex)
{
    ULONG ulrc;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    // M_XFolderData *somThis = M_XFolderGetData(somSelf);
    M_XFolderMethodDebug("M_XFolder","xfM_wpclsQueryIconDataN");

    if (pGlobalSettings->ReplIcons)
    {
        // icon replacements allowed:
        if (pIconInfo) {
            pIconInfo->fFormat = ICON_RESOURCE;
            pIconInfo->hmod = cmnQueryIconsDLL();
            pIconInfo->resid = 101;
        }
        ulrc = sizeof(ICONINFO);
    }
    else
        // icon replacements not allowed: call default
        ulrc = M_XFolder_parent_M_WPFolder_wpclsQueryIconDataN(somSelf,
                                                                pIconInfo,
                                                                ulIconIndex);
    return (ulrc);
}

/* ******************************************************************
 *                                                                  *
 *   here come the XFldStartup methods                              *
 *                                                                  *
 ********************************************************************/

/*
 *@@ wpModifyPopupMenu:
 *           add a "Process content" menu item to this
 *           popup menu; the other menu items are inherited
 *           from XFolder
 */

SOM_Scope BOOL  SOMLINK xfstup_wpModifyPopupMenu(XFldStartup *somSelf,
                                                 HWND hwndMenu,
                                                 HWND hwndCnr,
                                                 ULONG iPosition)
{
    BOOL rc;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();
    /* XFldStartupData *somThis = XFldStartupGetData(somSelf); */
    XFldStartupMethodDebug("XFldStartup","xfstup_wpModifyPopupMenu");

    rc = XFldStartup_parent_XFolder_wpModifyPopupMenu(somSelf,
                                                         hwndMenu,
                                                         hwndCnr,
                                                         iPosition);

    winhInsertMenuSeparator(hwndMenu, MIT_END,
            (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

    winhInsertMenuItem(hwndMenu,
            MIT_END,
            (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_PROCESSCONTENT),
            pNLSStrings->pszProcessContent,
            MIS_TEXT, 0);

    return (rc);
}

/*
 *@@ wpMenuItemSelected:
 *           react to selection of "Process content"
 *           menu item
 */

SOM_Scope BOOL  SOMLINK xfstup_wpMenuItemSelected(XFldStartup *somSelf,
                                                  HWND hwndFrame,
                                                  ULONG ulMenuId)
{
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    /* XFldStartupData *somThis = XFldStartupGetData(somSelf); */
    XFldStartupMethodDebug("XFldStartup","xfstup_wpMenuItemSelected");

    if ( (ulMenuId - pGlobalSettings->VarMenuOffset) == ID_XFMI_OFS_PROCESSCONTENT ) {
        if (cmnMessageBoxMsg((hwndFrame) ? hwndFrame : HWND_DESKTOP,
                            116, 138, MB_YESNO | MB_DEFBUTTON2)
                        == MBID_YES) {
                xthrPostWorkplaceObjectMsg(XOM_BEGINSTARTUP, MPNULL, MPNULL);
        }
        return (TRUE);
    } else
        return (XFldStartup_parent_XFolder_wpMenuItemSelected(somSelf,
                                                          hwndFrame,
                                                          ulMenuId));
}

/*
 *@@ wpMenuItemHelpSelected:
 *           display help for "Process content"
 *           menu item
 */

SOM_Scope BOOL  SOMLINK xfstup_wpMenuItemHelpSelected(XFldStartup *somSelf,
                                                      ULONG MenuId)
{
    /* XFldStartupData *somThis = XFldStartupGetData(somSelf); */
    XFldStartupMethodDebug("XFldStartup","xfstup_wpMenuItemHelpSelected");

    return (XFldStartup_parent_XFolder_wpMenuItemHelpSelected(somSelf,
                                                              MenuId));
}

/*
 *@@ wpQueryDefaultHelp:
 *           this instance method specifies the default
 *           help panel for this instance; display some
 *           help for the Startup folder
 */

SOM_Scope BOOL  SOMLINK xfstup_wpQueryDefaultHelp(XFldStartup *somSelf,
                                                  PULONG pHelpPanelId,
                                                  PSZ HelpLibrary)
{
    /* XFldStartupData *somThis = XFldStartupGetData(somSelf); */
    XFldStartupMethodDebug("XFldStartup","xfstup_wpQueryDefaultHelp");

    strcpy(HelpLibrary, cmnQueryHelpLibrary());
    *pHelpPanelId = ID_XMH_STARTUPSHUTDOWN;
    return (TRUE);

    /* return (XFldStartup_parent_XFolder_wpQueryDefaultHelp(somSelf,
                                                          pHelpPanelId,
                                                          HelpLibrary)); */
}

/*
 *@@ wpclsQueryTitle:
 *           tell the WPS the new class name: "XFolder Startup"
 */

SOM_Scope PSZ  SOMLINK xfstupM_wpclsQueryTitle(M_XFldStartup *somSelf)
{
    /* M_XFldStartupData *somThis = M_XFldStartupGetData(somSelf); */
    M_XFldStartupMethodDebug("M_XFldStartup","xfstupM_wpclsQueryTitle");

    return ("XFolder Startup");
    // return (M_XFldStartup_parent_M_XFolder_wpclsQueryTitle(somSelf));
}

/*
 *@@ wpclsQueryStyle:
 *           we return a flag so that no templates are created
 *           for the Startup folder
 */

SOM_Scope ULONG  SOMLINK xfstupM_wpclsQueryStyle(M_XFldStartup *somSelf)
{
    /* M_XFldStartupData *somThis = M_XFldStartupGetData(somSelf); */
    M_XFldStartupMethodDebug("M_XFldStartup","xfstupM_wpclsQueryStyle");

    return (M_XFldStartup_parent_M_XFolder_wpclsQueryStyle(somSelf)
                | CLSSTYLE_NEVERTEMPLATE
                | CLSSTYLE_NEVERCOPY
                // | CLSSTYLE_NEVERDELETE
           );

}

/*
 *@@ wpclsQueryIconData:
 *           give the Startup folder a new closed icon
 */

SOM_Scope ULONG  SOMLINK xfstupM_wpclsQueryIconData(M_XFldStartup *somSelf,
                                                    PICONINFO pIconInfo)
{
    /* M_XFldStartupData *somThis = M_XFldStartupGetData(somSelf); */
    M_XFldStartupMethodDebug("M_XFldStartup","xfstupM_wpclsQueryIconData");

    if (pIconInfo) {
       pIconInfo->fFormat = ICON_RESOURCE;
       pIconInfo->resid   = ID_STARTICON1;
       pIconInfo->hmod    = modQueryHandle();
    }

    return (sizeof(ICONINFO));

    /* return (M_XFldStartup_parent_M_XFolder_wpclsQueryIconData(somSelf,
                                                              pIconInfo)); */
}

/*
 *@@ wpclsQueryIconDataN:
 *           give the Startup folder a new animated icon
 */

SOM_Scope ULONG  SOMLINK xfstupM_wpclsQueryIconDataN(M_XFldStartup *somSelf,
                                                     ICONINFO* pIconInfo,
                                                     ULONG ulIconIndex)
{
    /* M_XFldStartupData *somThis = M_XFldStartupGetData(somSelf); */
    M_XFldStartupMethodDebug("M_XFldStartup","xfstupM_wpclsQueryIconDataN");

    if (pIconInfo) {
       pIconInfo->fFormat = ICON_RESOURCE;
       pIconInfo->resid   = ID_STARTICON2;
       pIconInfo->hmod    = modQueryHandle();
    }

    return (sizeof(ICONINFO));

    /* return (M_XFldStartup_parent_M_XFolder_wpclsQueryIconDataN(somSelf,
                                                               pIconInfo,
                                                               ulIconIndex)); */
}


/* ******************************************************************
 *                                                                  *
 *   here come the XFldShutdown methods                             *
 *                                                                  *
 ********************************************************************/

/*
 *@@ wpQueryDefaultHelp:
 *           this instance method specifies the default
 *           help panel for this instance; display some
 *           help for the Shutdown folder
 */

SOM_Scope BOOL  SOMLINK xfshut_wpQueryDefaultHelp(XFldShutdown *somSelf,
                                                  PULONG pHelpPanelId,
                                                  PSZ HelpLibrary)
{
    /* XFldShutdownData *somThis = XFldShutdownGetData(somSelf); */
    XFldShutdownMethodDebug("XFldShutdown","xfshut_wpQueryDefaultHelp");

    strcpy(HelpLibrary, cmnQueryHelpLibrary());
    *pHelpPanelId = ID_XMH_STARTUPSHUTDOWN;
    return (TRUE);

    /* return (XFldShutdown_parent_XFolder_wpQueryDefaultHelp(somSelf,
                                                           pHelpPanelId,
                                                           HelpLibrary)); */
}

/*
 *@@ wpclsQueryTitle:
 *           tell the WPS the new class name: "XFolderShutdown"
 */

SOM_Scope PSZ  SOMLINK xfshutM_wpclsQueryTitle(M_XFldShutdown *somSelf)
{
    /* M_XFldShutdownData *somThis = M_XFldShutdownGetData(somSelf); */
    M_XFldShutdownMethodDebug("M_XFldShutdown","xfshutM_wpclsQueryTitle");

    return ("XFolder Shutdown");
    // return (M_XFldShutdown_parent_M_XFolder_wpclsQueryTitle(somSelf));
}

/*
 *@@ wpclsQueryStyle:
 *           we return a flag so that no templates are created
 *           for the Shutdown folder
 */

SOM_Scope ULONG  SOMLINK xfshutM_wpclsQueryStyle(M_XFldShutdown *somSelf)
{
    /* M_XFldShutdownData *somThis = M_XFldShutdownGetData(somSelf); */
    M_XFldShutdownMethodDebug("M_XFldShutdown","xfshutM_wpclsQueryStyle");

    return (M_XFldShutdown_parent_M_XFolder_wpclsQueryStyle(somSelf)
                | CLSSTYLE_NEVERTEMPLATE
                | CLSSTYLE_NEVERCOPY
                // | CLSSTYLE_NEVERDELETE
           );
}

/*
 *@@ wpclsQueryIconData:
 *           give the Shutdown folder a new closed icon
 */

SOM_Scope ULONG  SOMLINK xfshutM_wpclsQueryIconData(M_XFldShutdown *somSelf,
                                                    PICONINFO pIconInfo)
{
    /* M_XFldShutdownData *somThis = M_XFldShutdownGetData(somSelf); */
    M_XFldShutdownMethodDebug("M_XFldShutdown","xfshutM_wpclsQueryIconData");

    if (pIconInfo) {
       pIconInfo->fFormat = ICON_RESOURCE;
       pIconInfo->resid   = ID_SHUTICON1;
       pIconInfo->hmod    = modQueryHandle();
    }

    return (sizeof(ICONINFO));

    /* return (M_XFldShutdown_parent_M_XFolder_wpclsQueryIconData(somSelf,
                                                               pIconInfo)); */
}

/*
 *@@ wpclsQueryIconDataN:
 *           give the Shutdown folder a new animated icon
 */

SOM_Scope ULONG  SOMLINK xfshutM_wpclsQueryIconDataN(M_XFldShutdown *somSelf,
                                                     ICONINFO* pIconInfo,
                                                     ULONG ulIconIndex)
{
    /* M_XFldShutdownData *somThis = M_XFldShutdownGetData(somSelf); */
    M_XFldShutdownMethodDebug("M_XFldShutdown","xfshutM_wpclsQueryIconDataN");

    if (pIconInfo) {
       pIconInfo->fFormat = ICON_RESOURCE;
       pIconInfo->resid   = ID_SHUTICON2;
       pIconInfo->hmod    = modQueryHandle();
    }

    return (sizeof(ICONINFO));

    /* return (M_XFldShutdown_parent_M_XFolder_wpclsQueryIconDataN(somSelf,
                                                                pIconInfo,
                                                                ulIconIndex)); */
}


/* ******************************************************************
 *                                                                  *
 *   here come all the XFolder window procedures                    *
 *                                                                  *
 ********************************************************************/

/*
 *@@ fnwpStatusBar:
 *      since the status bar is just created as a static frame
 *      control, it is subclassed (by XFolder::xfShowStatusBar),
 *      and this is the wnd proc for this.
 *      This handles the new STBM_UPDATESTATUSBAR message (which
 *      is posted any time the status bar needs to be updated)
 *      and intercepts WM_TIMER and WM_PAINT.
 */

MRESULT EXPENTRY fnwpStatusBar(HWND hwndBar, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    PSTATUSBARDATA psbd = (PSTATUSBARDATA)WinQueryWindowULong(hwndBar, QWL_USER);
    MRESULT        mrc = 0;

    PGLOBALSETTINGS pGlobalSettings =
            cmnQueryGlobalSettings();

    if (psbd)
    {
        PFNWP      pfnwpStatusBarOriginal = psbd->pfnwpStatusBarOriginal;

        switch(msg)
        {
            /*
             * STBM_UPDATESTATUSBAR:
             *      mp1: MPNULL,
             *      mp2: MPNULL
             *      Update status bar text. We will set a timer
             *      for a short delay to filter out repetitive
             *      messages here.
             *      This timer is "one-shot" in that it will be
             *      started here and stopped as soon as WM_TIMER
             *      is received.
             */

            case STBM_UPDATESTATUSBAR: {
                if (psbd->idTimer == 0) {
                    // only if timer is not yet running: start it now
                    psbd->idTimer = WinStartTimer(WinQueryAnchorBlock(hwndBar),
                                                  hwndBar,
                                                  1,
                                                  100); // delay: 100 ms
                }
            break; }

            /*
             * WM_TIMER:
             *      a timer is started by STBM_UPDATESTATUSBAR
             *      to avoid flickering;
             *      we now compose the actual text to be displayed
             */

            case WM_TIMER: {
                CHAR szText[1000];

                TRY_LOUD(excpt1)
                {
                    // stop timer (it's just for one shot)
                    WinStopTimer(WinQueryAnchorBlock(hwndBar), hwndBar, 1);
                    psbd->idTimer = 0;

                    // if we're not fully populated yet, start timer again and quit;
                    // otherwise we would display false information.
                    // We need an extra flag in the status bar data because the
                    // FOI_POPULATEDWITHALL is reset to 0 by the WPS for some reason
                    // when an object is deleted from an open folder, and no further
                    // status bar updates would occur then
                    if (psbd->fFolderPopulated == FALSE) {
                        ULONG ulFlags = _wpQueryFldrFlags(psbd->somSelf);
                        #ifdef DEBUG_STATUSBARS
                            _Pmpf(( "  Folder flags: %lX", ulFlags ));
                            _Pmpf(( "  View: %s",
                                    (psbd->psli->ulView == OPEN_TREE) ? "Tree"
                                    : (psbd->psli->ulView == OPEN_CONTENTS) ? "Content"
                                    : (psbd->psli->ulView == OPEN_DETAILS) ? "Details"
                                    : "unknown"
                                 ));
                        #endif

                        // for tree views, check if folder is populated with folders;
                        // otherwise check for populated with all
                        if (    (   (psbd->psli->ulView == OPEN_TREE)
                                 && ((ulFlags & FOI_POPULATEDWITHFOLDERS) !=0)
                                )
                            || ((ulFlags & FOI_POPULATEDWITHALL) != 0)
                           )
                        {
                            psbd->fFolderPopulated = TRUE;
                        } else {
                            // folder not yet populated:
                            // restart timer with a lower frequency
                            // to have this checked again
                            psbd->idTimer = WinStartTimer(WinQueryAnchorBlock(hwndBar),
                                    hwndBar,
                                    1,
                                    300);   // this time, use 300 ms
                            // and stop
                            break;
                        }
                    }

                    // OK:
                    // translate the template text (szText) into something
                    // meaningful (statbars.c)
                    stbComposeText(psbd->somSelf,
                            psbd->psli->hwndCnr,    // cnr hwnd in frame wnd struct
                            szText, sizeof(szText));
                }
                CATCH(excpt1)
                {
                    strcpy(szText, "*** error composing text");
                } END_CATCH;

                // set window text (provokes WM_PAINT) and quit
                WinSetWindowText(hwndBar, szText);
            break; }

            /*
             * WM_PAINT:
             *      this, well, paints the status bar.
             *      Since the status bar is just a static control,
             *      we want to do some extra stuff to make it prettier.
             *      At this point, the window text (of the status bar)
             *      contains the fully translated status bar mnemonics,
             *      except for the tabulators ("$x" flags), which we'll
             *      deal with here.
             */

            case WM_PAINT: {
                // preparations:
                RECTL   rclBar, rcl;
                HPS hps = WinBeginPaint(hwndBar, NULLHANDLE, &rcl);

                TRY_LOUD(excpt1)
                {
                    POINTL  ptl1;
                    CHAR    szText[1000], szTemp[100] = "0";
                    USHORT  usLength;
                    LONG    lNextX;
                    PSZ     p1, p2, p3;

                    WinQueryWindowRect(hwndBar, &rclBar);
                    // switch to RGB mode
                    GpiCreateLogColorTable(hps, 0,
                        LCOLF_RGB,
                        0, 0, NULL);

                    // 1) draw background
                    WinFillRect(hps, &rclBar, pGlobalSettings->lSBBgndColor);

                    // 2) draw 3D frame in selected style
                    if (pGlobalSettings->SBStyle == SBSTYLE_WARP3RAISED)
                        // Warp 3 style, raised
                        gpihDraw3DFrame(hps, &rclBar, 1, TRUE);
                    else if (pGlobalSettings->SBStyle == SBSTYLE_WARP3SUNKEN)
                        // Warp 3 style, sunken
                        gpihDraw3DFrame(hps, &rclBar, 1, FALSE);
                    else if (pGlobalSettings->SBStyle == SBSTYLE_WARP4MENU) {
                        // Warp 4 menu style: draw 3D line at top only
                        rcl = rclBar;
                        rcl.yBottom = rcl.yTop-2;
                        gpihDraw3DFrame(hps, &rcl, 1, FALSE);
                    } else {
                        // Warp 4 button style
                        rcl = rclBar;
                        // draw "sunken" outer rect
                        gpihDraw3DFrame(hps, &rclBar, 2, FALSE);
                        // draw "raised" inner rect
                        WinInflateRect(WinQueryAnchorBlock(hwndBar),
                                &rcl, -1, -1);
                        gpihDraw3DFrame(hps, &rcl, 2, TRUE);
                    }

                    // 3) start working on text; we do "simple" GpiCharString
                    //    if no tabulators are defined, but calculate some
                    //    subrectangles otherwise
                    WinQueryWindowText(hwndBar, sizeof(szText)-1, &szText[0]);
                            // szText now has the translated status bar text
                            // except for the tabulators ("$x" keys)
                    p1 = szText;
                    p2 = NULL;
                    ptl1.x = 7;

                    do {    // while tabulators are present

                        // search for tab mnemonic
                        if (p2 = strstr(p1, "$x(")) {
                            // tab found: calculate next x position into lNextX
                            usLength = (p2-p1);
                            strcpy(szTemp, "100");
                            if (p3 = strchr(p2, ')')) {
                                PSZ p4 = strchr(p2, '%');
                                strncpy(szTemp, p2+3, p3-p2-3);
                                // get the parameter
                                sscanf(szTemp, "%d", &lNextX);

                                if (lNextX < 0) {
                                    // if the result is negative, it's probably
                                    // meant to be an offset from the right
                                    // status bar border
                                    lNextX = (rclBar.xRight + lNextX); // lNextX is negative
                                }
                                else if ((p4) && (p4 < p3)) {
                                    // if we have a '%' char before the closing
                                    // bracket, consider lNextX a percentage
                                    // parameter and now translate it into an
                                    // absolute x position using the status bar's
                                    // width
                                    lNextX = (rclBar.xRight * lNextX) / 100;
                                }
                            } else
                                p2 = NULL;
                        } else
                            usLength = strlen(p1);

                        ptl1.y = (pGlobalSettings->SBStyle == SBSTYLE_WARP4MENU) ? 5 : 7;
                        // set the text color to the global value;
                        // this might have changed via color drag'n'drop
                        GpiSetColor(hps, pGlobalSettings->lSBTextColor);
                            // the font is already preset by the static
                            // text control (phhhh...)

                        if (p2) {
                            // did we have tabs? if so, print text clipped to rect
                            rcl.xLeft   = 0;
                            rcl.yBottom = 0; // ptl1.y;
                            rcl.xRight  = lNextX-10; // 10 pels space
                            rcl.yTop    = rclBar.yTop;
                            GpiCharStringPosAt(hps, &ptl1, &rcl, CHS_CLIP,
                                    usLength, p1, NULL);
                        } else {
                            // no (more) tabs: just print
                            GpiMove(hps, &ptl1);
                            GpiCharString(hps, usLength, p1);
                        }

                        if (p2) { // "tabulator" was found: set next x pos
                            ptl1.x = lNextX;
                            p1 = p3+1;
                        }

                    } while (p2);       // go for next tabulator, if we had one

                }
                CATCH(excpt1)
                {
                    PSZ pszErr = "*** error painting status bar";
                    POINTL ptl = {5, 5};
                    GpiMove(hps, &ptl);
                    GpiCharString(hps, strlen(pszErr), pszErr);
                } END_CATCH;

                WinEndPaint(hps);

            break; }

            /*
             * STBM_PROHIBITBROADCASTING:
             *      this msg is sent to us to tell us we will
             *      be given new presentation parameters soon;
             *      we will set a flag to TRUE in order to
             *      prevent broadcasting the pres params again.
             *      We need this flag because there are two
             *      situations in which we are given
             *      WM_PRESPARAMCHANGED messages:
             *      1)   the user has dropped something on this
             *           status bar window; in this case, we're
             *           getting WM_PRESPARAMCHANGED directly,
             *           without STBM_PROHIBITBROADCASTING, which
             *           means that we should notify the Worker
             *           thread to broadcast WM_PRESPARAMCHANGED
             *           to all status bars;
             *      2)   a status bar other than this one had been
             *           dropped something upon; in this case, we'll
             *           get WM_PRESPARAMCHANGED also (from the
             *           Worker thread), but STBM_PROHIBITBROADCASTING
             *           beforehand so that we know we should not
             *           notify the Worker thread again.
             *      See WM_PRESPARAMCHANGED below.
             */

            case STBM_PROHIBITBROADCASTING: {
                psbd->fDontBroadcast = TRUE;
            break; }

            /*
             * WM_PRESPARAMCHANGED:
             *      if fonts or colors were dropped on the bar, update
             *      GlobalSettings and have this message broadcast to all
             *      other status bars on the system.
             *      This is also posted to us by the Worker thread
             *      after some OTHER status bar has been dropped
             *      fonts or colors upon; in this case, psbd->fDontBroadcast
             *      is TRUE (see above), and we will only update our
             *      own display and NOT broadcast, because this is
             *      already being done.
             */

            case WM_PRESPARAMCHANGED: {
                mrc = (MRESULT)(*pfnwpStatusBarOriginal)(hwndBar, msg, mp1, mp2);

                if (psbd->fDontBroadcast) {
                    // this flag has been set if it was not this status
                    // bar whose presparams have changed, but some other
                    // status bar; in this case, update only
                    psbd->fDontBroadcast = FALSE;
                    // update parent's frame controls (because font size
                    // might have changed)
                    WinSendMsg(WinQueryWindow(hwndBar, QW_PARENT), WM_UPDATEFRAME, MPNULL, MPNULL);
                    // update ourselves
                    WinPostMsg(hwndBar, STBM_UPDATESTATUSBAR, MPNULL, MPNULL);
                    // and quit
                    break;
                }

                // else: it was us that the presparam has been set for
                #ifdef DEBUG_STATUSBARS
                    _Pmpf(( "WM_PRESPARAMCHANGED: %lX", mp1 ));
                #endif

                // now check what has changed
                switch ((ULONG)mp1) {
                    case PP_FONTNAMESIZE: {
                        ULONG attrFound;
                        // CHAR  szDummy[200];
                        CHAR  szNewFont[100];
                        WinQueryPresParam(hwndBar,
                                    PP_FONTNAMESIZE,
                                    0,
                                    &attrFound,
                                    (ULONG)sizeof(szNewFont),
                                    (PVOID)&szNewFont,
                                    0);
                        cmnSetStatusBarSetting(SBS_STATUSBARFONT,
                                szNewFont);
                        // update parent's frame controls (because font size
                        // might have changed)
                        WinSendMsg(WinQueryWindow(hwndBar, QW_PARENT),
                                WM_UPDATEFRAME, MPNULL, MPNULL);
                    break; }

                    case PP_FOREGROUNDCOLOR:
                    case PP_BACKGROUNDCOLOR: {
                        ULONG ul = 0, attrFound = 0;
                        WinQueryPresParam(hwndBar,
                                    (ULONG)mp1,
                                    0,
                                    &attrFound,
                                    (ULONG)sizeof(ul),
                                    (PVOID)&ul,
                                    0);
                        if ((ULONG)mp1 == PP_FOREGROUNDCOLOR)
                            pGlobalSettings->lSBTextColor = ul;
                        else
                            pGlobalSettings->lSBBgndColor = ul;
                        PrfWriteProfileData(HINI_USERPROFILE, INIAPP_XFOLDER, INIKEY_GLOBALSETTINGS,
                                    pGlobalSettings, sizeof(GLOBALSETTINGS));
                        WinPostMsg(hwndBar, STBM_UPDATESTATUSBAR, MPNULL, MPNULL);
                    break; }
                }

                // finally, broadcast this message to all other status bars;
                // this is handled by the Worker thread
                xthrPostWorkerMsg(WOM_UPDATEALLSTATUSBARS,
                        (MPARAM)2,      // update display
                        MPNULL);
            break; }

            /*
             * WM_BUTTON1CLICK:
             *      if button1 is clicked on the status
             *      bar, we should reset the focus to the
             *      container.
             */

            case WM_BUTTON1CLICK:
                // mrc = (MRESULT)(*pfnwpStatusBarOriginal)(hwndBar, msg, mp1, mp2);
                WinSetFocus(HWND_DESKTOP, psbd->psli->hwndCnr);
            break;

            /*
             * WM_BUTTON1DBLCLK:
             *      on double-click on the status bar,
             *      open the folder's settings notebook.
             *      If DEBUG_RESTOREDATA is on (common.h),
             *      dump some internal folder data to
             *      PMPRINTF instead.
             */

            case WM_BUTTON1DBLCLK: {

                /* TRY_LOUD(excpt1) {
                    CRASH;
                }
                CATCH(excpt1) {
                } END_CATCH; */

                #ifdef DEBUG_RESTOREDATA
                    XFolderData     *somThis = XFolderGetData(psbd->somSelf);
                    _Pmpf(("Dump for %s", _wpQueryTitle(psbd->somSelf) ));
                    _Pmpf(("FDRLONGARRAY: "));
                    dbgDump((PBYTE)_pFldrLongArray, _cbFldrLongArray, 3);
                #else
                    WinPostMsg(psbd->psli->hwndFrame,
                            WM_COMMAND,
                            (MPARAM)WPMENUID_PROPERTIES,
                            MPFROM2SHORT(CMDSRC_MENU,
                                    FALSE) );     // results from keyboard operation
                #endif
            break; }

            /*
             * WM_CONTEXTMENU:
             *      if the user right-clicks on status bar,
             *      display folder's context menu. Parameters:
             *      mp1:
             *          POINTS mp1      pointer position
             *      mp2:
             *          USHORT usReserved  should be 0
             *          USHORT fPointer    if TRUE: results from keyboard
             */

            case WM_CONTEXTMENU: {
                if (psbd->psli) {
                    POINTL ptl;
                    WinSetFocus(HWND_DESKTOP, psbd->psli->hwndCnr);
                    // give the cnr source emphasis
                    // (needed for some wpSelectingMenu funcs)
                    WinSendMsg(psbd->psli->hwndCnr,
                                CM_SETRECORDEMPHASIS,
                                (MPARAM)NULL,   // undocumented: if precc == NULL,
                                                // the whole cnr is given emphasis
                                MPFROM2SHORT(TRUE,  // set emphasis
                                        CRA_SOURCE));

                    ptl.x = SHORT1FROMMP(mp1)-30;
                    ptl.y = SHORT2FROMMP(mp1);
                    _wpDisplayMenu(psbd->somSelf,
                                WinQueryWindow(hwndBar, QW_PARENT), // owner: folder frame
                                hwndBar,                            // parent: status bar
                                &ptl,
                                MENU_OBJECTPOPUP,
                                0);

                    // set flag so that the fnwpSubclassedFolderFrame
                    // can remove the cnr source emphasis after the
                    // menu has been terminated
                    psbd->psli->fRemoveSrcEmphasis = TRUE;
                }
            break; }

            /*
             * WM_DESTROY:
             *      call original wnd proc, then free psbd
             */

            case WM_DESTROY:
                mrc = (MRESULT)(*pfnwpStatusBarOriginal)(hwndBar, msg, mp1, mp2);
                WinSetWindowULong(hwndBar, QWL_USER, 0);
                free(psbd);
            break;

            default:
                mrc = (MRESULT)(*pfnwpStatusBarOriginal)(hwndBar, msg, mp1, mp2);
            break;
        } // end switch
    } // end if (psbd)
    /* else // error: call default
        mrc = (MRESULT)(*pfnwpStatusBarOriginal)(hwndBar, msg, mp1, mp2); */

    return (mrc);
}

/*
 * CalcFrameRect:
 *      this gets called from fnwpSubclassedFolderFrame
 *      when WM_CALCFRAMERECT is received. This implements
 *      folder status bars.
 */

VOID CalcFrameRect(MPARAM mp1, MPARAM mp2)
{
    PRECTL prclPassed = (PRECTL)mp1;
    ULONG ulStatusBarHeight = cmnQueryStatusBarHeight();

    if (SHORT1FROMMP(mp2))
        //     TRUE:  Frame rectangle provided, calculate client
        //     FALSE: Client area rectangle provided, calculate frame
    {
        //  TRUE: calculate the rectl of the client;
        //  call default window procedure to subtract child frame
        //  controls from the rectangle's height
        LONG lClientHeight;

        //  position the static text frame extension below the client
        lClientHeight = prclPassed->yTop - prclPassed->yBottom;
        if ( ulStatusBarHeight  > lClientHeight  )
        {
            // extension is taller than client, so set client height to 0
            prclPassed->yTop = prclPassed->yBottom;
        }
        else
        {
            //  set the origin of the client and shrink it based upon the
            //  static text control's height
            prclPassed->yBottom += ulStatusBarHeight;
            prclPassed->yTop -= ulStatusBarHeight;
        }
    }
    else
    {
        //  FALSE: calculate the rectl of the frame;
        //  call default window procedure to subtract child frame
        //  controls from the rectangle's height;
        //  set the origin of the frame and increase it based upon the
        //  static text control's height
        prclPassed->yBottom -= ulStatusBarHeight;
        prclPassed->yTop += ulStatusBarHeight;
    }
}

/*
 * FormatFrame:
 *      this gets called from fnwpSubclassedFolderFrame
 *      when WM_FORMATFRAME is received. This implements
 *      folder status bars.
 */

VOID FormatFrame(PSUBCLASSEDLISTITEM psli, // in: frame information
                 MPARAM mp1,            // in: mp1 from WM_FORMATFRAME
                                        //     (points to SWP array)
                 ULONG *pulCount)       // in/out: frame control count
                                        //         from default wnd proc
{
    // access the SWP array that is passed to us
    // and search all the controls for the container child window,
    // which for folders always has the ID 0x8008
    ULONG       ul;
    PSWP        swpArr = (PSWP)mp1;
    CNRINFO     CnrInfo;

    for (ul = 0; ul < *pulCount; ul++)
    {
        if (WinQueryWindowUShort( swpArr[ul].hwnd, QWS_ID ) == 0x8008 )
                                                         // FID_CLIENT
        {
            // container found: reduce size of container by
            // status bar height
            POINTL          ptlBorderSizes;
            // XFolderData     *somThis = XFolderGetData(psli->somSelf);
            ULONG ulStatusBarHeight = cmnQueryStatusBarHeight();
            WinSendMsg(psli->hwndFrame, WM_QUERYBORDERSIZE,
                    (MPARAM)&ptlBorderSizes, MPNULL);

            // first initialize the _new_ SWP for the status bar.
            // Since the SWP array for the std frame controls is
            // zero-based, and the standard frame controls occupy
            // indices 0 thru ulCount-1 (where ulCount is the total
            // count), we use ulCount for our static text control.
            swpArr[*pulCount].fl = SWP_MOVE | SWP_SIZE | SWP_NOADJUST | SWP_ZORDER;
            swpArr[*pulCount].x  = ptlBorderSizes.x;
            swpArr[*pulCount].y  = ptlBorderSizes.y;
            swpArr[*pulCount].cx = swpArr[ul].cx;
            swpArr[*pulCount].cy = ulStatusBarHeight;
            swpArr[*pulCount].hwndInsertBehind = HWND_BOTTOM; // HWND_TOP;
            swpArr[*pulCount].hwnd = psli->hwndStatusBar;

            // adjust the origin and height of the container to
            // accomodate our static text control
            swpArr[ul].y  += swpArr[*pulCount].cy;
            swpArr[ul].cy -= swpArr[*pulCount].cy;

            // now we need to adjust the workspace origin of the cnr
            // accordingly, or otherwise the folder icons will appear
            // outside the visible cnr workspace and scroll bars will
            // show up.
            // We only do this the first time we're arriving here
            // (which should be before the WPS is populating the folder);
            // psli->fNeedCnrScroll has been initially set to TRUE
            // by xfShowStatusBar
            if (psli->fNeedCnrScroll)
            {
                winhQueryCnrInfo(swpArr[ul].hwnd, CnrInfo);
                #ifdef DEBUG_STATUSBARS
                    _Pmpf(( "Old CnrInfo.ptlOrigin.y: %lX", CnrInfo.ptlOrigin.y ));
                #endif
                if (    ((LONG)CnrInfo.ptlOrigin.y >= (LONG)ulStatusBarHeight)
                   )
                {
                    CnrInfo.ptlOrigin.y -= ulStatusBarHeight;
                    #ifdef DEBUG_STATUSBARS
                        _Pmpf(( "New CnrInfo.ptlOrigin.y: %lX", CnrInfo.ptlOrigin.y ));
                    #endif
                    WinSendMsg(swpArr[ul].hwnd, CM_SETCNRINFO,
                            (MPARAM)&CnrInfo, (MPARAM)CMA_PTLORIGIN);
                    // set flag to FALSE to prevent a second adjustment
                    psli->fNeedCnrScroll = FALSE;
                }
            } // end if (psli->fNeedCnrScroll)
        } // end if WinQueryWindowUShort
    } // end for (ul = 0; ul < ulCount; ul++)
}

/*
 * InitMenu:
 *      this gets called from fnwpSubclassedFolderFrame
 *      when WM_INITMENU is received.
 *      This is needed for various menu features:
 *      1)  for folder content menus, because these
 *          are initially empty and only filled if the user
 *          clicks on them.
 *          We will query a bunch of data first, which we need
 *          later for drawing our items, and then call
 *          mnuFillContentSubmenu, which populates the folder
 *          and fills the menu with the items therein;
 *      2)  for the menu system sounds;
 *      3)  for manipulating Warp 4 folder menu _bars_.
 *      WM_INITMENU parameters:
 *          SHORT mp1   menu item id
 *          HWND  mp2   menu window handle
 *      Returns: NULL always.
 */

// original wnd proc for folder content menus,
// which we must subclass
PFNWP   pfnwpFolderContentMenuOriginal = NULL;

VOID InitMenu(PSUBCLASSEDLISTITEM psli, // in: frame information
              MPARAM mp1,
              MPARAM mp2)   // in: mp1, mp2 from WM_INITMENU
{
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    XFolderData     *somThis = XFolderGetData(psli->somSelf);
    PTHREADGLOBALS pThreadGlobals = xthrQueryGlobals();
    ULONG           *pulWorkplaceFunc2 = &(pThreadGlobals->ulWorkplaceFunc2);

    #ifdef DEBUG_MENUS
        _Pmpf(( "WM_INITMENU: mp1 = %lX, mp2 = %lX", mp1, mp2 ));
    #endif

    // store the container window handle in instance
    // data for wpPopupMenu workaround (see wpPopupMenu)
    _hwndCnrSaved = psli->hwndCnr;

    // play system sound
    if (    ((USHORT)mp1 <  0x8000) // avoid system menu
         || ((USHORT)mp1 == 0x8020) // include context menu
       )
        xthrPlaySystemSound(MMSOUND_XFLD_CTXTOPEN);

    *pulWorkplaceFunc2 = 200;
    // find out whether the menu of which we are notified
    // is a folder content menu; if so (and it is not filled
    // yet), the first menu item is ID_XFMI_OFS_DUMMY
    if ((ULONG)WinSendMsg((HWND)mp2, MM_ITEMIDFROMPOSITION, (MPARAM)0, MPNULL)
        == (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_DUMMY))
    {
        // okay, let's go
        if (pGlobalSettings->FCShowIcons)
        {
            #ifdef DEBUG_MENUS
                _Pmpf(( "  preparing owner draw"));
            #endif
            // show folder content icons ON:
            mnuPrepareOwnerDraw(mp1, mp2);
        }

        *pulWorkplaceFunc2 = 200;
        mnuFillContentSubmenu(
                        (SHORT)mp1, (HWND)mp2,
                        // this func subclasses the folder content
                        // menu wnd and stores the result here
                        &pfnwpFolderContentMenuOriginal);
    } else {
        // no folder content menu:

        // on Warp 4, check if the folder has a menu bar
        if (doshIsWarp4()) {

            #ifdef DEBUG_MENUS
                _Pmpf(( "  checking for menu bar"));
            #endif

            if (SHORT1FROMMP(mp1) == 0x8005) {
                // seems to be some WPS menu item;
                // since the WPS seems to be using this
                // same ID for all the menu bar submenus,
                // we need to check the last selected
                // menu item, which was stored in the psli
                // structure by WM_MENUSELECT (below).
                PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();

                switch (psli->usLastSelMenuItem) {
                    case 0x2D3:
                        // "Help" submenu: add XFolder product info
                        #ifdef DEBUG_MENUS
                            _Pmpf(("  'Help' menu found"));
                        #endif
                        winhInsertMenuSeparator((HWND)mp2, MIT_END,
                                (pGlobalSettings->VarMenuOffset
                                        + ID_XFMI_OFS_SEPARATOR));
                        winhInsertMenuItem((HWND)mp2, MIT_END,
                                (pGlobalSettings->VarMenuOffset
                                        + ID_XFMI_OFS_PRODINFO),
                                pNLSStrings->pszProductInfo,
                                MIS_TEXT, 0);
                    break;

                    case 0x2D0: { // "Edit" submenu
                        // find position of "Deselect all" item
                        SHORT sPos = (SHORT)WinSendMsg((HWND)mp2,
                                    MM_ITEMPOSITIONFROMID,
                                    MPFROM2SHORT(0x73, FALSE),
                                    MPNULL);
                        #ifdef DEBUG_MENUS
                            _Pmpf(("  'Edit' menu found"));
                        #endif
                        // insert "Select by name" after that item
                        winhInsertMenuItem((HWND)mp2,
                                sPos+1,
                                (pGlobalSettings->VarMenuOffset
                                        + ID_XFMI_OFS_SELECTSOME),
                                pNLSStrings->pszSelectSome,
                                MIS_TEXT, 0);
                    break; }

                    case 0x2D1: { // "View" submenu
                        CNRINFO             CnrInfo;
                        #ifdef DEBUG_MENUS
                            _Pmpf(("  'View' menu found"));
                        #endif
                        // modify the "Sort" menu, as we would
                        // do it for context menus also
                        mnuModifySortMenu((HWND)mp2,
                                XFolderGetData(psli->somSelf),
                                pGlobalSettings,
                                pNLSStrings);
                        winhQueryCnrInfo(psli->hwndCnr, CnrInfo);
                        // and now insert the "folder view" items
                        winhInsertMenuSeparator((HWND)mp2,
                                MIT_END,
                                (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));
                        mnuInsertFldrViewItems(psli->somSelf,
                                        (HWND)mp2,
                                        psli->hwndCnr,
                                        FALSE,
                                        &CnrInfo);
                    break; }
                }
            }
        }
    }
}

/*
 * MenuSelect:
 *      this gets called from fnwpSubclassedFolderFrame
 *      when WM_MENUSELECT is received.
 *      We need this for three reasons:
 *      1) we will play a system sound, if desired;
 *      2) we need to swallow this for very large folder
 *         content menus, because for some reason, PM will
 *         select a random menu item after we have moved
 *         these menus on the screen;
 *      3) we can intercept certain menu items so that
 *         these don't get passed to wpMenuItemSelected.
 *         This is needed for menu items such as those in
 *         the "Sort" menu so that the menu is not dismissed
 *         after selection.
 *      WM_MENUSELECT parameters:
 *          mp1 USHORT usItem - selected menu item
 *              USHORT usPostCommand - TRUE: if we return TRUE,
 *                  a message will be posted to the owner.
 *          mp2 HWND - menu control wnd handle
 *      If we set pfDismiss to TRUE, wpMenuItemSelected will be
 *      called, and the menu will be dismissed.
 *      Otherwise the message will be swallowed.
 *      We return TRUE if the menu item has been handled here.
 *      Otherwise the default wnd proc will be used.
 */

// flags for fnwpSubclassedFolderFrame;
// these are set by fnwpFolderContentMenu.
// We can afford using global variables here
// because there will never be more than one
// open window at a time.
BOOL    fFldrContentMenuMoved = FALSE,
        fFldrContentMenuButtonDown = FALSE;

BOOL MenuSelect(PSUBCLASSEDLISTITEM psli, // in: frame information
                MPARAM mp1, MPARAM mp2,   // in: mp1, mp2 from WM_MENUSELECT
                BOOL *pfDismiss)          // out: dismissal flag
{
    BOOL fHandled = FALSE;

    // return value for WM_MENUSELECT;
    // TRUE means dismiss menu
    psli->usLastSelMenuItem = SHORT1FROMMP(mp1);

    // check if we have moved a folder content menu
    // (this flag is set by fnwpFolderContentMenu); for
    // some reason, PM gets confused with the menu items
    // then and automatically tries to select the menu
    // item under the mouse, so we swallow this one
    // message if (a) the folder content menu has been
    // moved by fnwpFolderContentMenu and (b) no mouse
    // button is pressed. These flags are all set by
    // fnwpFolderContentMenu.
    if (fFldrContentMenuMoved)
    {
        #ifdef DEBUG_MENUS
            _Pmpf(( "  FCMenuMoved set!"));
        #endif
        if (!fFldrContentMenuButtonDown) {
            // mouse was not pressed: swallow this
            // menu selection
            *pfDismiss = FALSE;
        } else {
            // mouse was pressed: allow selection
            // and unset flags
            *pfDismiss = TRUE;
            fFldrContentMenuButtonDown = FALSE;
            fFldrContentMenuMoved = FALSE;
        }
        fHandled = TRUE;
    } else {
        if (    (SHORT2FROMMP(mp1) == TRUE)
            && (    ((USHORT)mp1 <  0x8000) // avoid system menu
                 || ((USHORT)mp1 == 0x8020) // include context menu
               )
           )
        {
            HWND hwndCnr = xwpsQueryCnrFromFrame(psli->hwndFrame);

            // play system sound
            xthrPlaySystemSound(MMSOUND_XFLD_CTXTSELECT);

            // Now check if we have a menu item which we don't
            // want to see dismissed.

            if (hwndCnr) {
                // first find out what kind of objects we have here

                ULONG ulSelection = 0;
                WPObject *pObject1 =
                     mnuQuerySelectedObject(psli->somSelf,
                            hwndCnr,
                            &ulSelection);

                WPObject *pObject = pObject1;

                #ifdef DEBUG_MENUS
                    _Pmpf(( "  Object selections: %d", ulSelection));
                #endif

                // dereference shadows
                if (pObject)
                    if (_somIsA(pObject, _WPShadow))
                        pObject = _wpQueryShadowedObject(pObject, TRUE);

                // now call the functions in menus.c for this,
                // depending on the class of the object for which
                // the menu was opened
                if (pObject) {
                    if (_somIsA(pObject, _WPFileSystem))
                    {
                        fHandled = mnuFileSystemSelectingMenuItem(
                                pObject1,   // note that we're passing
                                            // pObject1 instead of pObject;
                                            // pObject1 might be a shadow!
                                SHORT1FROMMP(mp1),       // usItem
                                (BOOL)SHORT2FROMMP(mp1), // fPostCommand
                                (HWND)mp2,               // hwndMenu
                                hwndCnr,
                                ulSelection,             // SEL_* flags
                                pfDismiss);              // dismiss-menu flag
                    }

                    if (    (!fHandled)
                         && (_somIsA(pObject, _WPFolder))
                       )
                    {
                        fHandled = mnuFolderSelectingMenuItem(pObject,
                                SHORT1FROMMP(mp1),       // usItem
                                (BOOL)SHORT2FROMMP(mp1), // fPostCommand
                                (HWND)mp2,               // hwndMenu
                                hwndCnr,
                                ulSelection,             // SEL_* flags
                                pfDismiss);              // dismiss-menu flag
                    }

                    if ( (fHandled) && (!(*pfDismiss)) )
                    {
                        // menu not to be dismissed: set the flag
                        // which will remove cnr source
                        // emphasis when the menu is dismissed
                        // later (WM_ENDMENU msg here)
                        psli->fRemoveSrcEmphasis = TRUE;
                    }
                }
            }
        }
    }
    return (fHandled);
}

/*
 *@@ fnwpSubclassedFolderFrame:
 *          New window proc for subclassed folder frame windows.
 *          Folder frame windows are subclassed in XFolder::wpOpen
 *          (or XFldDisk::wpOpen for Disk views) with the address
 *          of this window procedure.
 *
 *          This is maybe the most central part of XFolder. Since
 *          most WPS methods are really just reacting to messages
 *          in the default WPS frame window proc, but for some
 *          features methods are just not sufficient, basically we
 *          simulate what the WPS does here by intercepting _lots_
 *          of messages before the WPS gets them.
 *
 *          Things we do in this proc:
 *          --  frame control manipulation for status bars
 *          --  Warp 4 folder menu bar manipulation (WM_INITMENU)
 *          --  handling of certain menu items w/out dismissing
 *              the menu; this calls functions in menus.c
 *          --  menu owner draw (folder content menus w/ icons);
 *              this calls functions in menus.c also
 *          --  container control messages: tree view auto-scroll,
 *              updating status bars etc.
 *          --  playing the new system sounds for menus and such
 *              by calling xthrPlaySystemSound.
 *
 *          Note that this function calls lots of "external" functions
 *          spread across all over the XFolder code. This reduces the
 *          code size of this function, which gets called very often,
 *          to avoid excessive use of the processor caches.
 */

MRESULT EXPENTRY fnwpSubclassedFolderFrame(HWND hwndFrame, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    PTHREADGLOBALS pThreadGlobals = xthrQueryGlobals();
    ULONG           *pulWorkplaceFunc2 = &(pThreadGlobals->ulWorkplaceFunc2);

    PSUBCLASSEDLISTITEM psli = NULL;
    PFNWP           pfnwpOriginal = NULL;

    XFolder         *somSelf;
    MRESULT         mrc = MRFALSE;
    BOOL            fCallDefault = FALSE;


    *pulWorkplaceFunc2 = 100;

    TRY_LOUD(excpt1)   // install "loud" exception handler (except.h)
    {
        // find the original wnd proc in the
        // global linked list, so we can pass messages
        // on to it
        *pulWorkplaceFunc2 = 110;
        psli = cmnQueryPSLI(hwndFrame);
        if (psli) {
            pfnwpOriginal = psli->pfnwpOriginal;
            somSelf = psli->somSelf;
        }

        if (pfnwpOriginal)
        {
            *pulWorkplaceFunc2 = 120;
            switch(msg)
            {
                /* *************************
                 *                         *
                 * Status bar:             *
                 *                         *
                 **************************/

                case WM_QUERYFRAMECTLCOUNT: {
                    // query the standard frame controls count
                    ULONG ulrc = (ULONG)((*pfnwpOriginal)(hwndFrame, msg, mp1, mp2));

                    // if we have a status bar, increment the count
                    if (psli->hwndStatusBar)
                        ulrc++;

                    mrc = (MPARAM)ulrc;
                break; }

                /*
                 * WM_FORMATFRAME:
                 *    this message is sent to a frame window to calculate the sizes
                 *    and positions of all of the frame controls and the client window:
                 *          mp1     PSWP    pswp        structure array
                 *          mp2     PRECTL  pprectl     pointer to client window
                 *                                      rectangle
                 *          returns USHORT  ccount      count of the number of SWP
                 *                                      arrays returned
                 */

                case WM_FORMATFRAME:
                {
                    //  query the number of standard frame controls
                    ULONG ulCount = (ULONG)((*pfnwpOriginal)(hwndFrame, msg, mp1, mp2));

                    #ifdef DEBUG_STATUSBARS
                        _Pmpf(( "WM_FORMATFRAME ulCount = %d", ulCount ));
                    #endif

                    if (psli->hwndStatusBar)
                    {
                        // we have a status bar:
                        // format the frame
                        FormatFrame(psli, mp1, &ulCount);

                        // increment the number of frame controls
                        // to include our status bar
                        mrc = (MRESULT)(ulCount + 1);
                    } // end if (psli->hwndStatusBar)
                    else
                        mrc = (MRESULT)ulCount;
                break; }

                /*
                 * WM_CALCFRAMERECT:
                 *     this message occurs when an application uses the
                 *     WinCalcFrameRect function. Parameters:
                 *          mp1     PRECTL  pRect      rectangle structure
                 *          mp2     USHORT  usFrame    frame indicator
                 *          returns BOOL    rc         rectangle-calculated indicator
                 */

                case WM_CALCFRAMERECT:
                {
                    mrc = (*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);

                    if (psli->hwndStatusBar)
                        // we have a status bar: calculate its rectangle
                        CalcFrameRect(mp1, mp2);
                break; }

                /* *************************
                 *                         *
                 * Menu items:             *
                 *                         *
                 **************************/

                /*
                 * WM_INITMENU:
                 *      this message is sent to a frame whenever a menu
                 *      is about to be displayed. This is needed for
                 *      various menu features; see InitMenu() above.
                 */

                case WM_INITMENU: {
                    // always call the default, in case someone else
                    // is subclassing folders (ObjectDesktop?!?)
                    mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);

                    InitMenu(psli, mp1, mp2);
                break; }

                /*
                 * WM_MENUSELECT:
                 *      this is SENT to a menu owner by the menu control to
                 *      determine what to do right after a menu item has been
                 *      selected. If we return TRUE, the menu will be dismissed.
                 *      See MenuSelect() above.
                 */

                case WM_MENUSELECT: {
                    BOOL fDismiss = TRUE;

                    #ifdef DEBUG_MENUS
                        _Pmpf(( "WM_MENUSELECT: mp1 = %lX/%lX, mp2 = %lX",
                                SHORT1FROMMP(mp1),
                                SHORT2FROMMP(mp1),
                                mp2 ));
                    #endif

                    // always call the default, in case someone else
                    // is subclassing folders (ObjectDesktop?!?)
                    mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);

                    // now handle our stuff; this might modify mrc to
                    // have the menu stay on the screen
                    if (MenuSelect(psli, mp1, mp2, &fDismiss))
                        mrc = (MRESULT)fDismiss;
                break; }

                /*
                 * WM_MENUEND:
                 *      this message occurs when a menu control is about to
                 *      terminate. We need to remove cnr source emphasis
                 *      if the user has requested a context menu from a
                 *      status bar.
                 */

                case WM_MENUEND: {
                    #ifdef DEBUG_MENUS
                        _Pmpf(( "WM_MENUEND: mp1 = %lX, mp2 = %lX",
                                mp1, mp2 ));
                        /* _Pmpf(( "  fFolderContentWindowPosChanged: %d",
                                fFolderContentWindowPosChanged));
                        _Pmpf(( "  fFolderContentButtonDown: %d",
                                fFolderContentButtonDown)); */
                    #endif
                    // menu opened from status bar?
                    if (psli->fRemoveSrcEmphasis) {
                        // if so, remove cnr source emphasis
                        WinSendMsg(psli->hwndCnr,
                                    CM_SETRECORDEMPHASIS,
                                    (MPARAM)NULL,   // undocumented: if precc == NULL,
                                                    // the whole cnr is given emphasis
                                    MPFROM2SHORT(FALSE,  // remove emphasis
                                            CRA_SOURCE));
                        // and make sure the container has the
                        // focus
                        WinSetFocus(HWND_DESKTOP, psli->hwndCnr);
                        // reset flag for next context menu
                        psli->fRemoveSrcEmphasis = FALSE;
                    }

                    // unset flag for WM_MENUSELECT above
                    fFldrContentMenuMoved = FALSE;

                    mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                break; }

                /*
                 * WM_MEASUREITEM:
                 *      this msg is sent only once per owner-draw item when
                 *      PM needs to know its size. This gets sent to us for
                 *      items in folder content menus; the height of our items
                 *      will be the same as with non-owner-draw ones, but
                 *      we need to calculate the width according to the item
                 *      text. Return value: check mnuMeasureItem (menus.c)
                 */

                case WM_MEASUREITEM: {
                    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
                    *pulWorkplaceFunc2 = 300;
                    if ( (SHORT)mp1 > (pGlobalSettings->VarMenuOffset+ID_XFMI_OFS_VARIABLE) )
                    {
                        // call the measure-item func in menus.c
                        mrc = mnuMeasureItem((POWNERITEM)mp2, pGlobalSettings);
                    }
                    else
                        // none of our items: pass to original wnd proc
                        mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                break; }

                /*
                 * WM_DRAWITEM:
                 *      this msg is sent for each item every time it
                 *      needs to be redrawn. This gets sent to us for
                 *      items in folder content menus.
                 */

                case WM_DRAWITEM: {
                    PGLOBALSETTINGS pGlobalSettings =
                            cmnQueryGlobalSettings();
                    *pulWorkplaceFunc2 = 400;
                    if ( (SHORT)mp1 > (pGlobalSettings->VarMenuOffset+ID_XFMI_OFS_VARIABLE) )
                    {
                        // variable menu item: this must be a folder-content
                        // menu item, because for others no WM_DRAWITEM is sent
                        // (menus.c)
                        if (mnuDrawItem(pGlobalSettings,
                                    mp1, mp2))
                            mrc = (MRESULT)TRUE;
                        else // error occured:
                            mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                    }
                    else
                        mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                    *pulWorkplaceFunc2 = 499;
                break; }

                /* *************************
                 *                         *
                 * Miscellaneae:           *
                 *                         *
                 **************************/

                /*
                 * WM_CHAR:
                 *      this is intercepted to provide folder hotkeys
                 */

                case WM_CHAR: {
                    XFolderData         *somThis = XFolderGetData(somSelf);
                    PGLOBALSETTINGS pGlobalSettings =
                                        cmnQueryGlobalSettings();

                    if  (   (_bAcceleratorsAllowed == 1)
                        || ((_bAcceleratorsAllowed == 2) && (pGlobalSettings->Accelerators))
                        )
                    {
                        if (cmnProcessFldrHotkey(hwndFrame, mp1, mp2)) {
                            mrc = (MRESULT)TRUE;
                            break;
                        }
                    }

                    mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                break; }

                /*
                 * WM_CONTROL:
                 *      this is intercepted to check for container
                 *      notifications we might be interested in
                 */

                case WM_CONTROL: {
                    *pulWorkplaceFunc2 = 950;
                    if (SHORT1FROMMP(mp1) /* id */ == 0x8008) // container!!
                    {
                        #ifdef DEBUG_CNRCNTRL
                            CHAR szTemp2[30];
                            sprintf(szTemp2, "unknown: %d", SHORT2FROMMP(mp1));
                            _Pmpf(("Cnr cntrl msg: %s, mp2: %lX",
                                (SHORT2FROMMP(mp1) == CN_BEGINEDIT) ? "CN_BEGINEDIT"
                                    : (SHORT2FROMMP(mp1) == CN_COLLAPSETREE) ? "CN_COLLAPSETREE"
                                    : (SHORT2FROMMP(mp1) == CN_CONTEXTMENU) ? "CN_CONTEXTMENU"
                                    : (SHORT2FROMMP(mp1) == CN_DRAGAFTER) ? "CN_DRAGAFTER"
                                    : (SHORT2FROMMP(mp1) == CN_DRAGLEAVE) ? "CN_DRAGLEAVE"
                                    : (SHORT2FROMMP(mp1) == CN_DRAGOVER) ? "CN_DRAGOVER"
                                    : (SHORT2FROMMP(mp1) == CN_DROP) ? "CN_DROP"
                                    : (SHORT2FROMMP(mp1) == CN_DROPNOTIFY) ? "CN_DROPNOTIFY"
                                    : (SHORT2FROMMP(mp1) == CN_DROPHELP) ? "CN_DROPHELP"
                                    : (SHORT2FROMMP(mp1) == CN_EMPHASIS) ? "CN_EMPHASIS"
                                    : (SHORT2FROMMP(mp1) == CN_ENDEDIT) ? "CN_ENDEDIT"
                                    : (SHORT2FROMMP(mp1) == CN_ENTER) ? "CN_ENTER"
                                    : (SHORT2FROMMP(mp1) == CN_EXPANDTREE) ? "CN_EXPANDTREE"
                                    : (SHORT2FROMMP(mp1) == CN_HELP) ? "CN_HELP"
                                    : (SHORT2FROMMP(mp1) == CN_INITDRAG) ? "CN_INITDRAG"
                                    : (SHORT2FROMMP(mp1) == CN_KILLFOCUS) ? "CN_KILLFOCUS"
                                    : (SHORT2FROMMP(mp1) == CN_PICKUP) ? "CN_PICKUP"
                                    : (SHORT2FROMMP(mp1) == CN_QUERYDELTA) ? "CN_QUERYDELTA"
                                    : (SHORT2FROMMP(mp1) == CN_REALLOCPSZ) ? "CN_REALLOCPSZ"
                                    : (SHORT2FROMMP(mp1) == CN_SCROLL) ? "CN_SCROLL"
                                    : (SHORT2FROMMP(mp1) == CN_SETFOCUS) ? "CN_SETFOCUS"
                                    : szTemp2,
                                mp2));
                        #endif

                        switch (SHORT2FROMMP(mp1)) {

                            /*
                             * CN_BEGINEDIT:
                             *      this is sent by the container control
                             *      when direct text editing is about to
                             *      begin, that is, when the user alt-clicks
                             *      on an object title.
                             *      We'll select the file stem of the object.
                             */

                            /* case CN_BEGINEDIT: {
                                PCNREDITDATA pced = (PCNREDITDATA)mp2;
                                mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                                if (pced) {
                                    PMINIRECORDCORE pmrc = (PMINIRECORDCORE)pced->pRecord;
                                    if (pmrc) {
                                        // editing WPS record core, not title etc.:
                                        // get the window ID of the MLE control
                                        // in the cnr window
                                        HWND hwndMLE = WinWindowFromID(pced->hwndCnr,
                                                            CID_MLE);
                                        if (hwndMLE) {
                                            ULONG cbText = WinQueryWindowTextLength(
                                                                    hwndMLE)+1;
                                            PSZ pszText = malloc(cbText);
                                            _Pmpf(("textlen: %d", cbText));
                                            if (WinQueryWindowText(hwndMLE,
                                                                   cbText,
                                                                   pszText))
                                            {
                                                PSZ pszLastDot = strrchr(pszText, '.');
                                                _Pmpf(("text: %s", pszText));
                                                WinSendMsg(hwndMLE,
                                                        EM_SETSEL,
                                                        MPFROM2SHORT(
                                                            // first char: 0
                                                            0,
                                                            // last char:
                                                            (pszLastDot)
                                                                ? (pszLastDot-pszText)
                                                                : 10000
                                                        ), MPNULL);
                                            }
                                            free(pszText);
                                        }
                                    }
                                }
                            break; } */

                            /*
                             * CN_ENTER:
                             *      double-click or enter key:
                             *      play sound
                             */

                            case CN_ENTER:
                                *pulWorkplaceFunc2 = 951;
                                xthrPlaySystemSound(MMSOUND_XFLD_CNRDBLCLK);
                                mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                            break;

                            /*
                             * CN_EMPHASIS:
                             *      selection changed:
                             *      update status bar
                             */

                            case CN_EMPHASIS:
                                *pulWorkplaceFunc2 = 952;
                                mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                                if (psli->hwndStatusBar) {
                                    #ifdef DEBUG_STATUSBARS
                                        _Pmpf(( "CN_EMPHASIS: posting PM_UPDATESTATUSBAR to hwnd %lX", psli->hwndStatusBar ));
                                    #endif
                                    WinPostMsg(psli->hwndStatusBar,
                                             STBM_UPDATESTATUSBAR,
                                             MPNULL,
                                             MPNULL);
                                }
                            break;

                            /*
                             * CN_EXPANDTREE:
                             *      tree view has been expanded:
                             *      do cnr auto-scroll
                             */

                            case CN_EXPANDTREE: {
                                PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
                                *pulWorkplaceFunc2 = 953;
                                mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                                if (pGlobalSettings->TreeViewAutoScroll) {
                                    xthrPostWorkerMsg(WOM_TREEVIEWAUTOSCROLL,
                                            (MPARAM)hwndFrame,
                                            mp2); // PMINIRECORDCORE
                                }
                            break; }

                            default:
                                *pulWorkplaceFunc2 = 954;
                                fCallDefault = TRUE;
                            break;
                        }
                    }
                break; }

                /*
                 * WM_CLOSE:
                 *      upon this, we need to clean up our linked list
                 *      of subclassed windows
                 */

                case WM_CLOSE: {
                    // first remove the status bar to store the correct wnd size
                    // xfShowStatusBar(hwndFrame, psli->somSelf, psli, FALSE);
                    // then do the default stuff
                    *pulWorkplaceFunc2 = 990;
                    mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
                    // upon closing the window, undo the subclassing, in case
                    // some other message still comes in
                    WinSubclassWindow(hwndFrame, pfnwpOriginal);
                    // destroy the supplementary object window for this folder
                    // frame window
                    WinDestroyWindow(psli->hwndSupplObject);
                    // and remove this window from our subclassing linked list
                    cmnRemovePSLI(psli);
                break; }

                default:
                    fCallDefault = TRUE;
                break;

            } // end switch
        } // end if (pfnwpOriginal)
        else {
            #ifdef DEBUG_MENUS
                DosBeep(10000, 10);
            #endif
        }
    } CATCH(excpt1) {
        // exception occured:
        return (0);
    } END_CATCH;

    if (fCallDefault)
    {
        // this has only been set to TRUE for "default" in
        // the switch statement above; we then call the
        // default window procedure.
        // This is either the original folder frame window proc
        // of the WPS itself or maybe the one of other WPS enhancers
        // which have subclassed folder windows (ObjectDesktop
        // and the like).
        // We do this outside the TRY/CATCH stuff above so that
        // we don't get blamed for exceptions which we are not
        // responsible for. ;-)
        *pulWorkplaceFunc2 = 997;
        mrc = (MRESULT)(*pfnwpOriginal)(hwndFrame, msg, mp1, mp2);
        *pulWorkplaceFunc2 = 998;
    }

    *pulWorkplaceFunc2 = 999;
    return (mrc);
}

/*
 *@@ fnwpSupplObject:
 *      this is the wnd proc for the "Supplementary Object wnd"
 *      which is created for each folder frame window when it's
 *      subclassed. We need this window to handle additional
 *      messages which are not part of the normal message set,
 *      which is handled by fnwpSubclassedFolderFrame.
 *
 *      If we added additional messages to that proc, we'd probably
 *      ruin other WPS enhancers which might use the same message
 *      in a different context (ObjectDesktop?), so we use a
 *      different window.
 *
 *      We cannot use the global XFolder object window
 *      (fnwpXFolderObject, xthreads.c) because
 *      sometimes folder windows do not run in the main PM thread
 *      (TID 1), esp. when they're opened using WinOpenObject or
 *      REXX functions. This wnd proc always runs in the same
 *      thread as the folder frame wnd does.
 *
 *      This func is new with XFolder V0.82.
 */

MRESULT EXPENTRY fnwpSupplObject(HWND hwndObject, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MPARAM mrc = NULL;
    PSUBCLASSEDLISTITEM psli = (PSUBCLASSEDLISTITEM)
                WinQueryWindowULong(hwndObject, QWL_USER);

    switch (msg) {
        case WM_CREATE:
            // set the USER window word to the SUBCLASSEDLISTITEM
            // structure which is passed to us upon window
            // creation (see cmnSubclassFrameWnd, which creates
            // us)
            mrc = WinDefWindowProc(hwndObject, msg, mp1, mp2);
            psli = (PSUBCLASSEDLISTITEM)mp1;
            WinSetWindowULong(hwndObject, QWL_USER, (ULONG)psli);
        break;

        /*
         * SOM_ACTIVATESTATUSBAR:
         *      add / remove / repaint the folder status bar;
         *      this is posted every time XFolder needs to change
         *      anything about status bars. We must not play with
         *      frame controls from threads other than the thread
         *      in which the status bar was created, i.e. the thread
         *      in which the folder frame is running (which, in most
         *      cases, is thread 1, the main PM thread of the WPS),
         *      because reformatting frame controls from other
         *      threads will cause PM hangs or WPS crashes.
         *      Parameters:
         *      ULONG mp1   0: disable (destroy) status bar
         *                  1: enable (create) status bar
         *                  2: update (reformat) status bar
         *      HWND  mp2:  hwndView (frame) to update
         */

        case SOM_ACTIVATESTATUSBAR: {
            HWND hwndFrame = (HWND)mp2;
            #ifdef DEBUG_STATUSBARS
                _Pmpf(( "SOM_ACTIVATESTATUSBAR, mp1: %lX, psli: %lX", mp1, psli));
            #endif

            if (psli)
                switch ((ULONG)mp1) {
                    case 0:
                        _xfShowStatusBar(psli->somSelf, psli, FALSE);
                    break;

                    case 1:
                        _xfShowStatusBar(psli->somSelf, psli, TRUE);
                    break;

                    default: {
                        // == 2 => update status bars; this is
                        // neccessary if the font etc. has changed
                        PSZ pszStatusBarFont =
                                cmnQueryStatusBarSetting(SBS_STATUSBARFONT);
                        WinSendMsg(psli->hwndStatusBar, STBM_PROHIBITBROADCASTING,
                            (MPARAM)TRUE, MPNULL);
                        WinSetPresParam(psli->hwndStatusBar, PP_FONTNAMESIZE,
                             (ULONG)strlen(pszStatusBarFont) + 1, (PVOID)pszStatusBarFont);
                        WinSendMsg(hwndFrame, WM_UPDATEFRAME, MPNULL, MPNULL);
                        WinSendMsg(psli->hwndStatusBar, STBM_UPDATESTATUSBAR, MPNULL, MPNULL);
                    break; }
                }
        break; }

        default:
            mrc = WinDefWindowProc(hwndObject, msg, mp1, mp2);
    }
    return (mrc);
}

/*
 *@@ fnwpFolderContentMenu:
 *      this is the subclassed wnd proc for folder content menus;
 *      we need to intercept mouse button 2 msgs to open a folder
 *      (WarpCenter behavior).
 */

MRESULT EXPENTRY fnwpFolderContentMenu(HWND hwndMenu, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    PTHREADGLOBALS pThreadGlobals = xthrQueryGlobals();
    ULONG       *pulWorkplaceFunc2 = &(pThreadGlobals->ulWorkplaceFunc2);
    ULONG       ulOldWorkplaceFunc2 = *pulWorkplaceFunc2;
    MRESULT     mrc = 0;

    *pulWorkplaceFunc2 = 10000;

    TRY_LOUD(excpt1)   // install "loud" exception handler (except.h)
    {
        USHORT  sSelected;
        POINTL  ptlMouse;
        RECTL   rtlItem;

        switch(msg)
        {
            case WM_ADJUSTWINDOWPOS:
            {
                PSWP pswp = (PSWP)mp1;
                BOOL fAdjusted = FALSE;
                #ifdef DEBUG_MENUS
                    _Pmpf(("WM_ADJUSTWINDOWPOS"));
                #endif

                if ((pswp->fl & (SWP_MOVE)) == (SWP_MOVE)) {
                    #ifdef DEBUG_MENUS
                        _Pmpf(("  SWP_MOVE set"));
                    #endif

                    if (!fFldrContentMenuMoved) {
                        #ifdef DEBUG_MENUS
                            _Pmpf(("    Checking bounds"));
                        #endif
                        if ((pswp->x + pswp->cx) >
                                WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN))
                        {
                            pswp->x = 0;
                            #ifdef DEBUG_MENUS
                                _Pmpf(("    Changed x pos"));
                            #endif
                            // avoid several changes for this menu;
                            // this flag is reset by WM_INITMENU in
                            // fnwpSubclassedFolderFrame
                            fFldrContentMenuMoved = TRUE;
                            fAdjusted = TRUE;
                        }
                        if ((pswp->y + pswp->cy) >
                                WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN))
                        {
                            pswp->y = 0;
                            #ifdef DEBUG_MENUS
                                _Pmpf(("    Changed y pos"));
                            #endif
                            // avoid several changes for this menu;
                            // this flag is reset by WM_INITMENU in
                            // fnwpSubclassedFolderFrame
                            fFldrContentMenuMoved = TRUE;
                            fAdjusted = TRUE;
                        }
                    }
                }
                if (fAdjusted)
                    pswp->fl |= (SWP_NOADJUST);
                mrc = (MRESULT)(*pfnwpFolderContentMenuOriginal)(hwndMenu, msg, mp1, mp2);
                fFldrContentMenuButtonDown = FALSE;
            break; }

            #ifdef DEBUG_MENUS
                case MM_SELECTITEM: {
                    _Pmpf(( "MM_SELECTITEM: mp1 = %lX/%lX, mp2 = %lX",
                        SHORT1FROMMP(mp1),
                        SHORT2FROMMP(mp1),
                        mp2 ));
                    mrc = (MRESULT)(*pfnwpFolderContentMenuOriginal)(hwndMenu, msg, mp1, mp2);
                break; }
            #endif

            case WM_BUTTON2DOWN:
                #ifdef DEBUG_MENUS
                    _Pmpf(("WM_BUTTON2DOWN"));
                #endif

                ptlMouse.x = SHORT1FROMMP(mp1);
                ptlMouse.y = SHORT2FROMMP(mp1);
                WinSendMsg(hwndMenu, MM_SELECTITEM,
                    MPFROM2SHORT(MIT_NONE, FALSE),
                    MPFROM2SHORT(0, FALSE));
                sSelected = winhQueryItemUnderMouse(hwndMenu, &ptlMouse, &rtlItem);
                WinSendMsg(hwndMenu, MM_SETITEMATTR,
                        MPFROM2SHORT(sSelected,
                            FALSE),
                        MPFROM2SHORT(MIA_HILITED, MIA_HILITED)
                    );
            break;

            case WM_BUTTON1DOWN:
                // let this be handled by the default proc
                #ifdef DEBUG_MENUS
                    _Pmpf(("WM_BUTTON1DOWN"));
                #endif
                fFldrContentMenuButtonDown = TRUE;
                mrc = (MRESULT)(*pfnwpFolderContentMenuOriginal)(hwndMenu, msg, mp1, mp2);
            break;

            case WM_BUTTON1DBLCLK:
            case WM_BUTTON2UP: {
                // upon receiving these, we will open the object directly; we need to
                // cheat a little bit because sending MM_SELECTITEM would open the submenu
                #ifdef DEBUG_MENUS
                    _Pmpf(("WM_BUTTON2UP"));
                #endif
                fFldrContentMenuButtonDown = TRUE;
                *pulWorkplaceFunc2 = 10100;
                ptlMouse.x = SHORT1FROMMP(mp1);
                ptlMouse.y = SHORT2FROMMP(mp1);
                WinSendMsg(hwndMenu, MM_SELECTITEM,
                    MPFROM2SHORT(MIT_NONE, FALSE),
                    MPFROM2SHORT(0, FALSE));
                sSelected = winhQueryItemUnderMouse(hwndMenu, &ptlMouse, &rtlItem);

                xthrPlaySystemSound(MMSOUND_XFLD_CTXTSELECT);

                WinPostMsg(WinQueryWindow(hwndMenu, QW_OWNER),
                    WM_COMMAND,
                    (MPARAM)sSelected,
                    MPFROM2SHORT(CMDSRC_MENU, FALSE));
            break; }

            default:
                *pulWorkplaceFunc2 = 11000;
                mrc = (MRESULT)(*pfnwpFolderContentMenuOriginal)(hwndMenu, msg, mp1, mp2);
                *pulWorkplaceFunc2 = 11001;
            break;
        } // end switch
    }
    CATCH(excpt1) {
        // exception occured:
        return (MRESULT)0; // keep compiler happy
    } END_CATCH;

    *pulWorkplaceFunc2 = ulOldWorkplaceFunc2;
    return (mrc);
}

/*
 * fnwpSelectSome:
 *      dlg proc for "Select by name" window
 */

MRESULT EXPENTRY fnwpSelectSome(HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    MRESULT mrc = MPNULL;

    switch (msg) {
        case WM_INITDLG:  {
            CHAR szTitle[CCHMAXPATH];
            WinSetWindowULong(hwndDlg, QWL_USER, (ULONG)mp2); // Owner frame hwnd;
            WinQueryWindowText((HWND)mp2,
                    sizeof(szTitle),
                    szTitle);
            WinSetWindowText(hwndDlg, szTitle);

            WinSetDlgItemText(hwndDlg, ID_XFDI_SOME_ENTRYFIELD, "*");
            WinSendDlgItemMsg(hwndDlg, ID_XFDI_SOME_ENTRYFIELD,
                    EM_SETSEL,
                    MPFROM2SHORT(0, 1000), // select all
                    MPNULL);

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

        case WM_COMMAND: {
            switch (SHORT1FROMMP(mp1)) {

                /*
                 * ID_XFDI_SOME_SELECT / DESELECT:
                 *      these are the "select" / "deselect" buttons
                 */

                case ID_XFDI_SOME_SELECT:
                case ID_XFDI_SOME_DESELECT: {
                    CHAR szMask[CCHMAXPATH];
                    HWND hwndFrame = WinQueryWindowULong(hwndDlg, QWL_USER);
                    if (hwndFrame) {

                        HWND hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);

                        if (hwndCnr) {
                            WinQueryDlgItemText(hwndDlg, ID_XFDI_SOME_ENTRYFIELD,
                                sizeof(szMask),
                                szMask);

                            if (strlen(szMask)) {
                                // now go through all the container items in hwndCnr
                                // and select / deselct them accordingly
                                PMINIRECORDCORE pmrc = NULL;
                                do {
                                    pmrc =
                                        (PMINIRECORDCORE)WinSendMsg(hwndCnr,
                                                CM_QUERYRECORD,
                                                (MPARAM)pmrc,
                                                MPFROM2SHORT(
                                                    (pmrc) ? CMA_NEXT : CMA_FIRST,
                                                    CMA_ITEMORDER)
                                                );
                                    if (pmrc) {
                                        CHAR szTarget[CCHMAXPATH];
                                        // CHAR szTemp[3000];
                                        DosEditName(1,
                                            pmrc->pszIcon,
                                            szMask,
                                            szTarget,
                                            sizeof(szTarget)-1);
                                        if (stricmp(pmrc->pszIcon, szTarget) == 0)
                                            // if the two are equal, the cnr item matches
                                            // the search mask
                                            WinSendMsg(hwndCnr,
                                                CM_SETRECORDEMPHASIS,
                                                pmrc,
                                                MPFROM2SHORT(
                                                    // select or deselect flag
                                                    (SHORT1FROMMP(mp1) == ID_XFDI_SOME_SELECT),
                                                    CRA_SELECTED
                                                ));
                                    }
                                } while (pmrc);
                            }

                            winhSetDlgItemFocus(hwndDlg, ID_XFDI_SOME_ENTRYFIELD);
                            WinSendDlgItemMsg(hwndDlg, ID_XFDI_SOME_ENTRYFIELD,
                                    EM_SETSEL,
                                    MPFROM2SHORT(0, 1000), // select all
                                    MPNULL);
                        }
                    }
                break; }

                case ID_XFDI_SOME_SELECTALL:
                case ID_XFDI_SOME_DESELECTALL: {
                    HWND hwndFrame = WinQueryWindowULong(hwndDlg, QWL_USER);
                    if (hwndFrame) {
                        HWND hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);
                        if (hwndCnr) {
                            PMINIRECORDCORE pmrc = NULL;
                            do {
                                pmrc =
                                    (PMINIRECORDCORE)WinSendMsg(hwndCnr,
                                            CM_QUERYRECORD,
                                            (MPARAM)pmrc,
                                            MPFROM2SHORT(
                                                (pmrc) ? CMA_NEXT : CMA_FIRST,
                                                CMA_ITEMORDER)
                                            );
                                if (pmrc) {
                                    WinSendMsg(hwndCnr,
                                        CM_SETRECORDEMPHASIS,
                                        pmrc,
                                        MPFROM2SHORT(
                                            // select or deselect flag
                                            (SHORT1FROMMP(mp1) == ID_XFDI_SOME_SELECTALL),
                                            CRA_SELECTED
                                        ));
                                }
                            } while (pmrc);

                            winhSetDlgItemFocus(hwndDlg, ID_XFDI_SOME_ENTRYFIELD);
                            WinSendDlgItemMsg(hwndDlg, ID_XFDI_SOME_ENTRYFIELD,
                                    EM_SETSEL,
                                    MPFROM2SHORT(0, 1000), // select all
                                    MPNULL);
                        }
                    }
                break; }

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

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



