
/*
 *@@sourcefile xfobj.c:
 *      This file contains SOM code for the following XWorkplace classes:
 *
 *      --  XFldObject (WPObject replacement)
 *
 *      XFldObject replaces WPObject and becomes thus part of any
 *      WPS object on the system. As a result, extreme care must
 *      be taken with whatever this code does.
 *
 *      XFldObject is needed for the following:
 *
 *      --  Initializes the whole XWorkplace environment at WPS startup
 *          by overriding M_XFldObject::wpclsInitData. This goes into
 *          initMain then.
 *
 *      --  Give the other classes access to WPS internals that cannot
 *          be reached otherwise. We drill into the WPObject instance
 *          data for some nasty hacks here.
 *
 *      --  Getting icon replacements to work.
 *
 *      --  Cooperation of all WPS objects with the trash can.
 *
 *      This class must always be installed.
 *
 *      Starting with V0.9.0, the files in classes\ contain only
 *      i.e. the methods themselves.
 *      The implementation for this class is mostly in filesys\object.c.
 *
 *@@somclass XWPObjList xl_
 *@@somclass XFldObject xo_
 *@@somclass M_XFldObject xoM_
 */

/*
 *      Copyright (C) 1997-2010 Ulrich M”ller.
 *
 *      This file is part of the XWorkplace source package.
 *      XWorkplace 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 XWorkplace 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.42
 */

#ifndef SOM_Module_xfobj_Source
#define SOM_Module_xfobj_Source
#endif
#define XFldObject_Class_Source
#define M_XFldObject_Class_Source

#pragma strings(readonly)

/*
 *  Suggested #include order:
 *  1)  os2.h
 *  2)  C library headers
 *  3)  setup.h (code generation and debugging options)
 *  4)  headers in helpers\
 *  5)  at least one SOM implementation header (*.ih)
 *  6)  dlgids.h, headers in shared\ (as needed)
 *  7)  headers in implementation dirs (e.g. filesys\, as needed)
 *  8)  #pragma hdrstop and then more SOM headers which crash with precompiled headers
 */

#define INCL_DOSPROCESS
#define INCL_DOSEXCEPTIONS
#define INCL_DOSSEMAPHORES
#define INCL_DOSERRORS

#define INCL_WINWINDOWMGR
#define INCL_WINFRAMEMGR
#define INCL_WININPUT
#define INCL_WINPOINTERS
#define INCL_WINDIALOGS
#define INCL_WINMENUS
#define INCL_WINSTDCNR
#define INCL_WINSTDBOOK
#define INCL_WINPROGRAMLIST
#define INCL_WINSHELLDATA
#define INCL_WINSWITCHLIST
#include <os2.h>

// C library headers
#include <stdio.h>
#include <setjmp.h>             // needed for except.h

// generic headers
#include "setup.h"                      // code generation and debugging options

// headers in /helpers
#include "helpers\cnrh.h"               // container helper routines
#include "helpers\dosh.h"               // Control Program helper routines
#include "helpers\except.h"             // exception handling
#include "helpers\linklist.h"           // linked list helper routines
#include "helpers\prfh.h"               // INI file helper routines
#include "helpers\sem.h"                // fast mutex semaphores
#include "helpers\standards.h"          // some standard macros
#include "helpers\stringh.h"            // string helper routines
#include "helpers\tree.h"               // red-black binary trees
#include "helpers\winh.h"               // PM helper routines
#include "helpers\xstring.h"            // extended string helpers

// SOM headers which don't crash with prec. header files
#include "xfldr.ih"
#include "xfobj.ih"

// XWorkplace implementation headers
#include "dlgids.h"                     // all the IDs that are shared with NLS
#include "shared\classtest.h"           // some cheap funcs for WPS class checks
#include "shared\common.h"              // the majestic XWorkplace include file
#include "shared\errors.h"              // private XWorkplace error codes
#include "shared\helppanels.h"          // all XWorkplace help panel IDs
#include "shared\init.h"                // XWorkplace initialization
#include "shared\kernel.h"              // XWorkplace Kernel
#include "shared\notebook.h"            // generic XWorkplace notebook handling
#include "shared\wpsh.h"                // some pseudo-SOM functions (WPS helper routines)

#include "shared\center.h"              // public XCenter interfaces

#include "filesys\fdrmenus.h"           // shared folder menu logic
#include "filesys\fileops.h"            // file operations implementation
#include "filesys\folder.h"             // XFolder implementation
#include "filesys\fdrhotky.h"           // folder hotkey handling
#include "filesys\icons.h"              // icons handling
#include "filesys\object.h"             // XFldObject implementation
#include "filesys\program.h"            // program implementation; WARNING: this redefines macros
#include "filesys\xthreads.h"           // extra XWorkplace threads

// other SOM headers
#pragma hdrstop

#include <wpdataf.h>
#include <wpshadow.h>

/* ******************************************************************
 *
 *   Global variables
 *
 ********************************************************************/

// #define USE_FASTMUTEX   1

// global variable whether XWorkplace is initialized yet
static BOOL         G_fXWorkplaceInitialized = FALSE;

extern WPFolder     *G_pConfigFolder;
                            // xfldr.c

// awake objects list V0.9.20 (2002-07-25) [umoeller]
static HMTX         G_hmtxAwakeObjects = NULLHANDLE;

static XFldObject   *G_pFirstAwakeObject = NULL,
                    *G_pLastAwakeObject = NULL;

// the following two are also exported through kernel.h
WPObject            *G_pAwakeWarpCenter = NULL;
ULONG               G_cAwakeObjects = 0;

// mutex for all XWPObjList objects V1.0.1 (2002-12-11) [umoeller]
static HMTX         G_hmtxObjectLists = NULLHANDLE;

static BOOL         G_fInsertRecordCrashed = FALSE;

/* ******************************************************************
 *
 *   Awake objects list
 *
 ********************************************************************/

/*
 *@@ LockAwakeObjectsList:
 *
 *@@added V0.9.20 (2002-07-25) [umoeller]
 */

STATIC BOOL LockAwakeObjectsList(VOID)
{
    if (G_hmtxAwakeObjects)
        // return !DosRequestMutexSem(G_hmtxAwakeObjects, SEM_INDEFINITE_WAIT);
        if (!DosRequestMutexSem(G_hmtxAwakeObjects, 2000))
            return TRUE;
        else
        {
            cmnLog(__FILE__, __LINE__, __FUNCTION__,
                   "Cannot request awake objects mutex.");
            return FALSE;
        }

    // first call:
    return !DosCreateMutexSem(NULL,
                              &G_hmtxAwakeObjects,
                              0,
                              TRUE);
}

/*
 *@@ UnlockAwakeObjectsList:
 *
 *@@added V0.9.20 (2002-07-25) [umoeller]
 */

STATIC VOID UnlockAwakeObjectsList(VOID)
{
    DosReleaseMutexSem(G_hmtxAwakeObjects);
}

/*
 *@@ LockObjectLists:
 *      locks all list objects.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

STATIC BOOL LockObjectLists(VOID)
{
    if (G_hmtxObjectLists)
        return !DosRequestMutexSem(G_hmtxObjectLists, SEM_INDEFINITE_WAIT);

    return !DosCreateMutexSem(NULL,
                              &G_hmtxObjectLists,
                              0,
                              TRUE);
}

/*
 *@@ UnlockObjectLists:
 *      unlocks all list objects.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

STATIC BOOL UnlockObjectLists(VOID)
{
    return !DosReleaseMutexSem(G_hmtxObjectLists);
}

/* ******************************************************************
 *
 *   here come the XWPObjList methods
 *
 ********************************************************************/

/*
 *@@ SetKeys:
 *      sets the internal INI key members.
 *
 *      Private method, only to be called by M_XFldObject::xwpclsCreateList.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope void  SOMLINK xl_SetKeys(XWPObjList *somSelf, PSZ pszApp,
                                   PSZ pszKey)
{
    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_SetKeys");

    _pszIniApp = pszApp;
    _pszIniKey = pszKey;
}

/*
 *@@ xl_Load:
 *      loads objects from OS2.INI into the list.
 *
 *      Private method, not to be called externally.
 *      Caller must have locked the list first.
 *
 *@@added V0.9.7 (2001-01-18) [umoeller]
 *@@changed V0.9.9 (2001-03-19) [pr]: tidied up
 *@@changed V0.9.9 (2001-03-27) [umoeller]: added OBJECTLIST encapsulation
 *@@changed V0.9.16 (2001-10-11) [umoeller]: rewritten
 *@@changed V0.9.16 (2001-10-24) [umoeller]: added fixes for duplicate objects on list, which lead to endlessly looping startup folders
 *@@changed V1.0.1 (2002-12-11) [umoeller]: turned this into an XWPObjList method
 */

SOM_Scope BOOL  SOMLINK xl_Load(XWPObjList *somSelf)
{
    BOOL        brc = FALSE;
    PSZ         pszHandles = NULL;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_Load");

    TRY_LOUD(excpt1)
    {
        if (    (!_fLoaded)
             && (_pszIniKey)
           )
        {
            BOOL fReSave = FALSE;

            if (pszHandles = prfhQueryProfileData(HINI_USERPROFILE,
                                                  _pszIniApp,
                                                  _pszIniKey,
                                                  NULL))
            {
                PCSZ pTemp = pszHandles;
                ULONG ulCharsLeft;

                while (     (pTemp)
                         && (*pTemp)
                      )
                {
                    HOBJECT          hObject;
                    if (hObject = strtol(pTemp,
                                         NULL,
                                         16))
                    {
                        PCSZ        p;
                        WPObject    *pobj;
                        if (    (pobj = _wpclsQueryObject(_WPObject, hObject))
                             && (wpshCheckObject(pobj))
                           )
                        {
                            // make sure the object is NOT on the list
                            // yet!! for some reason, we end up with objects
                            // being on the list twice or something.
                            // V0.9.16 (2001-10-24) [umoeller]

                            if (-1 == lstIndexFromItem(_pvList, pobj))
                            {
                                // not on list yet:
                                // add it now
                                if (lstAppendItem(_pvList, pobj))
                                    _xwpAddedToList(pobj, somSelf);
                            }
                            else
                            {
                                // we have duplicate objects on the list:
                                // save the fixed list back to OS2.INI
                                // V0.9.16 (2001-10-24) [umoeller]
                                fReSave = TRUE;
                                // _Pmpf(("   got duplicate obj %lX = %s", pobj, _wpQueryTitle(pobj)));
                            }
                        }

                        // find next space
                        if (p = strchr(pTemp, ' '))
                        {
                            // skip spaces
                            while (*p && *p == ' ')
                                p++;
                            pTemp = p;          // points to either next non-space
                                                // or null char now
                        }
                        else
                            // no more spaces:
                            break;
                    }
                    else
                        // cannot get object string:
                        break;
                };
            }

            _fLoaded = TRUE;

            if (fReSave)        // V0.9.16 (2001-10-24) [umoeller]
            {
                // _Pmpf(("  RESAVE, saving modified list back to OS2.INI"));
                _Write(somSelf);
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (pszHandles)
        free(pszHandles);

    return brc;
}

/*
 *@@ Write:
 *      writes the list out to the OS2.INI file.
 *
 *      This creates a handle for each object on the
 *      list.
 *
 *      Private method, not to be called externally.
 *      Caller must have locked the list first.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xl_Write(XWPObjList *somSelf)
{
    BOOL    brc = FALSE;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_Write");

    if (    (_fLoaded)
         && (_pszIniKey)
       )
    {
        TRY_LOUD(excpt1)
        {
            PLISTNODE pNode;
            if (pNode = lstQueryFirstNode(_pvList))
            {
                // list is not empty: recompose string
                XSTRING strTemp;
                xstrInit(&strTemp, 100);

                while (pNode)
                {
                    CHAR szHandle[30];
                    WPObject *pobj = (WPObject*)pNode->pItemData;
                    sprintf(szHandle,
                            "%lX ",         // note the space
                            _wpQueryHandle(pobj));

                    xstrcat(&strTemp, szHandle, 0);

                    pNode = pNode->pNext;
                }

                brc = PrfWriteProfileString(HINI_USERPROFILE,
                                            (PSZ)_pszIniApp,
                                            (PSZ)_pszIniKey,
                                            strTemp.psz);

                xstrClear(&strTemp);
            }
            else
            {
                // list is empty: remove
                brc = PrfWriteProfileData(HINI_USERPROFILE,
                                          (PSZ)_pszIniApp,
                                          (PSZ)_pszIniKey,
                                          NULL,
                                          0);
            }
        }
        CATCH(excpt1)
        {
            brc = FALSE;
        } END_CATCH();
    }

    return brc;
}

/*
 *@@ Append:
 *      appends pobj to the list object represented by somSelf,
 *      if it is not yet in the list.
 *
 *      Returns TRUE if the list was changed. In that case,
 *      the list is rewritten to the specified INI key.
 *
 *@@added V0.9.0 (99-11-16) [umoeller]
 *@@changed V0.9.7 (2001-01-18) [umoeller]: made this generic for all objs... renamed from fdr*
 *@@changed V0.9.7 (2001-01-18) [umoeller]: removed mallocs(), this wasn't needed
 *@@changed V0.9.9 (2001-01-29) [lafaix]: wrong object set, fixed
 *@@changed V0.9.9 (2001-03-19) [pr]: lock/unlock objects on the lists
 *@@changed V0.9.9 (2001-03-27) [umoeller]: added OBJECTLIST encapsulation
 *@@changed V0.9.14 (2001-07-31) [pr]: fixed confusing code
 *@@changed V1.0.1 (2002-12-11) [umoeller]: turned this into an XWPObjList method
 */

SOM_Scope BOOL  SOMLINK xl_Append(XWPObjList *somSelf, WPObject* pobj)
{
    BOOL    brc = FALSE,
            fLocked = FALSE;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_Append");

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
        {
            PLISTNODE pNode;

            if (    (!_fLoaded)
                 && (_pszIniKey)
               )
                _Load(somSelf);

            if (    (!(pNode = lstNodeFromItem(_pvList, pobj)))
                 && (brc = !!lstAppendItem(_pvList, pobj))
               )
            {
                // notify object of addition
                _xwpAddedToList(pobj, somSelf);

                // list changed:
                if (    (_fLoaded)
                     && (_pszIniKey)
                   )
                    _Write(somSelf);
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        UnlockObjectLists();

    return brc;
}

/*
 *@@ Count:
 *      returns the no. of objects currently stored
 *      in the list.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xl_Count(XWPObjList *somSelf)
{
    ULONG   ulrc = 0;
    BOOL    fLocked = FALSE;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_Count");

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
            ulrc = lstCountItems(_pvList);
    }
    CATCH(excpt1)
    {
    } END_CATCH();

    if (fLocked)
        UnlockObjectLists();

    return ulrc;
}

/*
 *@@ IsIn:
 *      returns TRUE if pobj is already in the list
 *      represented by somSelf.
 *
 *@@added V0.9.0 (99-11-16) [umoeller]
 *@@changed V0.9.7 (2001-01-18) [umoeller]: made this generic for all objs... renamed from fdr*
 *@@changed V0.9.7 (2001-01-18) [umoeller]: removed mallocs(), this wasn't needed
 *@@changed V0.9.9 (2001-03-27) [umoeller]: added OBJECTLIST encapsulation
 *@@changed V1.0.1 (2002-12-11) [umoeller]: turned this into an XWPObjList method
 */

SOM_Scope BOOL  SOMLINK xl_IsIn(XWPObjList *somSelf, WPObject* pobj)
{
    BOOL    brc = FALSE,
            fLocked = FALSE;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_IsIn");

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
        {
            if (    (!_fLoaded)
                 && (_pszIniKey)
               )
                _Load(somSelf);

            brc = !!lstNodeFromItem(_pvList, pobj);
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fLocked)
        UnlockObjectLists();

    return brc;
}

/*
 *@@ Remove:
 *      removes pobj from the list represented by somSelf.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xl_Remove(XWPObjList *somSelf, WPObject* pobj)
{
    BOOL    brc = FALSE,
            fLocked = FALSE;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_Remove");

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
        {
            PLISTNODE pNode;

            if (    (!_fLoaded)
                 && (_pszIniKey)
               )
                _Load(somSelf);

            if (    (pNode = lstNodeFromItem(_pvList, pobj))
                 && (brc = lstRemoveNode(_pvList, pNode))
               )
            {
                // notify object of removal
                _xwpRemovedFromList(pobj, somSelf);

                // list changed:
                if (    (_fLoaded)
                     && (_pszIniKey)
                   )
                    _Write(somSelf);
            }
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fLocked)
        UnlockObjectLists();

    return brc;
}

/*
 *@@ Enum:
 *      traverses the list represented by somSelf.
 *
 *      If pobj is NULL, this returns the first object on
 *      the list. Otherwise it returns the object that
 *      comes after pobj in the list.
 *
 *      Returns NULL if no object was found (that is,
 *      if the list is empty or there is no object
 *      after pobj).
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope WPObject*  SOMLINK xl_Enum(XWPObjList *somSelf, WPObject* pobj)
{
    BOOL        fLocked = FALSE;
    WPObject    *pobjReturn = NULL;

    XWPObjListData *somThis = XWPObjListGetData(somSelf);
    XWPObjListMethodDebug("XWPObjList","xl_Enum");

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
        {
            PLISTNODE pNode;

            if (    (!_fLoaded)
                 && (_pszIniKey)
               )
                _Load(somSelf);

            if (!pobj)
            {
                // caller wants first:
                if (pNode = lstQueryFirstNode(_pvList))
                    pobjReturn = (WPObject*)pNode->pItemData;
            }
            else
            {
                // caller wants next:
                if (    (pNode = lstNodeFromItem(_pvList, pobj))
                     && (pNode = pNode->pNext)
                   )
                    pobjReturn = (WPObject*)pNode->pItemData;
            }
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fLocked)
        UnlockObjectLists();

    return pobjReturn;
}

/*
 *@@ somDefaultInit:
 *      overridden SOM constructor to initialize a
 *      new instance of XWPObjList.
 *
 *      Always use M_XFldObject::xwpclsCreateList to
 *      create a new instance of XWPObjList, never
 *      use somNew directly.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope void SOMLINK xl_somDefaultInit(XWPObjList *somSelf,
                                         somInitCtrl* ctrl)
{
    BOOL        fLocked = FALSE;

    XWPObjListData *somThis; /* set in BeginInitializer */
    somInitCtrl globalCtrl;
    somBooleanVector myMask;
    XWPObjListMethodDebug("XWPObjList","somDefaultInit");
    XWPObjList_BeginInitializer_somDefaultInit;

    XWPObjList_Init_SOMObject_somDefaultInit(somSelf, ctrl);

    /*
     * local XWPObjList initialization code added by programmer
     */

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
        {
            _fLoaded = FALSE;
            _pszIniApp = NULL;
            _pszIniKey = NULL;
            _pvList = lstCreate(FALSE);       // no auto-free

            PMPF_OBJLISTS(("XWPObjList at 0x%lX created", somSelf));
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fLocked)
        UnlockObjectLists();
}


/*
 *@@ somDestruct:
 *      overridden SOM destructor to de-initialize an
 *      instance of XWPObjList.
 *
 *      You can call somFree(pList) to destroy a list.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope void SOMLINK xl_somDestruct(XWPObjList *somSelf, octet doFree,
                                      somDestructCtrl* ctrl)
{
    BOOL        fLocked = FALSE;

    XWPObjListData *somThis; /* set in BeginDestructor */
    somDestructCtrl globalCtrl;
    somBooleanVector myMask;
    XWPObjListMethodDebug("XWPObjList","xl_somDestruct");
    XWPObjList_BeginDestructor;

    /*
     * local XWPObjList deinitialization code added by programmer
     */

    TRY_LOUD(excpt1)
    {
        if (fLocked = LockObjectLists())
        {
            PLISTNODE pNode;

            while (pNode = lstQueryFirstNode(_pvList))
            {
                WPObject *pobj = (WPObject*)pNode->pItemData;
                lstRemoveNode(_pvList, pNode);
                // notify object of removal
                _xwpRemovedFromList(pobj, somSelf);
            }

            lstFree((LINKLIST**)&_pvList);
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fLocked)
        UnlockObjectLists();

    XWPObjList_EndDestructor;
}



/* ******************************************************************
 *
 *   here come the XFldObject instance methods
 *
 ********************************************************************/

/*
 *@@ xwpQueryHandle:
 *      returns the 32-bit object handle for somSelf.
 *
 *      If the object is a file-system object AND does not
 *      yet have a handle assigned AND (fCreate == TRUE),
 *      a new handle is created and returned. If
 *      (fCreate == FALSE), we may return NULLHANDLE to
 *      signal that the file-system object does not yet
 *      have a handle assigned.
 *
 *      Right now fCreate doesn't work yet because we
 *      always call _wpQueryHandle at this point, but this
 *      method is a preparation for future file-system handles
 *      replacements.
 *
 *@@added V0.9.20 (2002-08-04) [umoeller]
 */

SOM_Scope HOBJECT  SOMLINK xo_xwpQueryHandle(XFldObject *somSelf,
                                             BOOL fCreate)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpQueryHandle");

    return _wpQueryHandle(somSelf);
}

/*
 *@@ xwpQueryLocks:
 *      returns the current lock count of somSelf.
 *      This is needed because wpIsLocked only returns
 *      TRUE or FALSE and we'd like to debug.
 *
 *@@added V1.0.0 (2002-08-28) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xo_xwpQueryLocks(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpQueryLocks");

    if (_pvWPObjectData)
        return ((PIBMOBJECTDATA)_pvWPObjectData)->cLocks;

    return -1;
}

/*
 *@@ xwpAddReplacementIconPage:
 *      calls ntbInsertPage for a replacement "Icon" page with
 *      the given page ID and default help panel.
 *
 *      icoIcon1InitPage reacts to the page ID and formats
 *      the page accordingly. Valid page IDs are:
 *
 *      --  SP_OBJECT_ICONPAGE1: standard "Icon" page.
 *          Called from XFldObject::wpAddObjectGeneralPage.
 *
 *      --  SP_OBJECT_ICONPAGE2: animation "Icon" page 2.
 *          Called from XFldObject::wpAddObjectGeneralPage.
 *
 *      --  SP_TRASHCAN_ICON: special trash can "Icon" page.
 *
 *      --  SP_OBJECT_ICONPAGE1_X: special "Icon" page if
 *          some dumb WPS class is trying to trick us into
 *          removing the object "Icon" page, such as with
 *          WPSharedDir.
 *
 *      Note: This does not check the global setting for the
 *      icon page replacement. Call this only if the setting
 *      is enabled.
 *
 *@@added V0.9.19 (2002-06-15) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xo_xwpAddReplacementIconPage(XFldObject *somSelf,
                                                      HWND hwndNotebook,
                                                      ULONG ulPageID,
                                                      ULONG ulDefaultHelpPanel)
{
    INSERTNOTEBOOKPAGE inbp;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpAddReplacementIconPage");

    memset(&inbp, 0, sizeof(INSERTNOTEBOOKPAGE));
    inbp.somSelf = somSelf;
    inbp.hwndNotebook = hwndNotebook;
    inbp.hmod = cmnQueryNLSModuleHandle(FALSE);
    inbp.ulDlgID = ID_XFD_EMPTYDLG;
    inbp.ulPageID = ulPageID;

    if (ulPageID == SP_OBJECT_ICONPAGE2)
    {
        inbp.usPageStyleFlags = BKA_MINOR;
    }
    else
    {
        inbp.usPageStyleFlags = BKA_MAJOR;
        inbp.pcszName = cmnGetString(ID_XSSI_ICONPAGE);
                // no new string needed, was defined for trash can already
    }

    inbp.fEnumerate = TRUE;
    inbp.ulDefaultHelpPanel  = ulDefaultHelpPanel; // ID_XSH_OBJICONPAGE1;
    inbp.pfncbInitPage    = icoIcon1InitPage;
    inbp.pfncbItemChanged = icoIcon1ItemChanged;
    return ntbInsertPage(&inbp);
}

/*
 *@@ xwpQueryRealDefaultView:
 *      this new method returns the "real default view" of
 *      the object.
 *
 *      This might be the same as the return value of
 *      WPObject::wpQueryDefaultView, but not necessarily.
 *
 *      Thing is, several classes override the wpQueryDefaultView
 *      method to implement a different behavior. The most
 *      annoying example is the WPFolder class which forces
 *      the folder default view to be inherited from the parent
 *      folder.
 *
 *      The WPObject class has an instance variable which
 *      contains the "real" default view for the object.
 *      This is mostly set to OPEN_DEFAULT (-1), which is
 *      never returned by WPObject::wpQueryDefaultView though,
 *      because in that case, M_WPObject::wpclsQueryDefaultView
 *      gets called. Since this mechanism apparently doesn't
 *      work for folders though, we have added this method
 *      which returns the true value of the instance variable.
 *
 *      This will only be != OPEN_DEFAULT if the user explicitly
 *      changed the default view for the object on the "Menu"
 *      page.
 *
 *      This returns 0 if we couldn't get access to the internal
 *      WPObject data.
 *
 *@@added V0.9.12 (2001-05-01) [umoeller]
 *@@changed V0.9.16 (2001-11-25) [umoeller]: now using new instance var to pick up changes
 *@@changed V0.9.19 (2002-07-01) [umoeller]: rewritten to use IBMOBJECTDATA now
 */

SOM_Scope ULONG  SOMLINK xo_xwpQueryRealDefaultView(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpQueryRealDefaultView");

    if (_pvWPObjectData)
        return ((PIBMOBJECTDATA)_pvWPObjectData)->ulDefaultView;

    return OPEN_DEFAULT;
}

/*
 *@@ xwpQueryOriginalObjectID:
 *      returns the original object ID that was stored
 *      in the object's instance data. This is not
 *      necessarily the same value as WPObject::wpQueryObjectID.
 *
 *      The object ID is stored both in the instance data
 *      of the object (and thus restored with wpRestoreState)
 *      _and_ the list in the OS2.INI file. This leads to
 *      problems if the same folder is made awake from two
 *      WPS installations which use different INI files.
 *      The pathological case is that one WPS sets the object ID
 *      (which updates both the INI data and the instance data)
 *      and then another WPS encounters the object. With the
 *      second WPS, we then get the following scenario:
 *
 *      1) The WPS restores the object ID in wpRestoreState.
 *         wpRestoreState makes _no_ check for whether the
 *         object ID matches the one in the INI file (which
 *         it doesn't, in this case, because the ID was from
 *         another WPS).
 *
 *      2) For some stupid, stupid reason, WPObject::wpQueryObjectID
 *         however then _removes_ the object ID from the instance
 *         data if it doesn't match the INI file. This is desastrous
 *         for "foreign" desktops because in this case, the <WP_DESKTOP>
 *         ID is removed from the other desktop and that WPS won't
 *         start anymore then.
 *
 *      Here's our hack solution:
 *
 *      1) In XFldObject::wpRestoreState, we make a backup copy of
 *         the original object ID which has _not_ yet been verified
 *         against the INI data.
 *
 *      2) We leave WPObject::wpQueryObjectID alone, i.e. we allow
 *         it to nuke the object's instance data (but we still have
 *         the backup).
 *
 *      3) In XFldObject::wpSaveState, we check for whether we have
 *         a backup but the instance member is NULL. In that case,
 *         we temporarily hack the instance data to contain the
 *         old object ID again and restore the NULL pointer
 *         after the save.
 *
 *      This method returns the backed up value. This is only used
 *      in the object "Details" dialog.
 *
 *@@added V0.9.16 (2001-12-06) [umoeller]
 */

SOM_Scope PSZ  SOMLINK xo_xwpQueryOriginalObjectID(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpQueryOriginalObjectID");

    return _pWszOriginalObjectID;
}

/*
 *@@ xwpSetTitleOnly:
 *      wrapper around XFldObject::wpSetTitle which allows
 *      us to call that method code directly without calling
 *      the additional code that WPFileSystem and others
 *      add on top of that.
 *
 *      This is for the case where refresh detects that the
 *      title of an object has changed (e.g. because a
 *      .LONGNAME was added or deleted) and the object's
 *      title needs to be refreshed. However, if we called
 *      WPFileSystem::wpSetTitle, the file's real name
 *      would also be changed.
 *
 *      We can't use WPFileSystem::wpSetTitleNoRenameFile
 *      either because that writes a new .LONGNAME EA.
 *      Hence this additional method.
 *
 *@@added V1.0.1 (2002-12-08) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_xwpSetTitleOnly(XFldObject *somSelf,
                                           PSZ pszNewTitle)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpSetTitleOnly");

    // note, direct function, no method call
    return xo_wpSetTitle(somSelf, pszNewTitle);
}

/*
 *@@ xwpQueryDeletion:
 *      this method may be called at any time to determine if and
 *      when an object has been deleted into the trash can.
 *
 *      If this object has been deleted into the XWorkplace trash can
 *      (that is, if the actual object currently resides in the hidden
 *      \TRASH directory tree and a related XWPTrashObject exists),
 *      this returns TRUE and puts the date and time of deletion into
 *      the specified structures.
 *
 *      If this object has not been deleted, FALSE is returned and the
 *      structures are not changed.
 *
 *@@added V0.9.0 [umoeller]
 *@@changed V0.9.20 (2002-07-25) [umoeller]: adjusted for new TRASHDATA
 */

SOM_Scope BOOL  SOMLINK xo_xwpQueryDeletion(XFldObject *somSelf,
                                            CDATE* pcdateDeleted,
                                            CTIME* pctimeDeleted)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    PTRASHDATA p;

    // BOOL    brc = (_cdateDeleted.year != 0);     // V0.9.16 (2001-12-06) [umoeller]

    XFldObjectMethodDebug("XFldObject","xo_xwpQueryDeletion");

    // _PmpfF(("_cdateDeleted.year %d, returning %d", _cdateDeleted.year, brc));

    if (p = (PTRASHDATA)_pvTrashData)
    {
        if (pcdateDeleted)
            memcpy(pcdateDeleted, &p->cdateDeleted, sizeof(CDATE));
        if (pctimeDeleted)
            memcpy(pctimeDeleted, &p->ctimeDeleted, sizeof(CTIME));

        return TRUE;
    }

    return FALSE;
}

/*
 *@@ GetTrashData:
 *      little helper func that returns a pointer to
 *      the TRASHDATA in the object's instance data.
 *
 *      This allocates the struct on the first call.
 *
 *      Preconditions:
 *
 *      --  Caller must hold object mutex.
 *
 *@@added V0.9.20 (2002-07-25) [umoeller]
 */

STATIC PTRASHDATA GetTrashData(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);

    // set deletion data:
    if (!_pvTrashData)
    {
        // no trash data yet:
        ULONG rc;
        if (_pvTrashData = _wpAllocMem(somSelf,
                                       sizeof(TRASHDATA),
                                       &rc))
            memset(_pvTrashData, 0, sizeof(TRASHDATA));
    }

    return (PTRASHDATA)_pvTrashData;
}

/*
 *@@ xwpSetDeletion:
 *      this sets the deletion date and time fields for this object.
 *      This method is _only_ to be called by the XWorkplace trash
 *      can and changes the data which is subsequently returned by
 *      XFldObject::xwpQueryDeletion.
 *
 *      If (fSet == TRUE),the object is assumed to have been moved to the
 *      invisible TRASH directory on the object's drive already.
 *
 *      The object's internal fields for deletion date and time will
 *      then be set to the current system date and time, and
 *      subsequent calls to XFldObject::xwpQueryDeletion will return
 *      this data.
 *
 *      If (fSet == FALSE), this means the obejct is no longer
 *      considered "deleted" (because it's been restored), and subsequent
 *      calls to XFldObject::xwpQueryDeletion will return FALSE only.
 *
 *@@added V0.9.0 [umoeller]
 *@@changed V0.9.20 (2002-08-10) [umoeller]: now killing object ID
 */

SOM_Scope BOOL  SOMLINK xo_xwpSetDeletion(XFldObject *somSelf,
                                          BOOL fSet)
{
    BOOL fLocked = FALSE,
         brc = FALSE;
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpSetDeletion");

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            if (fSet)
            {
                PTRASHDATA p;

                if (p = GetTrashData(somSelf))
                {
                    DATETIME    dt;
                    DosGetDateTime(&dt);
                    cnrhDateTimeDos2Win(&dt, &p->cdateDeleted, &p->ctimeDeleted);
                    brc = TRUE;
                }
            }
            else
            {
                // clear:
                if (_pvTrashData)
                {
                    _wpFreeMem(somSelf, (PBYTE)_pvTrashData);
                    _pvTrashData = NULL;
                }

                brc = TRUE;
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    if (brc)
        return _wpSaveDeferred(somSelf);

    return FALSE;
}

/*
 *@@ xwpTrashObjectID:
 *
 *@@added V0.9.20 (2002-08-10) [umoeller]
 */

SOM_Scope ULONG SOMLINK xo_xwpTrashObjectID(XFldObject *somSelf)
{
    ULONG ulrc = 0;
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpTrashObjectID");

    return ulrc;
}

/*
 *@@ xwpSetTrashObject:
 *      sets the internal trash object field to the specified
 *      trash object (XWPTrashObject) to be able to relate a
 *      deleted object to the corresponding trash object in the
 *      trash can.
 *
 *      Only to be called by the trash can.
 *
 *@@added V0.9.3 (2000-04-11) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_xwpSetTrashObject(XFldObject *somSelf,
                                             WPObject* pTrashObject)
{
    BOOL fLocked = FALSE,
         brc = FALSE;
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpSetTrashObject");

    TRY_LOUD(excpt1)
    {
        if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
        {
            PTRASHDATA p;

            if (    (pTrashObject)
                 && (p = GetTrashData(somSelf))
               )
            {
                p->pTrashObject = pTrashObject;
                brc = TRUE;
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return brc;
}

/*
 *@@ xwpQueryFlags:
 *      returns the current list notification flags for this
 *      object. See XFldObject::xwpModifyFlags for details.
 *
 *@@added V0.9.6 (2000-10-23) [umoeller]
 *@@changed V1.0.0 (2002-09-02) [umoeller]: now using new flags mutex to avoid deadlocks
 */

SOM_Scope ULONG  SOMLINK xo_xwpQueryFlags(XFldObject *somSelf)
{
    ULONG   ulrc = 0;
    BOOL    fFlagsLocked = FALSE;
    XFldObjectMethodDebug("XFldObject","xo_xwpQueryFlags");

    // we need the lock here because xwpModifyFlags
    // reads and writes holding the lock too
    TRY_LOUD(excpt1)
    {
        XFldObjectData *somThis = XFldObjectGetData(somSelf);

        if (fFlagsLocked = objLockFlags())
        {
            ulrc = _flObject;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        objUnlockFlags();

    return ulrc;
}

/*
 *@@ xwpModifyFlags:
 *      this modifies the current list-notify flags for the
 *      current object in an atomic operation.
 *
 *      List notification flags are a new object feature
 *      with XWorkplace. These are intended to implement
 *      object-granular functions when an object has been
 *      stored in some XWP-internal list which needs to
 *      be updated in certain cases.
 *
 *      Current flags are:
 *
 *      --  OBJLIST_RUNNINGSTORED: this means that the
 *          object has been given in-use emphasis by
 *          progOpenProgram. This can happen to program
 *          objects (WPProgram or WPProgramFile) or
 *          data files. If the object is destroyed
 *          for whatever reason, the object needs to
 *          be removed from the list maintained by
 *          progOpenProgram because otherwise we'd
 *          run into problems when WM_APPTERMINATENOTIFY
 *          comes in.
 *
 *      --  OBJLIST_CONFIGFOLDER: this means that the
 *          object is a config folder or resides in
 *          one. The config folder cache must be invalidated
 *          when this object gets deleted.
 *
 *      --  OBJLIST_FAVORITEFOLDER: object is a folder
 *          on the "favorite folders" list.
 *
 *      --  OBJLIST_QUICKOPENFOLDER: object is a folder
 *          on the "quick-open folders" list.
 *
 *      --  OBJLIST_HANDLESCACHE: object is in handles
 *          cache (see objFindObjFromHandle).
 *
 *      --  OBJLIST_QUERYAWAKEFSOBJECT: object is in root
 *          folders list or has been touched by that cache
 *          (fdrRegisterAwakeRootFolder).
 *
 *      --  OBJLIST_IMAGECACHE: object is a WPImageFile
 *          that has been used as a folder background
 *          in folder split view.
 *
 *      Note: These flags are NOT persistent across
 *      reboots, i.e. not stored with wpSaveState.
 *      They are also cleared for the copy if the
 *      object gets copied.
 *
 *      Use this method if you need to modify single flags
 *      while keeping others (instead of first querying and
 *      then setting the new flags). This function is an
 *      atomic operation, similar to wpModifyStyle.
 *
 *      This operates bit-wise. To set a flag, set it in
 *      both flNotifyFlags and flNotifyMask. To clear a
 *      flag, set it in flNotifyFlags only.
 *
 *      For example, to set FLAG1 and clear FLAG2, call:
 +
 +          _xwpModifyFlags(...,
 +                          FLAG1 | FLAG2,     // affected flags
 +                          FLAG1);            // flags to set (i.e. clear FLAG2)
 *
 *@@added V0.9.6 (2000-10-23) [umoeller]
 *@@changed V1.0.0 (2002-09-02) [umoeller]: now using new flags mutex to avoid deadlocks
 */

SOM_Scope BOOL  SOMLINK xo_xwpModifyFlags(XFldObject *somSelf,
                                          ULONG flFlags,
                                          ULONG flMask)
{
    BOOL    brc = FALSE,
            fFlagsLocked = FALSE;

    XFldObjectMethodDebug("XFldObject","xo_xwpModifyFlags");

    TRY_LOUD(excpt1)
    {
        XFldObjectData *somThis = XFldObjectGetData(somSelf);

        // V1.0.0 (2002-09-02) [umoeller]
        // I have introduced a new mutex for the object flags
        // here. The mutex is global and thus protects the
        // object flags of all objects. We used to request
        // the object mutex here, but according to Knut's
        // research, this appeared to cause deadlocks in
        // certain situations.
        // The scenario for the instl001.cmd hangs was that
        // two threads worked on the same object, but
        // requested mutexes in reverse order:
        // -- Thread 7 was working on wpSaveImmediate. It was holding
        //    the object's mutex and called XFldObject::wpSaveImmediate,
        //    which called objRemoveFromDirtyList. It blocks on
        //    requesting the "dirty objects" mutex, which is held
        //    by thread 13.
        // -- Thread 13 was deleting the object at the same time.
        //    wpFree called wpSaveImmediate too, which called
        //    objRemoveFromDirtyList as well. It stops in this method
        //    on the _wpRequestObjectMutex, which is held by thread 7.

        // So get rid of the object mutex in this method, which
        // gets called from all over the place. I suspect this
        // will also fix the deadlocks with the lazy icons, which
        // set flags too.

        if (fFlagsLocked = objLockFlags())
        {
            _flObject     = (
                                // copy all unaffected
                                (_flObject & ~flFlags)
                                // OR with masked new ones
                              | (flFlags & flMask)
                            );
            brc = TRUE;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        objUnlockFlags();

    return brc;
}

/*
 *@@ xwpAddedToList:
 *      this new instance method gets called whenever an
 *      object has been added to a linked list represented
 *      by an XWPObjList object.
 *
 *      This method must then perform reference counting
 *      so that the list can be cleaned up when the object
 *      goes dormant.
 *
 *      When the method gets called, the object is already
 *      in the list, and all object lists are globally
 *      locked.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope void  SOMLINK xo_xwpAddedToList(XFldObject *somSelf,
                                          XWPObjList* pList)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpAddedToList");

    if (    (_pvllLists)
         || (_pvllLists = lstCreate(FALSE))
       )
    {
        lstAppendItem(_pvllLists, pList);

        PMPF_OBJLISTS(("[%s] added to list 0x%lX, now %d items",
                        _wpQueryTitle(somSelf),
                        pList,
                        _Count(pList)));
    }
}

/*
 *@@ xwpRemovedFromList:
 *      this new instance method gets called whenever an
 *      object has been removed from a linked list represented
 *      by an XWPObjList object.
 *
 *      This method must then undo reference counting
 *      previously performed in XFldObject::xwpAddedToList.
 *
 *      When the method gets called, the object is already
 *      removed from the list, and all object lists are globally
 *      locked.
 *
 *@@added V1.0.1 (2002-12-11) [umoeller]
 */

SOM_Scope void  SOMLINK xo_xwpRemovedFromList(XFldObject *somSelf,
                                              XWPObjList* pList)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpRemovedFromList");

    if (_pvllLists)
    {
        lstRemoveItem(_pvllLists, pList);

        PMPF_OBJLISTS(("[%s] removed from list 0x%lX, now %d items",
                        _wpQueryTitle(somSelf),
                        pList,
                        _Count(pList)));
    }
}

/*
 *@@ xwpPrepareInsertRecord:
 *      this new XFldObject instance method gets called from
 *      our XFldObject::wpCnrInsertObject override to set up
 *      the MINIRECORDCORE of an object just before the object
 *      actually gets inserted into a container.
 *
 *      If no error occurred, this returns a new USEITEM with
 *      the RECORDITEM following, which is allocated from
 *      the WPS heap.
 *
 *      This method can be overridden by subclasses to fix
 *      up the MINIRECORDCORE.flRecordAttr flag bits. The
 *      XFldObject version of this method sets these up
 *      according to the object style and state flags, but
 *      a subclass could choose to flip on the CRA_OWNERDRAW
 *      bit so that it will receive the
 *      XFldObject::xwpOwnerDrawIcon method call to paint itself.
 *
 *      All the parameters could be retrieved via method calls
 *      or instance data hacks, but are passed to this method
 *      for efficiency. In detail:
 *
 *      --  pmrc is what _wpQueryCoreRecord would return,
 *          where ptlIcon and pszIcon have already been
 *          set;
 *
 *      --  flObjectStyle is the true object style (while
 *          the _wpQueryStyle implementation of a subclass
 *          could elect to manipulate some flags);
 *
 *      --  fFirstInsert is TRUE if the object is inserted
 *          into a container for the very first time.
 *
 *      If you override this method for a subclass, be sure
 *      to call the parent first and only _then_ manipulate
 *      data.
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

SOM_Scope PUSEITEM  SOMLINK xo_xwpPrepareInsertRecord(XFldObject *somSelf,
                                                      HWND hwndCnr,
                                                      PMINIRECORDCORE pmrc,
                                                      ULONG flObjectStyle,
                                                      BOOL fFirstInsert)
{
    PUSEITEM    pui;
    ULONG       flRecordAttr;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpPrepareInsertRecord");

    #define CBITEM (sizeof(USEITEM) + sizeof(RECORDITEM))

    // use the WPS heap for the USEITEM!
    if (pui = (PUSEITEM)ShlAllocMem(CBITEM, NULL))
    {
        // RECORDITEM comes right after USEITEM
        PRECORDITEM pri = (PRECORDITEM)(pui + 1);

        // 1) set up the USEITEM and RECORDITEM

        pui->type = USAGE_RECORD;
        pri->hwndCnr = hwndCnr;
        pri->pRecord = pmrc;
        pri->ulUser = 0;        // "for application use"?!?

        // 2) fill the MINIRECORDCORE fields

        pmrc->cb = sizeof(MINIRECORDCORE);

        /* #define TESTFL(f) ((pmrc->flRecordAttr & f) ? # f ## " " : "")
        _PmpfF(("[%s] flRecordAttr first: 0x%lX (%s%s%s%s%s)",
                pmrc->pszIcon,
                pmrc->flRecordAttr,
                TESTFL(CRA_SELECTED),
                TESTFL(CRA_SOURCE),
                TESTFL(CRA_EXPANDED),
                TESTFL(CRA_FILTERED),
                TESTFL(CRA_LOCKED) )); */

        // add "in use" emphasis if we have an open view
        // already; we never clear this flag afterwards,
        // wpAddToObjUseList and wpDeleteFromObjUseList
        // take care of that automatically when they find
        // VIEWITEM's
        if (    (fFirstInsert)
             && (_wpFindUseItem(somSelf, USAGE_OPENVIEW, NULL))
           )
            pmrc->flRecordAttr  |= CRA_INUSE;

        // in this ULONG, we accumulate the bits that we
        // want to see set in the record's flRecordAttr;
        // NOTE, if you OR something to this flag, make
        // sure it's also cleared in the big bit manipulation
        // below
        flRecordAttr = 0;

        // now, from my testing, on the first time this gets
        // called for an object, all flRecordAttr fields are
        // clear, but to be on the safe side, we check or clear
        // them all here

        if (flObjectStyle & OBJSTYLE_LOCKEDINPLACE)
            flRecordAttr |= CRA_LOCKED;

        if (flObjectStyle & OBJSTYLE_NORENAME)
            flRecordAttr |= CRA_RECORDREADONLY;

        if (    (flObjectStyle & OBJSTYLE_TEMPLATE)
             || objIsAShadow(somSelf)
           )
            flRecordAttr |= CRA_OWNERDRAW;

        // if we have the "not visible" style, always hide
        if (flObjectStyle & OBJSTYLE_NOTVISIBLE)
            flRecordAttr |= CRA_FILTERED;
        else
            // the WPS apparently caches the "filtered" status
            // of the object in a private bit in the object
            // instance data; it appears that this is what
            // the undocumented wpXXXState methods return
            // (see IBMOBJECTDATA in object.h)... so, on
            // the first call, test the folder's filter
            if (fFirstInsert)
            {
                WPFolder *pFolder;
                ULONG flState = 0;

                if (    (pFolder = _wpQueryFolder(somSelf))
                     && (fdrIsObjectFiltered(pFolder, somSelf))
                   )
                {
                    flRecordAttr |= CRA_FILTERED;
                    flState = STATEFL_FILTERED;
                }

                _wpModifyState(somSelf,
                               STATEFL_FILTERED,
                               flState);
            }
            else
                // subsequent calls: use the cached value
                if (_wpQueryState(somSelf) & STATEFL_FILTERED)
                    flRecordAttr |= CRA_FILTERED;

        pmrc->flRecordAttr =  (    pmrc->flRecordAttr
                                // clear flags that we processed
                                & ~ (   CRA_LOCKED
                                      | CRA_RECORDREADONLY
                                      | CRA_OWNERDRAW
                                      | CRA_FILTERED
                                      // and clear these always
                                      | CRA_SELECTED
                                      | CRA_SOURCE
                                      | CRA_EXPANDED
                                    )
                              ) // and set those we determined above
                              | flRecordAttr
             // in any case, the half-documented CRA_OWNERFREE flag
             // MUST be set for our records because otherwise the
             // container will try to free it, but it's been
             // allocated by the WPS itself
                              | CRA_OWNERFREE
                              | CRA_COLLAPSED;
    }

    return pui;
}

/*
 *@@ xwpOwnerDrawIcon:
 *      this new XFldObject instance method gets called from the
 *      XWorkplace container owner-draw routines if global
 *      settings are enabled that require XWorkplace to take
 *      over owner draw for specific objects _and_ if this object's
 *      MINIRECORDCORE.flRecordAttr has the CRA_OWNERDRAW bit set.
 *
 *      Per default, the WPS enables that flag for folders and
 *      shadows only. If that flag is not set, the container itself
 *      does the painting (using MINIRECORDCORE.hptrIcon), and this
 *      method never gets called.
 *
 *      This is why XWorkplace now also introduces the
 *      XFldObject::xwpPrepareInsertRecord method; seee remarks
 *      there.
 *
 *      If that bit is set, this method gets called every time the
 *      icon needs to be painted for the given object. See the
 *      remarks below for how to defer icon loading lazily.
 *
 *      Parameters:
 *
 *      --  pmrc has the MINIRECORDCORE that was given into
 *          the owner-draw routine. You must check for
 *          whether hptrIcon is set.
 *
 *      --  hps is the presentation space handle that you
 *          must use for drawing. This has already been
 *          switched to RGB mode.
 *
 *      --  flOwnerDraw has a set of flags:
 *
 *          --  If OWDRFL_LAZYLOADICON is set, this means that
 *              the lazy icons thread is active and you may
 *              return OWDRFL_LAZYLOADICON from this method to
 *              queue the icon for lazy drawing (see below).
 *
 *          --  If OWDRFL_SHADOWOVERLAY is set, the "shadow
 *              overlay" setting is active, that is, shadows
 *              should be overpainted with an additional
 *              shadow icon.
 *
 *          --  If OWDRFL_INUSE is set, the object currently
 *              has a view open and the icon will receive in-use
 *              (hatched) emphasis. Note that XWorkplace has
 *              already painted in-use emphasis before calling
 *              this method, so this method is not required to
 *              do this; however the flag is necessary for
 *              folders to correctly figure out when to paint
 *              the animation icon instead of the regular folder
 *              icon.
 *
 *          --  If OWDRFL_MINI is set, this method must paint
 *              a mini-icon instead of a regular icon. This
 *              gets set in two cases: the container has been
 *              set to use mini-icons (which is always the
 *              case for Details view), or the icon is to
 *              be painted for a template. In the latter case,
 *              the template icon has already been painted
 *              before calling this method, and pptl->x and y
 *              have been adjusted.
 *
 *          --  If OWDRFL_RGBMODE is set, the given HPS has
 *              already been switched to RGB mode. If you
 *              only paint an icon or bitmap, this makes no
 *              difference, but if you draw lines or something,
 *              you might need to call GpiCreateLogColorTable.
 *
 *      --  prcl contains the coordinates at which to paint
 *          the icon in this method. This rectangle has been
 *          set to _exactly_ the coordinates at which to
 *          paint the icon, so there is no need for additional
 *          centering etc. This takes mini-icons, different cnr
 *          views and templates into account automatically.
 *
 *      You must return the following flags:
 *
 *      --  OWDRFL_LAZYLOADICON: if this bit is set in the return
 *          value, the object is queued for lazy icon loading.
 *          That is, somSelf is passed to the XWorkplace lazy
 *          icons thread, which will then invoke wpQueryIcon
 *          on the object later (which should retrieve the correct
 *          icon for the object). Eventually this will cause
 *          this method to be called again when that new icon
 *          needs to be painted, so you must check for whether
 *          pmrc->hptrIcon is currently set in this method's code
 *          to tell the two cases apart.
 *
 *      --  OWDRFL_RGBMODE: if this bit is set, XWorkplace
 *          assumes that the HPS is in RGB mode on exit and
 *          will reset the HPS to non-rgb mode before returning
 *          from its owner-draw routine. This is very important
 *          because the PM container expects the HPS _not_ to
 *          be in RGB mode when an owner-draw routine exits
 *          and will paint garbage otherwise. You must
 *          therefore copy this flag from the input flOwnerDraw
 *          bits unless you change the RGB mode of the HPS.
 *
 *      As a result, the usual sequence for lazy icon loading is
 *      this:
 *
 *      1)  Check that OWDRFL_LAZYLOADICON is set. If not,
 *          call wpQueryIcon to load the icon synchronously.
 *
 *      2)  Otherwise, if pmrc->hptrIcon is NULLHANDLE, paint
 *          the class default icon and set the OWDRFL_LAZYLOADICON
 *          in the return value. This means that the object gets
 *          painted the first time after populate and the icon
 *          has not yet been determined.
 *
 *      3)  After returning from this method, the object gets
 *          queued for lazy icon loading, which ends up in
 *          a wpQueryIcon call on the lazy icons thread.
 *          When wpQueryIcon calls wpSetIcon on the object,
 *          this method gets called again because the icon
 *          has been invalidated.
 *
 *      4)  Now pmrc->hptrIcon is _not_ NULLHANDLE, so paint
 *          that (correct) icon then.
 *
 *@@added V1.0.1 (2002-11-30) [umoeller]
 *@@changed V1.0.1 (2003-01-25) [umoeller]: fixed problems with animation icons of folder shadows @@fixes 329
 */

SOM_Scope ULONG SOMLINK xo_xwpOwnerDrawIcon(XFldObject *somSelf,
                                            PMINIRECORDCORE pmrc,
                                            HPS hps,
                                            ULONG flOwnerDraw,
                                            PRECTL prcl)
{
    // return flags: preserve the OWDRFL_RGBMODE from input
    ULONG       flReturn = (flOwnerDraw & OWDRFL_RGBMODE);

    ULONG       flObject = objQueryFlags(somSelf);
    HPOINTER    hptrPaint;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpOwnerDrawIcon");

    // check if we have an icon already or if this
    // is the first call for this object... in that
    // case hptrIcon might be NULLHANDLE still
    if (!(hptrPaint = pmrc->hptrIcon))
    {
        PMPF_ICONREPLACEMENTS(("    CMA_ICON, pmrc->hptrIcon is NULLHANDLE"));

        // this object does not have an icon yet:
        // lazy icons enabled?
        if (    (flOwnerDraw & OWDRFL_LAZYLOADICON)
             && (flObject & (OBJFL_WPDATAFILE | OBJFL_WPPROGRAM))
                                 // ^^ this includes WPProgramFile
           )
        {
            // lazy icon drawing:
            // use default class icon for now and
            // queue object for lazy icon processing
            hptrPaint = _wpclsQueryIcon(_somGetClass(somSelf));
            flReturn |= OWDRFL_LAZYLOADICON;
        }
        else
            // no data or program file, or lazy icons disabled:
            // get the icon synchronously
            hptrPaint = _wpQueryIcon(somSelf);
                    // this should set MINIRECORDCORE.hptrIcon
    }
    else
    {
        // pmrc->hptrIcon was set: still check if this is
        // a folder (cannot happen presently because they
        // are not owner-drawn) or a shadow to a folder;
        // in that case, we need to draw an animation icon
        // if the folder has an open view... pmrc->hptrIcon
        // ALWAYS has the closed icon for folders!
        WPObject *pobjTest = somSelf;

        if (    (    (flObject & OBJFL_WPFOLDER)
                  || (    (pobjTest = _xwpResolveIfLink(somSelf))
                       && (objIsAFolder(pobjTest))
                     )
                )
             // && (_wpFindViewItem(pobjTest, VIEW_ANY, NULL))
             // no, no, no! _wpFindViewItem requests the object mutex
             // if the current thread doesn't have it, which can deadlock
             // the system if a shadow is just being created!
             // so use CRA_INUSE instead, which should be correct
             // V1.0.0 (2002-09-17) [umoeller]
             && (flOwnerDraw & OWDRFL_INUSE) // poi->fsAttribute & CRA_INUSE)
           )
            if (!(hptrPaint = _wpQueryIconN(pobjTest, 1)))
                // folder has no animation icon:
                // V1.0.1 (2003-01-25) [umoeller] @@fixes 329
                hptrPaint = pmrc->hptrIcon;
        // else: leave hptrPaint with pmrc->hptrIcon
    }

    WinDrawPointer(hps,
                   prcl->xLeft,
                   prcl->yBottom,
                   hptrPaint,
                   (flOwnerDraw & OWDRFL_MINI)
                        ? DP_MINI
                        : DP_NORMAL);

    return flReturn;
}

/*
 *@@ xwpLazyLoadIcon:
 *      this new XFldObject instance method gets called on
 *      the lazy icon thread if XFldObject::xwpOwnerDrawIcon
 *      returned the OWDRFL_LAZYLOADICON bit set. This must
 *      then load the icon of the object and invalidate the
 *      object in all containers where it is currently
 *      inserted.
 *
 *      The XFldObject implementation simply calls wpQueryIcon
 *      and then wpSetIcon, which goes through all the
 *      class-specific overhead of determining the correct
 *      icon for the object.
 *
 *      However, this method can be overridden by subclasses
 *      that implement owner-draw icon/bitmap painting, for
 *      example to produce thumbnails. XFldDataFile abuses
 *      this method to implement its own system for that
 *      with its new XFldDataFile::xwpLazyLoadThumbnail
 *      method.
 *
 *      Parameters:
 *
 *      --  phpsMem points to a HPS containing a memory
 *          presentation space or NULLHANDLE if the lazy
 *          load thread has not yet created a memory presentation
 *          space. If you need a memory PS for bitmaps work,
 *          create a memory PS and store the handle here if
 *          it is NULLHANDLE. Otherwise resize the memory
 *          PS using GpiSetPS to the size of the bitmap file
 *          that you are loading. In any case, access to this
 *          method is serialized so you can safely use this
 *          buffer.
 *
 *      --  flOwnerDraw contains the current OWDRFL_* settings.
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

SOM_Scope void  SOMLINK xo_xwpLazyLoadIcon(XFldObject *somSelf,
                                           ULONG flOwnerDraw,
                                           BOOL* pbQuitEarly)
{
    HPOINTER hptr;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpLazyLoadIcon");

    PMPF_ICONREPLACEMENTS(("[%s] calling _wpQueryIcon, hptrIcon 0x%lX, OBJSTYLE_NOTDEFAULTICON: %lX",
                _wpQueryTitle(somSelf),
                _wpQueryCoreRecord(somSelf)->hptrIcon,
                _wpQueryStyle(somSelf) & OBJSTYLE_NOTDEFAULTICON
              ));

    if (hptr = _wpQueryIcon(somSelf))
        _wpSetIcon(somSelf, hptr);

    PMPF_ICONREPLACEMENTS(("    loaded hptr 0x%lX", hptr));
}

/*
 *@@ xwpForceRepaint:
 *      this new XFldObject method repaints the object in
 *      all containers where it is currently inserted.
 *
 *      With flRefresh, pass in the following flags:
 *
 *      --  REFRESH_RECORDREPAINT: invalidate the object's
 *          record in all containers where it is currently
 *          inserted.
 *
 *      --  REFRESH_RECORDICON: the object's icon has changed;
 *          in conjunction with REFRESH_RECORDREPAINT,
 *          REFRESH_SHADOWS, and REFRESH_WINDOWS, this
 *          determines the messages that are sent.
 *
 *      --  REFRESH_RECORDTEXT: the object's title has changed;
 *          in conjunction with REFRESH_RECORDREPAINT,
 *          REFRESH_SHADOWS, and REFRESH_WINDOWS, this
 *          determines the messages that are sent.
 *
 *      --  REFRESH_SHADOWS: update awake shadows pointing
 *          to this object with the new icon and/or the
 *          new title.
 *
 *      --  REFRESH_WINDOWS: update open views of this
 *          object with either the new icon and/or the
 *          new title.
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

SOM_Scope void  SOMLINK xo_xwpForceRepaint(XFldObject *somSelf,
                                           ULONG flRefresh)
{
    BOOL    fLocked = FALSE;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpForceRepaint");

    TRY_LOUD(excpt1)
    {
        PIBMOBJECTDATA  pod;
        PMINIRECORDCORE pmrc;

        if (    (pod = (PIBMOBJECTDATA)_pvWPObjectData)
             && (pmrc = pod->pmrc)
             && (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
           )
        {
            PUSEITEM    pui;
            LINKLIST    llShadows,
                        llWindows,
                        llCnrs;
            PLISTNODE   pNode;
            ULONG       flCnrInv;
            lstInit(&llShadows, FALSE);
            lstInit(&llWindows, FALSE);
            lstInit(&llCnrs, FALSE);

            for (pui = pod->puiFirst;
                 pui;
                 pui = pui->pNext)
            {
                switch (pui->type)
                {
                    case USAGE_LINK:
                        if (flRefresh & REFRESH_SHADOWS)
                        {
                            PLINKITEM pli = (PLINKITEM)(pui + 1);
                            WPShadow *pShadow;
                            if (pShadow = pli->LinkObj)
                                lstAppendItem(&llShadows, pShadow);
                        }
                    break;

                    case USAGE_OPENVIEW:
                        if (flRefresh & REFRESH_WINDOWS)
                        {
                            PVIEWITEM   pvi = (PVIEWITEM)(pui + 1);
                            HWND        hwndView;
                            // this can also be a HAPP, so check if this really
                            // is a window
                            if (    (hwndView = pvi->handle)
                                 && (WinIsWindow(0, // G_habThread1,
                                                 hwndView))
                               )
                                lstAppendItem(&llWindows, (PVOID)hwndView);
                        }
                    break;

                    case USAGE_RECORD:
                        if (flRefresh & REFRESH_RECORDREPAINT)
                        {
                            PRECORDITEM pri = (PRECORDITEM)(pui + 1);
                            lstAppendItem(&llCnrs, (PVOID)pri->hwndCnr);
                        }
                    break;
                }
            }

            _wpReleaseObjectMutexSem(somSelf);
            fLocked = FALSE;

            // now process the stuff outside the mutex

            FOR_ALL_NODES(&llShadows, pNode)
            {
                if (flRefresh & REFRESH_RECORDICON)
                    _wpSetIcon((WPShadow*)pNode->pItemData,
                               pmrc->hptrIcon);

                if (flRefresh & REFRESH_RECORDTEXT)
                    _wpSetShadowTitle((WPShadow*)pNode->pItemData,
                                      pmrc->pszIcon);

            }

            FOR_ALL_NODES(&llWindows, pNode)
            {
                HWND hwndView = (HWND)pNode->pItemData;

                if (flRefresh & REFRESH_RECORDTEXT)
                {
                    HWND hwndTitleBar;
                    // in "text changed" mode, refresh the title bar
                    if (    (hwndTitleBar = WinWindowFromID(hwndView,
                                                            FID_TITLEBAR))
                         && (WinSetWindowText(hwndTitleBar,
                                              pmrc->pszIcon))
                       )
                        // update the switch handle also, if we have one
                        winhUpdateTasklist(hwndView,
                                           pmrc->pszIcon);
                }

                if (flRefresh & REFRESH_RECORDICON)
                {
                    HWND hwndSysMenu;
                    if (hwndSysMenu = WinWindowFromID(hwndView,
                                                      FID_SYSMENU))
                        WinSendMsg(hwndView,
                                   WM_SETICON,
                                   (MPARAM)pmrc->hptrIcon,
                                   0);
                }
            }

            if (flRefresh & REFRESH_RECORDTEXT)
                flCnrInv = CMA_TEXTCHANGED;
            else
                flCnrInv = CMA_NOREPOSITION | CMA_NOTEXTCHANGED;

            FOR_ALL_NODES(&llCnrs, pNode)
            {
                HWND hwndCnr = (HWND)pNode->pItemData;
                // refresh the record in the useitem;
                // the WPS shares records between containers
                WinSendMsg(hwndCnr,
                           CM_QUERYRECORDINFO,
                           (MPARAM)&pmrc,
                           (MPARAM)1);

                // and refresh the view
                WinSendMsg(hwndCnr,
                           CM_INVALIDATERECORD,
                           (MPARAM)&pmrc,
                           MPFROM2SHORT(1,
                                        flCnrInv));
            }
        }
    }
    CATCH(excpt1)
    {
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);
}

/*
 *@@ xwpAddWidgetNotify:
 *      this new XFldObject instance method adds a widget
 *      window handle to the object's internal widget-notify
 *      list.
 *
 *      Widget notifies are used to establish a link
 *      between a Desktop object and an object button widget
 *      in the XCenter. This will post a WM_CONTROL
 *      message to the given HWND in the following
 *      situations:
 *
 *      --  When the object is destroyed for any reason
 *          (i.e. when it is deleted or made dormant), the
 *          specified HWND is posted (!) a WM_CONTROL message
 *          with the XN_OBJECTDESTROYED notify code.
 *
 *          Any XCenter widget that uses an object pointer
 *          internally (such as the built-in object button
 *          widget) should use this method to destroy itself
 *          when its related object gets destroyed.
 *
 *      --  When the object's in-use emphasis changes
 *          (CRA_INUSE), XFldObject::wpCnrSetEmphasis
 *          posts a WM_CONTROL message with the
 *          XN_INUSECHANGED notify code.
 *
 *      Use XFldObject::xwpRemoveDestroyNotify to remove
 *      the notification again... e.g. when the widget
 *      itself is destroyed.
 *
 *@@added V0.9.7 (2001-01-03) [umoeller]
 *@@changed V0.9.13 (2001-06-21) [umoeller]: renamed from xwpAddDestroyNotify
 *@@changed V1.0.0 (2002-09-02) [umoeller]: now using new flags mutex to avoid deadlocks
 */

SOM_Scope BOOL  SOMLINK xo_xwpAddWidgetNotify(XFldObject *somSelf,
                                              HWND hwnd)
{
    BOOL    brc = FALSE,
            fFlagsLocked = FALSE;

    XFldObjectMethodDebug("XFldObject","xo_xwpAddWidgetNotify");

    TRY_LOUD(excpt1)
    {
        if (fFlagsLocked = objLockFlags())
        {
            XFldObjectData *somThis = XFldObjectGetData(somSelf);
            if (_pvllWidgetNotifies == NULL)
                // list not created yet: do it now
                _pvllWidgetNotifies = lstCreate(FALSE);     // no auto-free
            else
                // list exists:
                // make sure it's not in the list yet
                // V0.9.13 (2001-06-21) [umoeller]
                if (lstIndexFromItem(_pvllWidgetNotifies,
                                     (PVOID)hwnd)
                        != -1)
                {
                    // exists already:
                    brc = TRUE;
                }

            if ((_pvllWidgetNotifies) && (!brc))
                lstAppendItem((PLINKLIST)_pvllWidgetNotifies,
                              (PVOID)hwnd);

            brc = TRUE;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        objUnlockFlags();

    return brc;
}

/*
 *@@ xwpRemoveDestroyNotify:
 *      the reverse to XFldObject::AddWidgetNotify.
 *
 *@@added V0.9.7 (2001-01-03) [umoeller]
 *@@changed V1.0.0 (2002-09-02) [umoeller]: now using new flags mutex to avoid deadlocks
 */

SOM_Scope BOOL  SOMLINK xo_xwpRemoveDestroyNotify(XFldObject *somSelf,
                                                  HWND hwnd)
{
    BOOL    brc = FALSE,
            fFlagsLocked = FALSE;
    XFldObjectMethodDebug("XFldObject","xo_xwpRemoveDestroyNotify");

    TRY_LOUD(excpt1)
    {
        if (fFlagsLocked = objLockFlags())
        {
            XFldObjectData *somThis = XFldObjectGetData(somSelf);
            if (_pvllWidgetNotifies)
            {
                lstRemoveItem((PLINKLIST)_pvllWidgetNotifies,
                              (PVOID)hwnd);
            }

            brc = TRUE;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        objUnlockFlags();

    return brc;
}

/*
 *@@ xwpQueryObjectHotkey:
 *      this new XFldObject instance method returns the global
 *      object hotkey which has been defined to be monitored for
 *      in the XWorkplace hook, if any.
 *
 *      If this object has been assigned a hotkey to, TRUE is
 *      returned, and the hotkey data is stored in the OBJECTHOTKEY
 *      struct.
 *
 *      xfobj.idl defines OBJECTHOTKEY as follows:
 *
 +          struct OBJECTHOTKEY
 +          {
 +              USHORT  usFlags;
 +              UCHAR   ucScanCode;
 +              USHORT  usKeyCode;
 +          };
 *
 *      usFlags contains the flags for the fsFlags parameter of
 *      the WM_CHAR message (SHORT1FROMMP(mp1)). Those flags have
 *      been filtered. See GLOBALHOTKEY for the valid flags.
 *
 *      ucScanCode contains the hardware scan code which is used
 *      to identify the hotkey in the XWorkplace hook. Since the
 *      char and virtual codes are not valid when a VIO session
 *      currently has the input focus, only the scan code can be
 *      used to identify keystrokes in the hook.
 *
 *      If usFlags has the KC_VIRTUALKEY flag set, usKeyCode has
 *      the usvk parameter of WM_CHAR; otherwise, it has the usch
 *      parameter (SHORT1/2FROMMP(mp2)). This will only be used to
 *      be able to describe the key on the dialogs, not to identify
 *      keystrokes in the XWorkplace hook.
 *
 *      If no hotkey has been assigned, FALSE is returned only.
 *
 *@@added V0.9.0 [umoeller]
 *@@changed V0.9.16 (2001-10-15) [umoeller]: switched prototype to using struct
 */

SOM_Scope BOOL  SOMLINK xo_xwpQueryObjectHotkey(XFldObject *somSelf,
                                                XFldObject_POBJECTHOTKEY pHotkey)
{
    XFldObjectMethodDebug("XFldObject","xo_xwpQueryObjectHotkey");

    return objQueryObjectHotkey(somSelf, pHotkey);
}

/*
 *@@ xwpSetObjectHotkey:
 *      this new XFldObject instance method sets a new global
 *      object hotkey for the object.
 *
 *      See XFldObject::xwpQueryObjectHotkey for the description
 *      of the hotkey parameters.
 *
 *      If the object was given a hotkey previously, the
 *      hotkey definition is overwritten.
 *
 *      If another object had the same object hotkey, the
 *      other object will lose its hotkey definition,
 *      because only each object must have a unique hotkey
 *      definition, if any.
 *
 *      As a special exception, if the pHotkey pointer is
 *      NULL, the hotkey for the object is removed from
 *      the internal hotkeys list (i.e. somSelf loses its
 *      hotkey).
 *
 *      The XWorkplace hook is automatically notified of the
 *      change, so that the new hotkey definition takes effect
 *      immediately.
 *
 *@@added V0.9.0 [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_xwpSetObjectHotkey(XFldObject *somSelf,
                                              XFldObject_POBJECTHOTKEY pHotkey)
{
    XFldObjectMethodDebug("XFldObject","xo_xwpSetObjectHotkey");

    return objSetObjectHotkey(somSelf, pHotkey);
}

/*
 *@@ xwpQuerySetup:
 *      this new XFldObject instance method composes a setup
 *      string for the given object which contains all non-default
 *      settings for this object.
 *
 *      This method is the reverse to the wpSetup method.
 *      The settings string returned by this method can be
 *      passed to wpSetup (or WinSetObjectData) to restore
 *      the setting for this object. Unfortunately, IBM
 *      has never implemented a "query setup" method, so
 *      I did this now.
 *
 *      Notes:
 *
 *      --  Starting with V0.9.16, this returns a string
 *          in a new buffer. If pulLength is != NULL, it
 *          receives the length of the string that was
 *          composed, excluding the null terminator.
 *
 *          Call XFldObject::xwpFreeSetupBuffer to free
 *          the return value.
 *
 *      --  This only returns setup strings which have
 *          non-default values. Since it can be hard to
 *          find out what the "default" really is for a
 *          setting, the values may differ between system
 *          reboots.
 *
 *      --  This method only resolves the method pointer for
 *          the "xwpQuerySetup2" method, which must to the actual
 *          setup string composing. See XFldObject::xwpQuerySetup2
 *          for details.
 *
 *      --  Never override this method for subclasses;
 *          override xwpQuerySetup2 instead.
 *
 *@@added V0.9.1 (2000-01-16) [umoeller]
 *@@changed V0.9.12 (2001-05-19) [umoeller]: added object lock
 *@@changed V0.9.16 (2001-10-11) [umoeller]: changed implementation to using XSTRINGs
 *@@changed V1.0.1 (2002-12-08) [umoeller]: now calling parent methods directly
 */

SOM_Scope PSZ  SOMLINK xo_xwpQuerySetup(XFldObject *somSelf,
                                        PULONG pulLength)
{
    PSZ pszReturn = NULL;
    XSTRING str;
    WPObject *pobjLock = NULL;

    XFldObjectMethodDebug("XFldObject","xo_xwpQuerySetup");

    xstrInit(&str, 500);

    TRY_LOUD(excpt1)
    {
        if (pobjLock = cmnLockObject(somSelf))
        {
            if (    (_xwpQuerySetup2(somSelf, &str))
                 && (str.ulLength)
               )
            {
                pszReturn = str.psz;
                        // do not free
                if (pulLength)
                    *pulLength = str.ulLength;
            }
            else
                xstrClear(&str);

            /* V1.0.1 (2002-12-08) [umoeller]

            // obtain "xwpQuerySetup2" method pointer
            somTD_XFldObject_xwpQuerySetup2 pfn_xwpQuerySetup2;

            if (pfn_xwpQuerySetup2 = (somTD_XFldObject_xwpQuerySetup2)somResolveByName(
                                                    somSelf,
                                                    "xwpQuerySetup2"))
            {
                // method resolved: call it
                if (    (pfn_xwpQuerySetup2(somSelf, &str))
                     && (str.ulLength)
                   )
                {
                    pszReturn = str.psz;
                            // do not free
                    if (pulLength)
                        *pulLength = str.ulLength;
                }
                else
                    xstrClear(&str);
            } */
        }
    }
    CATCH(excpt1)
    {
        // crash:
        xstrClear(&str);
        pszReturn = NULL;
    } END_CATCH();

    if (pobjLock)
        _wpReleaseObjectMutexSem(pobjLock);

    return pszReturn;
}

/*
 *@@ xwpFreeSetupBuffer:
 *      this new XFldObject instance method frees the string
 *      buffer returned by XFldObject::xwpQuerySetup.
 *
 *@@added V0.9.16 (2001-10-11) [umoeller]
 */

SOM_Scope void  SOMLINK xo_xwpFreeSetupBuffer(XFldObject *somSelf,
                                              PSZ pszSetupBuffer)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpFreeSetupBuffer");

    TRY_LOUD(excpt1)
    {
        if (pszSetupBuffer)
            free(pszSetupBuffer);
    }
    CATCH(excpt1) {} END_CATCH();
}

/*
 *@@ xwpQuerySetup2:
 *      this is the implementation for querying setup strings
 *      which gets called by XFldObject::xwpQuerySetup.
 *
 *      This has been placed into a separate method because this
 *      method is designed to be overridden by subclasses,
 *      while xwpQuerySetup is not.
 *
 *      In other words:
 *
 *      --  _call_ xwpQuerySetup for getting a setup string.
 *
 *      --  _override_ xwpQuerySetup2 for adding setup-string support
 *          to your class.
 *
 *      Guidelines:
 *
 *      1.  You must call the parent method _after_ your implementation
 *          to make sure XFldObject gets called last, because the OBJECTID
 *          setup string must be added at the very last position in the
 *          complete setup string (IBM says), and that string is implemented
 *          by the original XFldObject method.
 *
 *      2.  There used to be a difficult overhead associated with
 *          calling the parent method, because previously the
 *          SOM-generated header files for XWorkplace were not
 *          aware of our class replacements (that is, you could not
 *          simply call parent_xwpQuerySetup2 in, say, XFolder method
 *          code because the SOM headers did not know that XFolder
 *          had in fact XFldObject in its parent hierarchy).
 *
 *          This has changed now. I have hacked the XWorkplace
 *          include files and the include path to include these
 *          dependencies as well. Simply use parent_xwpQuerySetup2.
 *
 *      3.  The implementation of this method has been changed with
 *          V0.9.16. The PVOID is a pointer to an XSTRING buffer which
 *          has been initialized by XFldObject::xwpQuerySetup. Use
 *          the xstr* functions for appending stuff to the buffer.
 *
 *          This was changed because the double call to this method
 *          that was previously required always had the slight risk
 *          that the object data would change in between the two calls.
 *
 *      4.  Always terminate your setup strings with a semicolon (";"),
 *          even if it's the last.
 *
 *      5.  While this method is running, XWP has locked the object
 *          using its object mutex. So sending messages and other
 *          funky stuff is a no-no.
 *
 *      Use the following code to call the parent method (V0.9.16):
 *
 +          xstrcat(pstrSetup, "MYSETUPSTRING=VALUE;", 0);
 +              // and so on... settings for your class
 +
 +          return parent_xwpQuerySetup2(somSelf,
 +                                       pstrSetup);
 *
 *      If all methods obey these conventions, this results in a complete
 *      setup string for any object of any class, with the subclass's strings
 *      first and XFldObject's strings last.
 *
 *      See XFolder::xwpQuerySetup2 for a sample implementation
 *      which adds setup strings for a subclass of XFldObject.
 *
 *@@added V0.9.1 (2000-01-17) [umoeller]
 *@@changed V0.9.16 (2001-10-11) [umoeller]: adjusted to new implementation
 */

SOM_Scope BOOL  SOMLINK xo_xwpQuerySetup2(XFldObject *somSelf,
                                          PVOID pstrSetup)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpQuerySetup2");

    return objQuerySetup(somSelf,
                         pstrSetup);
}

/*
 *@@ xwpHotkeyOrBorderAction:
 *      this new XFldObject instance method gets called
 *      from the thread-1 object window whenever the
 *      XWPDaemon notifies it that an object should be
 *      opened, either because a hotkey was pressed or
 *      because a screen border was touched with the mouse.
 *
 *      This method gets resolved by name from
 *      T1M_OpenObjectFromHandle. This allows WPS
 *      classes to override what happens in this case.
 *
 *      Parameters:
 *
 *      --  ulView is the OPEN_* flag, normally OPEN_DEFAULT.
 *
 *      --  ulCorner is 0 if this is really from a
 *          hotkey. In that case, the "hotkey"
 *          system sound has already been played.
 *          It is > 0 if this resulted from a
 *          screen border mouse action.
 *          It is -1 if the caller desires only object
 *          opening/resurfacing without any special
 *          processing.
 *
 *      Presently, only the XCenter overrides this,
 *      so this default implementation gets called
 *      for everything but the XCenter. This calls
 *      wpViewObject on somSelf to either open or
 *      resurface an existing view.
 *
 *@@added V0.9.19 (2002-04-17) [umoeller]
 *@@changed V1.0.2 (2003-03-07) [umoeller]: removed hab, added ulView param
 */

SOM_Scope HWND  SOMLINK xo_xwpHotkeyOrBorderAction(XFldObject *somSelf,
                                                   ULONG ulView,
                                                   ULONG ulCorner)
{
    HWND hwnd;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpHotkeyOrBorderAction");

    // open the object, or resurface if already open
    hwnd = _wpViewObject(somSelf,
                         NULLHANDLE,   // hwndCnr (?!?)
                         ulView, // OPEN_DEFAULT,
                         0);           // "optional parameter" (?!?)

    PMPF_KEYS(("opened hwnd 0x%lX", hwnd));

    if (hwnd)
    {
        if (WinIsWindow(WinQueryAnchorBlock(HWND_DESKTOP),
                        hwnd))
        {
            // it's a window:
            // move to front
            WinSetActiveWindow(HWND_DESKTOP, hwnd);
        }
        else
        {
            // wpViewObject only returns a window handle for
            // WPS windows. By contrast, if a program object is
            // started, an obscure USHORT value is returned.
            // I suppose this is a HAPP instead.
            // From my testing, the lower byte (0xFF) contains
            // the session ID of the started application, while
            // the higher byte (0xFF00) contains the application
            // type, which is:
            // --   0x0300  presentation manager
            // --   0x0200  VIO

            // IBM, this is sick.

            // So now we go thru the switch list and find the
            // session which has this lo-byte. V0.9.4 (2000-06-15) [umoeller]

            // V0.9.19 (2002-04-17) [umoeller]: replaced all the
            // old code with this, which is probably more efficient.
            HSWITCH hsw;
            SWCNTRL swc;
            if (    (hsw = winhHSWITCHfromHAPP((HAPP)hwnd))
                 && (!WinQuerySwitchEntry(hsw, &swc))
               )
                WinSetActiveWindow(HWND_DESKTOP,
                                   swc.hwnd);
        }
    }

    return hwnd;
}

/*
 *@@ xwpHandleSelfClose:
 *      this new XFldObject instance method implements
 *      auto-closing the given view of a folder when a
 *      new view of somSelf was opened from it.
 *
 *      Warp 3 added support for the "self close" settings
 *      (IIRC), but unfortunately there are no exported
 *      methods to allow new views to behave according to
 *      these settings. The WPS will auto-close a folder
 *      if it handles a new from wpMenuItemSelected, but
 *      if XWorkplace handles such a menu item (such as
 *      for our new split view), we must do it ourselves.
 *
 *      So whenever we intercept a non-standard menu item
 *      in wpMenuItemSelected to open a new view, we just
 *      call this method now, which closes hwndFrame if
 *      necessary.
 *
 *@@added V1.0.1 (2002-12-08) [umoeller]
 */

SOM_Scope void  SOMLINK xo_xwpHandleSelfClose(XFldObject *somSelf,
                                              HWND hwndFrame,
                                              ULONG ulMenuId)
{
    WPFolder *pFolder;
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpHandleSelfClose");

    // get the folder of which hwndFrame is a view;
    // we can't just use wpQueryFolder because
    // of shadows and tree views... this might
    // be in a sub-subfolder also
    if (    (pFolder = WinSendMsg(hwndFrame,
                                  WM_QUERYOBJECTPTR,
                                  0,
                                  0))
         && (_somIsA(pFolder, _WPFolder))
         && (!(_wpQueryFldrFlags(pFolder) & FOI_WORKAREA))
       )
    {
        ULONG   ulClose = _wpQueryFldrSelfClose(pFolder);

        _PmpfF(("folder [%s] self-close: 0x%lX",
                _wpQueryTitle(pFolder),
                ulClose));

        if (    (ulClose == SELFCLOSE_ALL)
             || (WinGetKeyState(HWND_DESKTOP, VK_SHIFT) & 0x8000)
             || (    (ulClose == SELFCLOSE_SUBFOLDERS)
                  && (_somIsA(somSelf, _WPFolder))
                )
           )
            WinPostMsg(hwndFrame,
                       WM_SYSCOMMAND,
                       (MPARAM)SC_CLOSE,
                       MPFROM2SHORT(CMDSRC_MENU,
                                    TRUE));
    }
}

/*
 *@@ xwpCreateShadow:
 *      this new XFldObject method creates either a WPShadow or
 *      an XWPLink object for somSelf in the given folder.
 *
 *      The point of this new method is that if somSelf is a
 *      file-system object, it should have XWPLink's created
 *      instead of shadows.
 *
 *@@added V1.0.2 (2003-02-07) [umoeller]
 */

SOM_Scope WPObject*  SOMLINK xo_xwpCreateShadow(XFldObject *somSelf,
                                                WPFolder* pFolder,
                                                BOOL fLock)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpCreateShadow");

    return _wpCreateShadowObject(somSelf, pFolder, fLock);
}

/*
 *@@ xwpResolveIfLink:
 *      this new XFldObject method returns the object that
 *      somSelf points to.
 *
 *      If somSelf is not of the WPShadow or the XWPLink
 *      class, this returns somSelf. We only implement the
 *      WPShadow case here since XWPLink overrides this
 *      method.
 *
 *      Otherwise it can return NULL if the link is broken.
 *
 *@@added V1.0.2 (2003-02-07) [umoeller]
 */

SOM_Scope WPObject*  SOMLINK xo_xwpResolveIfLink(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_xwpResolveIfLink");

    if (_flObject & OBJFL_WPSHADOW)
        // call the WPShadow method
        return _wpQueryShadowedObject(somSelf, FALSE);

    return somSelf;
}

/*
 *@@ wpInitData:
 *      this WPObject instance method gets called when the
 *      object is being initialized (on wake-up or creation).
 *      We initialize our additional instance data here.
 *      Always call the parent method first.
 *
 *@@added V0.9.0 [umoeller]
 *@@changed V0.9.2 (2000-03-15) [umoeller]: initializing new members
 *@@changed V0.9.20 (2002-07-25) [umoeller]: added awake objects list maintenance
 */

SOM_Scope void  SOMLINK xo_wpInitData(XFldObject *somSelf)
{
    static SOMClass *s_pWPObject = NULL, *s_pWarpCenter = NULL;

    BOOL fAwakeSem = FALSE;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpInitData");

#ifdef USE_FASTMUTEX
    semCreate((PFASTMTX)&_mtxObject, NULL);
#endif

    XFldObject_parent_WPObject_wpInitData(somSelf);

    // get pointer to IBM WPObject instance data;
    // see WPProgram::wpInitData for more about this hack
    if (!s_pWPObject)
    {
        // first call:
        SOMClass *pClass = _somGetClass(somSelf);
                        // XWPObject class object now

        while (pClass = _somGetParent(pClass))
                    // either WPObject class object or another
                    // WPObject replacement now
        {
            if (!strcmp(_somGetName(pClass), "WPObject"))
            {
                // got it:
                s_pWPObject = pClass;

                PMPF_RESTOREDATA(("somGetInstanceSize %d",
                                _somGetInstanceSize(pClass)));
                PMPF_RESTOREDATA(("somGetInstancePartSize %d",
                                _somGetInstancePartSize(pClass)));
                PMPF_RESTOREDATA(("sizeof(IBMOBJECTDATA) %d",
                                sizeof(IBMOBJECTDATA)));

                break;
            }
        }
    }

    if (s_pWPObject)
        _pvWPObjectData = somDataResolve(somSelf, _somGetInstanceToken(s_pWPObject));
    else
        _pvWPObjectData = NULL; // shouldn't happen, but be safe

    // maintain the list of awake objects
    // (new with V0.9.20 (2002-07-25) [umoeller]):
    // we used to post WOM_ADDAWAKEOBJECT and have the
    // Worker thread maintain a tree of awake objects,
    // but for one, this was a terrible overhead both in
    // memory and time, and secondly, that tree was
    // maintained asynchronously and thus not up-to-date.
    // We now maintain a doubly-linked list right here
    // and in wpUnInitData. And instead of wasting memory
    // for another LINKLIST, we use the "pNextAwake" and
    // "pPreviousAwake" members in the object instance
    // data for the list directly... the below code is
    // basically copied from linklist.c.
    _pNextAwake = NULL;
    _pPreviousAwake = NULL;

    if (fAwakeSem = LockAwakeObjectsList())
    {
        if (G_pLastAwakeObject)
        {
            // list is not empty:
            // store ourselves in the previously last object
            XFldObjectData *somLast = XFldObjectGetData(G_pLastAwakeObject);
            somLast->pNextAwake = somSelf;
            // store that object as the previous for ourselves
            _pPreviousAwake = G_pLastAwakeObject;
            // terminate list with ourselves
            _pNextAwake = NULL;
            // and make ourselves the last one now
            G_pLastAwakeObject = somSelf;

            // raise list count
            ++G_cAwakeObjects;
        }
        else
        {
            // list is empty:
            G_pFirstAwakeObject
                = G_pLastAwakeObject
                = somSelf;

            G_cAwakeObjects = 1;
        }

        UnlockAwakeObjectsList();
    }

    // store awake WarpCenter if that's us; moved that
    // here as well from Worker thread, this finally
    // allows us to catch the WarpCenter even if it's
    // started from config.sys
    // V1.0.3 (2004-5-9) [pr]: Handle subclassed WarpCenter
    if (!s_pWarpCenter)
    {
        somId centerID = somIdFromString((PSZ) G_pcszSmartCenter);

        s_pWarpCenter = _somFindClass(SOMClassMgrObject, centerID, 0, 0);
        SOMFree(centerID);
    }

    // if (!strcmp(_somGetClassName(somSelf), G_pcszSmartCenter))
    if (_somIsA(somSelf, s_pWarpCenter))
        G_pAwakeWarpCenter = somSelf;

    // set the class flags
    ctsSetClassFlags(somSelf, &_flObject);

    _pvTrashData = NULL;            // V0.9.20 (2002-07-25) [umoeller]

    _pWszOriginalObjectID = NULL;

    _pvllWidgetNotifies = NULL;
    _pvllLists = NULL;

    // init the icon shares
    // V0.9.20 (2002-07-25) [umoeller]
    _pFirstIconClient = NULL;
    _pLastIconClient = NULL;
    _cIconClients = 0;

    _pobjIconServer = NULL;
    _pNextClient = NULL;
    _pPreviousClient = NULL;
}

/*
 *@@ wpObjectReady:
 *      this WPObject notification method gets called by the
 *      WPS when object instantiation is complete, for any reason.
 *      ulCode and refObject signify why and where from the
 *      object was created.
 *      The parent method must be called first.
 *
 *      We will have this object's pointer stored
 *      in a global list (maintained by the Worker thread)
 *      so that XShutdown knows which objects are currently
 *      awake.
 *
 *      Note: On my Warp 4 (FP 10), this method does _not_
 *      get called for WPFolder instances, so we override
 *      XFolder::wpObjectReady also.
 *
 *      Flags for ulCode:
 *
 *      --  OR_NEW          0x00000001
 *      --  OR_AWAKE        0x00000002
 *      --  OR_REFERENCE    0x10000000
 *      --  OR_FROMTEMPLATE (0x00000004 | OR_REFERENCE)
 *      --  OR_FROMCOPY     (0x00000008 | OR_REFERENCE)
 *      --  OR_SHADOW       (0x00000010 | OR_REFERENCE)
 *
 *@@changed V0.9.0: adjust for XFolder::wpObjectReady override
 *@@changed V0.9.7 (2000-12-18) [umoeller]: fixed _ulListNotify
 *@@changed V0.9.19 (2002-04-02) [umoeller]: moved impl to objReady
 */

SOM_Scope void  SOMLINK xo_wpObjectReady(XFldObject *somSelf,
                                         ULONG ulCode,
                                         WPObject* refObject)
{
    // XFldObjectMethodDebug("XFldObject","xo_wpObjectReady");

    XFldObject_parent_WPObject_wpObjectReady(somSelf, ulCode,
                                             refObject);

    // moved implementation V0.9.19 (2002-04-02) [umoeller]
    objReady(somSelf, ulCode, refObject);
}

/*
 *@@ wpSetup:
 *      this WPObject instance method is called to allow an
 *      object to set itself up according to setup strings.
 *      As opposed to wpSetupOnce, this gets called any time
 *      a setup string is invoked.
 *
 *@@added V0.9.9 (2001-04-06) [umoeller]
 *@@changed V1.0.2 (2003-02-07) [umoeller]: removed rexx setup string
 */

SOM_Scope BOOL  SOMLINK xo_wpSetup(XFldObject *somSelf, PSZ pszSetupString)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSetup");

    return XFldObject_parent_WPObject_wpSetup(somSelf, pszSetupString);
}

/*
 *@@ wpSetupOnce:
 *      this WPObject method allows special object handling
 *      based on a creation setup string after an object has
 *      been fully created.
 *      As opposed to WPObject::wpSetup, this method _only_
 *      gets called during object creation. The WPObject
 *      implementation calls wpSetup in turn.
 *      If FALSE is returned, object creation is aborted.
 *
 *      We check if we're being created inside the config folder.
 *      If so, we must invalidate the config folder cache.
 *
 *@@added V0.9.16 (2001-11-25) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpSetupOnce(XFldObject *somSelf,
                                       PSZ pszSetupString)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSetupOnce");

    if (G_pConfigFolder)
        // config folder is awake:
        if (wpshResidesBelow(somSelf, G_pConfigFolder))
            mnuInvalidateConfigCache();

    return XFldObject_parent_WPObject_wpSetupOnce(somSelf, pszSetupString);
}

/*
 *@@ wpFree:
 *      this WPObject method destroys the persistent form of the object
 *      and then frees the memory that represented that object.
 *      See object.c for a detailed description of an object's lifecycle.
 *
 *      If folder auto-refresh has been replaced by XWP, we override
 *      the entire WPObject::wpFree method without calling the parent.
 *      This is necessary for a number of reasons:
 *
 *      --  wpFree in turn calls the undocumented wpDestroyObject
 *          method, which has a number of bugs but cannot be overridden
 *          (because it's not exported in the IDL file).
 *
 *          So in order to fix those bugs, wpFree had to be rewritten,
 *          which now calls the new XFldObject::xwpDestroyStorage method
 *          (which is our reimplementation of wpDestroyObject).
 *
 *      --  wpFree is not very good at cleaning up object data in the
 *          INI files. This needs to be fixed finally.
 *
 *      In other words, when wpFree is now invoked on an object,
 *      the following happens:
 *
 *      1) In this method override, we clean up some object data and
 *         then call the xwpDestroyStorage method, using SOM name-lookup
 *         resolution. This allows subclasses (such as XFolder
 *         and XFldDataFile) to override the method, even though
 *         SOM doesn't know that XFldObject is actually a parent
 *         class of those subclasses (since the IDL files do not
 *         reflect that WPObject has been replaced with XFldObject).
 *
 *      2) XFldObject::xwpDestroyStorage is the standard implementation,
 *         which invokes the standard undocumented wpDestroyObject
 *         method.
 *
 *      In summary, for classes which have not overridden xwpNukeObject,
 *      the behavior is EXACTLY as with the standard WPObject::wpFree.
 *
 *      HOWEVER, this way we can override xwpDestroyStorage in
 *      XFldDataFile and XFolder to fix the annoying message box
 *      bugs.
 *
 *@@added V0.9.9 (2001-02-04) [umoeller]
 *@@changed V0.9.20 (2002-07-16) [umoeller]: optimized
 *@@changed V0.9.20 (2002-07-16) [umoeller]: fixed higly broken _wpSaveImmediate call
 *@@changed V0.9.20 (2002-07-16) [umoeller]: added exception handling
 *@@changed V0.9.20 (2002-07-25) [umoeller]: optimized to use real method calls finally
 *@@changed V1.0.0 (2002-09-09) [umoeller]: added wpSetError for errors
 *@@changed V1.0.9 (2010-07-29) [pr]: remove hotkeys @@fixes 1115
 */

SOM_Scope BOOL  SOMLINK xo_wpFree(XFldObject *somSelf)
{
    BOOL                brc = FALSE;
    PCKERNELGLOBALS     pKernelGlobals = krnQueryGlobals();
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpFree");

    TRY_LOUD(excpt1)        // V0.9.20 (2002-07-16) [umoeller]
    {
        _xwpSetObjectHotkey(somSelf, NULL);  // V1.0.9
        if (pKernelGlobals->fAutoRefreshReplaced)
        {
            ULONG   ulStyle = _wpQueryStyle(somSelf);
            PSZ     pszID = _wpQueryObjectID(somSelf);

            // reset the object error code because our wpDestroyObject
            // overrides may set that V1.0.0 (2002-09-09) [umoeller]
            _wpSetError(somSelf, NO_ERROR);

            // if the object has an object ID assigned, remove this...
            // this should clean the INI entry
            if (pszID && *pszID) // strlen(pszID)) V0.9.20 (2002-07-12) [umoeller]
                _wpSetObjectID(somSelf, NULL);

            // if the object is a template, unset that bit...
            // this should clean the INI entry as well
            if (ulStyle & OBJSTYLE_TEMPLATE)
                _wpModifyStyle(somSelf, OBJSTYLE_TEMPLATE, 0);

            // OK, here comes the fun stuff.
            // The WPS normally calls the "wpDestroyObject" method,
            // which is responsible for killing the physical representation
            // of the object. Unfortunately, we cannot override that method
            // because IBM wasn't kind enough to make it public. Our way
            // around this (without breaking compatibility) is to introduce
            // a new method in XFldObject, which calls wpDestroyObject per
            // default.

            // We resolve the method by name because it is overridden
            // in some XWorkplace classes. If it is not overridden,
            // XFldObject::xwpDestroyStorage calls wpDestroyObject.

            // no longer so, we can override wpDestroyObject now
            // V0.9.20 (2002-07-25) [umoeller]

            // but we should check the return code, dammit... this can
            // return FALSE, for example, if the user tries to delete
            // an executable that is currently in use, and then this frees
            // the SOM object even though the file is still there, which
            // then reappears on refresh
            // V1.0.0 (2002-09-09) [umoeller]
            if (_wpDestroyObject(somSelf))
            {
                // the WPS then calls wpSaveImmediate just in case the object
                // has called wpSaveDeferred. I'm not sure this is a good idea...
                // this will add another entry to the INI file. This should be
                // moved up.
                // _wpSaveImmediate(somSelf);

                // no!!
                // V0.9.20 (2002-07-16) [umoeller]
                xo_wpSaveImmediate(somSelf);
                    // NOTE: NO METHOD CALL!
                    // this makes sure ONLY that the object is removed from
                    // both our private dirty list AND the IBM dirty list,
                    // but does not actually save the data because only
                    // the storage classes (WPAbstract, WPFileSystem) call
                    // wpSaveState... this fix probably solves the problems
                    // that people were reporting with CHECKINI restoring
                    // lots of objects already

                // then there's another undocumented method call... i'm unsure
                // what this does, but what the heck.
                _wpDeleteWindowPosKeys(somSelf);
                        // we can call the method now V0.9.20 (2002-07-25) [umoeller]

                // finally, this calls wpMakeDormant, which destroys the SOM object
                brc = _wpMakeDormant(somSelf, 0);
                        // we can call the method now V0.9.20 (2002-07-25) [umoeller]
            }

        } // if (pKernelGlobals->fAutoRefreshReplaced)
        else
            brc = XFldObject_parent_WPObject_wpFree(somSelf);
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    return brc;
}

/*
 *@@ wpUnInitData:
 *      this WPObject instance method is called when the object
 *      is destroyed as a SOM object, either because it's being
 *      made dormant (via wpMakeDormant) or being deleted for
 *      good (via wpFree). All allocated in-memory resources
 *      should be freed here, but to destroy the physical
 *      representation of the object, override wpDestroyObject
 *      instead.
 *
 *      The parent method must always be called last.
 *
 *      We will have this object removed from our global list
 *      of awake objects.
 *
 *@@changed V0.9.3 (2000-04-11) [umoeller]: now destroying related trash object too
 *@@changed V0.9.6 (2000-10-23) [umoeller]: added support for progOpenProgram
 *@@changed V0.9.7 (2001-01-18) [umoeller]: added support for favorite and quick-open folders
 *@@changed V0.9.16 (2001-12-31) [umoeller]: added fixes for replacement icons
 *@@changed V0.9.20 (2002-07-25) [umoeller]: added object mutex request
 *@@changed V0.9.20 (2002-07-25) [umoeller]: added excpt handling
 *@@changed V0.9.20 (2002-07-25) [umoeller]: added awake objects list maintenance
 *@@changed V0.9.20 (2002-07-25) [umoeller]: adjusted for icon sharing
 */

SOM_Scope void  SOMLINK xo_wpUnInitData(XFldObject *somSelf)
{
    BOOL        fAwakeSem = FALSE,
                fIconSem = FALSE,
                fListSem = FALSE;
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpUnInitData");

    TRY_LOUD(excpt1)
    {
        PMINIRECORDCORE pmrc;
        PTRASHDATA      p;

        // maintain list of awake objects
        // (see wpInitData for remarks)
        // V0.9.20 (2002-07-25) [umoeller]
        if (fAwakeSem = LockAwakeObjectsList())
        {
            XFldObjectData *somThat;

            if (G_pFirstAwakeObject == somSelf)
                // we are first: adjust first
                G_pFirstAwakeObject = _pNextAwake;     // can be NULL

            if (G_pLastAwakeObject == somSelf)
                // we are last: adjust last
                G_pLastAwakeObject = _pPreviousAwake;  // can be NULL

            if (_pPreviousAwake)
            {
                // we have a previous object: make that point to
                // our next object, taking us out from the middle
                somThat = XFldObjectGetData(_pPreviousAwake);
                somThat->pNextAwake = _pNextAwake;
            }

            if (_pNextAwake)
            {
                // we have a next object: make that point to
                // our previous object, taking us out from the middle
                somThat = XFldObjectGetData(_pNextAwake);
                somThat->pPreviousAwake = _pPreviousAwake;
            }

            // decrease list count
            --G_cAwakeObjects;

            UnlockAwakeObjectsList();
            fAwakeSem = FALSE;
        }

        // maintain icon shares; we request the icon shares mutex,
        // which makes sure that we'll wait while someone is
        // working on those lists
        if (fIconSem = icomLockIconShares())
        {
            if (_pobjIconServer)
                // we're a client: detach ourselves from the
                // server's list
                icomUnShareIcon(_pobjIconServer,
                                somSelf);
            else if (_pFirstIconClient)
            {
                // we're a server:
                // run through the list of clients and set each
                // client's HPOINTER to NULLHANDLE; this will
                // force a requery of each client's icon on
                // the next client's _wpQueryIcon... at the
                // same time, clean out the list
                WPObject *pobjClient = _pFirstIconClient;

                while (pobjClient)
                {
                    XFldObjectData *somClient = XFldObjectGetData(pobjClient);

                    if (pmrc = _wpQueryCoreRecord(pobjClient))
                        pmrc->hptrIcon = NULLHANDLE;

                    pobjClient = somClient->pNextClient;

                    // clear out client instance data
                    somClient->pobjIconServer
                        = somClient->pPreviousClient
                        = somClient->pNextClient
                        = NULL;
                }
            }

            icomUnlockIconShares();
            fIconSem = FALSE;
        }

        // kick us out of all object lists that we are currently
        // stored in V1.0.1 (2002-12-11) [umoeller]
        if (fListSem = LockObjectLists())
        {
            if (_pvllLists)
            {
                PLISTNODE pNode;
                while (pNode = lstQueryFirstNode(_pvllLists))
                {
                    XWPObjList *pList = (XWPObjList*)pNode->pItemData;
                    _Remove(pList, somSelf);
                            // this calls back or xwpRemovedFromList method,
                            // which removes pList from _pvllLists
                }

                lstFree((LINKLIST**)&_pvllLists);
            }

            UnlockObjectLists();
            fListSem = FALSE;
        }

        // grab the object's mutex to let no-one mess with us
        // while we're cleaning up! Note that we do NOT release
        // it because WPObject::wpUnInitData will delete it anway.
        // V0.9.20 (2002-07-25) [umoeller]
        _wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT);

        #ifdef __DEBUG__
            if (!(_flObject & OBJFL_INITIALIZED))
            {
                cmnLog(__FILE__, __LINE__, __FUNCTION__,
                       "Object 0x%lX (%s) was never initialized?",
                       somSelf,
                       STRINGORNULL(_wpQueryTitle(somSelf)));
            }
        #endif

        // have object removed from awake-objects list
        // removed V0.9.20 (2002-07-25) [umoeller]
        // xthrPostWorkerMsg(WOM_REMOVEAWAKEOBJECT,
        //                   (MPARAM)somSelf,
        //                   MPNULL);

        // destroy trash object, if there's one
        if (p = (PTRASHDATA)_pvTrashData)
        {
            if (p->pTrashObject)
                _wpFree(p->pTrashObject);

            _wpFreeMem(somSelf, (PBYTE)_pvTrashData);
            _pvTrashData = NULL;
        }

        // we have a problem with our replacement icons in that
        // the WPS frees the pointer handle in WPObject::wpUnInitData
        // if the object has the OBJSTYLE_NOTDEFAULTICON or OBJSTYLE_TEMPLATE
        // flags set... so in these cases, check if the object has one
        // of our standard icons WHICH MUST NOT BE FREED under any circumstances,
        // or the shared icon would disappear globally
        // V0.9.16 (2001-12-31) [umoeller]
        if (    (pmrc = _wpQueryCoreRecord(somSelf))
             && (pmrc->hptrIcon)
           )
        {
            // Now, we might have made the object's icon global in
            // icomShareIcon... if so, make it private again so we
            // can kill it without leaking memory.
            if (_flObject & OBJFL_GLOBALICON)
            {
                WinSetPointerOwner(pmrc->hptrIcon,
                                   G_pidWPS,
                                   TRUE);
                _flObject &= ~OBJFL_GLOBALICON;
            }

            if (_wpQueryStyle(somSelf) & (OBJSTYLE_NOTDEFAULTICON | OBJSTYLE_TEMPLATE))
            {
                PMPF_ICONREPLACEMENTS(("checking hptr 0x%lX", pmrc->hptrIcon));

                if (cmnIsStandardIcon(pmrc->hptrIcon))
                {
                    #ifdef __DEBUG__
                        cmnLog(__FILE__, __LINE__, __FUNCTION__,
                               "WPS was about to nuke standard icon for object \"%s\"",
                               pmrc->pszIcon);
                    #endif

                    // alright, the WPS is about to nuke this icon:
                    // set the HPOINTER in the record to NULLHANDLE
                    // to prevent the WPS from freeing it
                    pmrc->hptrIcon = NULLHANDLE;
                }
            }
        }

        // kill the title string we allocated in our wpSetTitle replacement
        // V0.9.16 (2002-01-04) [umoeller]
        if (pmrc->pszIcon)
        {
            _wpFreeMem(somSelf, pmrc->pszIcon);
            // we must set the ptr to NULL or the WPS will crash
            // in the parent method call
            pmrc->pszIcon = NULL;
        }

        // free the object ID backup if there's one
        // V0.9.16 (2001-12-06) [umoeller]
        wpshStore(somSelf, &_pWszOriginalObjectID, NULL, NULL);

        // go thru list notifications, if we have any
        if (_flObject)
        {
            if (_flObject & OBJLIST_RUNNINGSTORED)
            {
                // this object is currently stored in the
                // "running programs" list: remove it, or
                // we'll get crashes later...
                _flObject &= ~OBJLIST_RUNNINGSTORED;
                progRunningAppDestroyed(somSelf);
            }

            if (_flObject & OBJLIST_CONFIGFOLDER)
            {
                // somSelf is in the config folder hierarchy:
                // invalidate the content lists for the config
                // folders so that they will be rebuilt
                _flObject &= ~OBJLIST_CONFIGFOLDER;
                mnuInvalidateConfigCache();
            }

#if 0 // these flags removed V1.0.1 (2002-12-11) [umoeller]
    #ifndef __NOFOLDERCONTENTS__
                if (_flObject & OBJLIST_FAVORITEFOLDER)
                {
                    _flObject &= ~OBJLIST_FAVORITEFOLDER;
                    objAddToList(somSelf,
                                 &G_llFavoriteFolders,      // folder.h
                                 FALSE,         // remove
                                 INIKEY_FAVORITEFOLDERS,
                                 0);            // no modify flags... we're being destroyed
                }
    #endif

    #ifndef __NOQUICKOPEN__
                if (_flObject & OBJLIST_QUICKOPENFOLDER)
                {
                    _flObject &= ~OBJLIST_QUICKOPENFOLDER;
                    objAddToList(somSelf,
                                 &G_llQuickOpenFolders,      // folder.h
                                 FALSE,         // remove
                                 INIKEY_QUICKOPENFOLDERS,
                                 0);            // no modify flags... we're being destroyed
                }
    #endif
#endif

            if (_flObject & OBJLIST_HANDLESCACHE)
            {
                _flObject &= ~OBJLIST_HANDLESCACHE;
                objRemoveFromHandlesCache(somSelf);
            }

            if (_flObject & OBJLIST_DIRTYLIST)  // V0.9.11 (2001-04-18) [umoeller]
            {
                objRemoveFromDirtyList(somSelf);
                        // this unsets the flag
            }

            if (_flObject & OBJLIST_QUERYAWAKEFSOBJECT)  // V0.9.16 (2001-10-25) [umoeller]
            {
                _flObject &= ~OBJLIST_QUERYAWAKEFSOBJECT;
                fdrRemoveAwakeRootFolder(somSelf);
            }
        }

        if (_pvllWidgetNotifies)
        {
            // we have windows that requested notifications:
            // go thru list
            PLISTNODE pNode = lstQueryFirstNode(_pvllWidgetNotifies);
            while (pNode)
            {
                HWND hwnd = (HWND)pNode->pItemData;
                WinPostMsg(hwnd,
                           WM_CONTROL,
                           MPFROM2SHORT(ID_XCENTER_CLIENT,
                                        XN_OBJECTDESTROYED),
                           (MPARAM)somSelf);
                pNode = pNode->pNext;
            }

            lstFree((LINKLIST**)&_pvllWidgetNotifies);
        }
    }
    CATCH(excpt1)
    {
    } END_CATCH();

    if (fListSem)
        UnlockObjectLists();

    if (fIconSem)
        icomUnlockIconShares();

    if (fAwakeSem)
        UnlockAwakeObjectsList();

    // even if we crashed, call the parent... we can't
    // afford stopping here
    XFldObject_parent_WPObject_wpUnInitData(somSelf);

#ifdef USE_FASTMUTEX
    while (semAssert((PFASTMTX)&_mtxObject))
        semRelease((PFASTMTX)&_mtxObject);

    semClose((PFASTMTX)&_mtxObject);
#endif
}

/*
 *@@ wpSaveDeferred:
 *      this WPObject method saves object instance data
 *      asynchronously on a separate thread.
 *
 *      What the WPS really does here is to put the object
 *      on an internal list. There is some separate thread
 *      running which ages the objects on the list and,
 *      if an object has aged enough, will invoke
 *      WPObject::wpSaveImmediate upon the object, which
 *      not only saves the data by invoking the object's
 *      wpSaveState, but remove it from the ager list as
 *      well.
 *
 *      Now, we need to override this method to keep track
 *      of which objects are currently on the list. Otherwise
 *      objects won't be properly saved during XShutdown.
 *
 *      This WPObject method is never overridden in the WPS.
 *
 *@@added V0.9.9 (2001-04-04) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpSaveDeferred(XFldObject *somSelf)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSaveDeferred");

    // store it only if the parent actually succeeded
    // V0.9.18 (2002-03-24) [umoeller]
    if (XFldObject_parent_WPObject_wpSaveDeferred(somSelf))
    {
        // add the object to our private "dirty" list
        objAddToDirtyList(somSelf);
        return TRUE;
    }

    return FALSE;
}

/*
 *@@ wpSaveImmediate:
 *      this WPObject method allocates some memory and then
 *      invokes the object's wpSaveState so that the object's
 *      instance data will be saved synchronously. The
 *      WPObject implementation calls wpSaveState eventually.
 *
 *      This can get called in three situations:
 *
 *      --  Explicitly, if an object wants to save its state
 *          _now_, e.g. because some critical data has changed.
 *
 *      --  Deferred as a result of WPObject::wpSaveDeferred,
 *          if the object has aged enough. This method will
 *          then run on the WPS-internal ager thread (see
 *          XFldObject::wpSaveDeferred).
 *
 *      --  Automatically during WPObject::wpMakeDormant if
 *          the object has a deferred save pending.
 *
 *      We remove the object from our private "dirty" list
 *      because it no longer needs to be saved on shutdown.
 *      See XFldObject::wpSaveDeferred.
 *
 *      This method is overridden by the base storage classes
 *      (WPAbstract, WPFileSystem, WPTransient), but most of
 *      them call the WPObject parent... except WPAbstract,
 *      apparently, which only sometimes calls the parent
 *      for some reason. Never mind, we still know that the
 *      object has been touched, so we have it on the list
 *      for XShutdown. Saving twice won't hurt.
 *
 *@@added V0.9.9 (2001-04-04) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpSaveImmediate(XFldObject *somSelf)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSaveImmediate");

    objRemoveFromDirtyList(somSelf);

    // now make sure the object is not residing below some desktop
    // which isn't the current desktop... we run into endless
    // problems with object IDs if we save objects that aren't
    // ours!
    // if (!cmnIsObjectFromForeignDesktop(somSelf))
        return XFldObject_parent_WPObject_wpSaveImmediate(somSelf);

    // else: we shouldn't save...
    /* {
        CHAR szFolderPath[CCHMAXPATH];
        _wpQueryFilename(_wpQueryFolder(somSelf),
                         szFolderPath,
                         TRUE);
        cmnLog(__FILE__, __LINE__, __FUNCTION__,
               "skipping save of object %s (class: %s) in folder %s",
               _wpQueryTitle(somSelf),
               _somGetClassName(somSelf),
               szFolderPath);
    }

    return TRUE;*/
}

/*
 *@@ wpSaveState:
 *      this WPObject instance method saves an object's state
 *      persistently so that it can later be re-initialized
 *      with wpRestoreState. This gets called during wpClose,
 *      wpSaveImmediate or wpSaveDeferred processing.
 *      All persistent instance variables should be stored here.
 *
 *@@added V0.9.0 [umoeller]
 *@@changed V0.9.16 (2001-12-06) [umoeller]: fixed problems with disappearing object IDs
 */

SOM_Scope BOOL  SOMLINK xo_wpSaveState(XFldObject *somSelf)
{
    BOOL            brc = FALSE,
                    fHacked = FALSE;
    PTRASHDATA      p;
    PIBMOBJECTDATA  pod;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSaveState");

    // now, here's the bug with the object IDs disappearing if
    // the object's ID was set from another Desktop...
    // see XFldObject::xwpQueryOriginalObjectID for the scenario.

    // replaced this section to use the new IBMOBJECTDATA instead
    if (    // we have a pointer to the object strings:
            (pod = (PIBMOBJECTDATA)_pvWPObjectData)
            // and object ID in instance data is NULL:
         && (!pod->pszObjectID)
            // but there was one originally:
         && (_pWszOriginalObjectID)
       )
    {
        // restore the old object ID for save!!
        _PmpfF(("restoring old object ID \"%s\" for save", _pWszOriginalObjectID));

        pod->pszObjectID = _pWszOriginalObjectID;
        fHacked = TRUE;
    }

    brc = XFldObject_parent_WPObject_wpSaveState(somSelf);

    if (fHacked)
        // restore old NULL pointer
        pod->pszObjectID = NULL;

    /* old code V0.9.19 (2002-07-01) [umoeller]
    if (    // we have a pointer to the object strings:
            (_pObjectStrings)
            // and object ID in instance data is NULL:
         && (!_pObjectStrings->pszObjectID)
            // but there was one originally:
         && (_pWszOriginalObjectID)
       )
    {
        // restore the old object ID for save!!
        _PmpfF(("restoring old object ID \"%s\" for save", _pWszOriginalObjectID));

        _pObjectStrings->pszObjectID = _pWszOriginalObjectID;
        fHacked = TRUE;
    }

    brc = XFldObject_parent_WPObject_wpSaveState(somSelf);

    if (fHacked)
        // restore old NULL pointer
        _pObjectStrings->pszObjectID = NULL;
    */

    // save deletion data if we are currently in the trash can
    if (p = (PTRASHDATA)_pvTrashData)       // V0.9.20 (2002-07-25) [umoeller]
    {
        _wpSaveData(somSelf,
                    (PSZ)G_pcszXFldObject, 1,
                    (PBYTE)&p->cdateDeleted, sizeof(CDATE));
        _wpSaveData(somSelf,
                    (PSZ)G_pcszXFldObject, 2,
                    (PBYTE)&p->ctimeDeleted, sizeof(CTIME));
    }

    return brc;
}

/*
 *@@ wpRestoreState:
 *      this WPObject instance method gets called during object
 *      initialization (after wpInitData) to restore the data
 *      which was stored with wpSaveState.
 *
 *@@added V0.9.0 [umoeller]
 *@@changed V0.9.16 (2001-12-06) [umoeller]: now saving backup of object ID
 */

SOM_Scope BOOL  SOMLINK xo_wpRestoreState(XFldObject *somSelf,
                                          ULONG ulReserved)
{
    BOOL            brc = FALSE;
    CDATE           cdate;
    CTIME           ctime;
    ULONG           cbcdate = sizeof(CDATE),
                    cbctime = sizeof(CTIME);
    PTRASHDATA      p;
    PIBMOBJECTDATA  pod;
    PSZ             pszObjectID;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpRestoreState");

    brc = XFldObject_parent_WPObject_wpRestoreState(somSelf,
                                                    ulReserved);

    #ifdef __DEBUG__
        if (pod = (PIBMOBJECTDATA)_pvWPObjectData)
        {
            PMPF_RESTOREDATA(("pData->pFolder 0x%lX, _wpQueryFolder 0x%lX",
                        pod->pFolder, _wpQueryFolder(somSelf)));

            PMPF_RESTOREDATA(("pData->ulDefaultView 0x%lX, _wpQueryDefaultView 0x%lX",
                        pod->ulDefaultView, _wpQueryDefaultView(somSelf)));

            PMPF_RESTOREDATA(("pData->pszObjectID 0x%lX, _wpQueryObjectID 0x%lX",
                        pod->pszObjectID, _wpQueryObjectID(somSelf)));
        }
    #endif

    // now check: if an object ID was remembered with
    // this object's instance data, make a backup of
    // this because _wpQueryObjectID sometimes resets
    // this to NULL
    // V0.9.16 (2001-12-06) [umoeller]
    if (    (pod = (PIBMOBJECTDATA)_pvWPObjectData)
         && (pszObjectID = pod->pszObjectID)
         && (*pszObjectID)
       )
        wpshStore(somSelf,
                  &_pWszOriginalObjectID,
                  pszObjectID,
                  NULL);

    // restore trash can deletion
    // updated V0.9.20 (2002-07-25) [umoeller]
    if (    (_wpRestoreData(somSelf, (PSZ)G_pcszXFldObject, 1,
                            (PBYTE)&cdate, &cbcdate))
         && (_wpRestoreData(somSelf, (PSZ)G_pcszXFldObject, 2,
                            (PBYTE)&ctime, &cbctime))
         && (p = GetTrashData(somSelf))
       )
    {
        // both keys successfully restored:
        memcpy(&p->cdateDeleted, &cdate, sizeof(cdate));
        memcpy(&p->ctimeDeleted, &ctime, sizeof(ctime));
    }

    return brc;
}

/*
 *@@ wpSetObjectID:
 *      this WPObject method sets a new object ID for the
 *      object. We must then invalidate our backup object
 *      ID, or we'll run into problems in wpSaveState.
 *
 *      See XFldObject::xwpQueryOriginalObjectID for details.
 *
 *@@added V0.9.16 (2001-12-06) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpSetObjectID(XFldObject *somSelf,
                                         PSZ pszObjectID)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSetObjectID");

    // now this is a true overwrite of the object ID,
    // so nuke the one we backed up, but only if the
    // object has been initialized
    if (_flObject & OBJFL_INITIALIZED) // _wpIsObjectInitialized(somSelf))
        wpshStore(somSelf, &_pWszOriginalObjectID, NULL, NULL);

    return XFldObject_parent_WPObject_wpSetObjectID(somSelf,
                                                    pszObjectID);
}

/*
 *@@ wpQueryDefaultHelp:
 *      this WPObject instance method specifies the default
 *      help panel for an object. This should hand out a help
 *      panel describing what this object can do in general
 *      and return TRUE.
 *
 *      "Default help" is displayed when help is requested
 *      on an object itself, i.e. when a single object is
 *      selected and F1 is pressed (or when "Extended help"
 *      is selected from the context menu).
 *
 *      Default help can either be instance or class help.
 *      WPObject::wpQueryDefaultHelp checks for whether
 *      the object has HELPLIBRARY and/or HELPPANEL set and
 *      hands out those values if so, or otherwise calls the
 *      class method wpclsQueryDefaultHelp on the object's
 *      class object instead.
 *
 *      As a result, classes should only override the instance
 *      method if they want to display specific help depending
 *      on the object's instance settings. For example, this
 *      makes sense in XWPProgramFile::wpQueryDefaultHelp in
 *      order to differentiate between program types. To
 *      change help for an entire class without breaking
 *      instance help settings for an object, classes should
 *      override wpclsQueryDefaultHelp. (Finally got this
 *      right with 0.9.20.)
 *
 *      We override this method to hand out more meaningful
 *      help for many default WPS objects which have no
 *      HELPPANEL assigned, unfortunately. We no longer call
 *      the parent, but check the instance data ourselves
 *      and base additional help on the object ID.
 *
 *@@added V0.9.20 (2002-07-12) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpQueryDefaultHelp(XFldObject *somSelf,
                                              PULONG pHelpPanelId,
                                              PSZ HelpLibrary)
{
    PIBMOBJECTDATA  pod;
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpQueryDefaultHelp");

    if (    (pod = _pvWPObjectData)
#ifndef __ALWAYSREPLACEHELP__
         && (cmnQuerySetting(sfHelpReplacements))
#endif
       )
    {
        // check the help library, if it's null, it's WPHELP.HLP
        PSZ     p;
        ULONG   ul;
        if (    (    (!(p = pod->pszHelpLibrary))
                  || (!*p)
                )
                // and no help panel assigned
             && (!(pod->ulHelpPanelID))
                // but we have an object ID
             && (p = _wpQueryObjectID(somSelf))
             && (*p)
           )
        {
            static const struct
            {
                PCSZ    pcszID;
                ULONG   ulHelpPanelId;
            } aObjectIDsWithHelp[] =
            {
                // "picture viewer" never had one
                "<WP_PICV>",                ID_XSH_PICVIEW,
                // programs folder never had one
                "<WP_PROGRAMSFOLDER>",      ID_XSH_PROGRAMSFDR,
                "<WP_CONNECTIONSFOLDER>",   ID_XSH_CONNECTIONSFDR,
                "<WP_INTERNET>",            ID_XSH_INTERNETFDR,
                "<WP_ASSISTANCE>",          ID_XSH_OS2INFORMATIONFDR,
                "<WP_TROUBLEINFO>",         ID_XSH_OS2INFORMATIONFDR,
                "<WP_PRINTERSFOLDER>",      ID_XSH_PRINTERSFDR,
                "<WP_INSTREMFOLDER>",       ID_XSH_INSTALLREMOVEFDR,
                // items in system setup:
                "<WP_LOCALE>",              ID_XSH_SYSSETUP_LOCALE,
                "<LVMGUI>",                 ID_XSH_SYSSETUP_LVMGUI,
                "<MMPM2_SETUP>",            ID_XSH_SYSSETUP_MMPM2,
                "<WP_REGEDIT>",             ID_XSH_SYSSETUP_REGEDIT,
                // Appearance folder under System Setup, new with eCS 1.1
                "<WP_CONFIG_LOOK>",         ID_XSH_SYSSETUP_LOOK,
                "<ECS_ESTLRLITEPREF>",      ID_XSH_SYSSETUP_ESTYLER,
                "<ECS_THEMEMGRPREF>",       ID_XSH_SYSSETUP_THEMEMGR,
                "<ECS_ITHEME>",             ID_XSH_SYSSETUP_ITHEME,
                // Network folder under System Setup
                "<WP_CONFIG_NET>",          ID_XSH_SYSSETUP_NET,
                "<TCP/IP>",                 ID_XSH_SYSSETUP_NET,
                // "Refresh removeable media" in Drives
                "<LVMREFRESH>",             ID_XSH_DRIVES_REFRESHMEDIA,
            };

            for (ul = 0;
                 ul < ARRAYITEMCOUNT(aObjectIDsWithHelp);
                 ++ul)
            {
                if (!strcmp(p, aObjectIDsWithHelp[ul].pcszID))
                {
                    strcpy(HelpLibrary, cmnQueryHelpLibrary());
                    *pHelpPanelId = aObjectIDsWithHelp[ul].ulHelpPanelId;
                    return TRUE;
                }
            }
        }

        if (ul = pod->ulHelpPanelID)
        {
            *pHelpPanelId = ul;
            if (p = pod->pszHelpLibrary)
                strcpy(HelpLibrary, p);
            else
                *HelpLibrary = '\0';

            return TRUE;
        }

        return _wpclsQueryDefaultHelp(_somGetClass(somSelf),
                                      pHelpPanelId,
                                      HelpLibrary);
    }

    return XFldObject_parent_WPObject_wpQueryDefaultHelp(somSelf,
                                                         pHelpPanelId,
                                                         HelpLibrary);
}

/*
 *@@ wpSetIcon:
 *
 *@@added V0.9.20 (2002-07-31) [umoeller]
 *@@changed V1.0.1 (2002-12-14) [umoeller]: fixed broken lazy loading of ICO files @@fixes 293
 */

SOM_Scope BOOL  SOMLINK xo_wpSetIcon(XFldObject *somSelf, HPOINTER hptrNewIcon)
{
    BOOL            brc = FALSE;
    BOOL            fSharedLocked = FALSE,
                    fFlagsLocked = FALSE,
                    fTurnBackOn = FALSE;

    XFldObjectData  *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSetIcon");

    TRY_LOUD(excpt1)
    {
        PIBMOBJECTDATA  pData;
        PMINIRECORDCORE pmrc;

        // nasty hack for lazy icons: wpSetIcon will not update
        // the records of the object in all open views if the
        // current MINIRECORDCORE.hptrIcon is NULLHANDLE. However
        // we can't preset the hptrIcon thing to the class default
        // icon in our cnr owner draw proc because then the
        // _wpQueryIcon call in fntLazyIcons would fail
        // because it would think there was already an icon.
        // So we have to use another object flag (OBJFL_LAZYLOADINGICON)
        // which is set by icomQueueLazyIcon when the object gets added
        // to the lazy load queue and is reset again here on the first
        // wpSetIcon that comes in afterwards, which (hopefully) is
        // from fntLazyIcons.
        if (    (pData = (PIBMOBJECTDATA)_pvWPObjectData)
             && (pmrc = pData->pmrc)
             // && (fSelfLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
                // V1.0.0 (2002-09-02) [umoeller]
             && (fFlagsLocked = objLockFlags())
           )
        {
            PMPF_ICONREPLACEMENTS(("[%s] hptr 0x%lX -> 0x%lX, OBJFL_LAZYLOADINGICON: %lX",
                _wpQueryTitle(somSelf),
                pmrc->hptrIcon,
                hptrNewIcon,
                _flObject & OBJFL_LAZYLOADINGICON));

            if (_flObject & OBJFL_LAZYLOADINGICON)
            {
                pmrc->hptrIcon = _wpclsQueryIcon(_somGetClass(somSelf));

                // warning: this causes data file association icons
                // for icon files if extended assocs are off!!
                // the WPS uses OBJSTYLE_NOTDEFAULTICON as an
                // internal marker that, if hptrIcon is NULLHANDLE,
                // the icon still needs to be loaded from an icon
                // file, and we leak icon handles like crazy if
                // we turn this off... besides, this causes the
                // real icons of ICO files to disappear when
                // clicked on them, so turn it back on below!
                // V1.0.1 (2002-12-14) [umoeller]
                fTurnBackOn = (_wpQueryStyle(somSelf) & OBJSTYLE_NOTDEFAULTICON);

                _wpModifyStyle(somSelf,
                               OBJSTYLE_NOTDEFAULTICON,
                               0);

                _flObject &= ~OBJFL_LAZYLOADINGICON;
            }

            objUnlockFlags();       // V1.0.0 (2002-09-02) [umoeller]
            fFlagsLocked = FALSE;
        }

        // now go call the parent, which updates the object in all
        // open views
        if (XFldObject_parent_WPObject_wpSetIcon(somSelf, hptrNewIcon))
        {
            // now update client icons, if we're an icon server

            if (fSharedLocked = icomLockIconShares())
            {
                WPObject *pobjClient = _pFirstIconClient;

                while (pobjClient)
                {
                    XFldObjectData *somThat = XFldObjectGetData(pobjClient);
                    _wpSetIcon(pobjClient, hptrNewIcon);
                    pobjClient = somThat->pNextClient;
                }
            }
        }
    }
    CATCH(excpt1)
    {
    } END_CATCH();

    if (fSharedLocked)
        icomUnlockIconShares();

    if (fFlagsLocked)
        objUnlockFlags();       // V1.0.0 (2002-09-02) [umoeller]

    if (fTurnBackOn)
        _wpModifyStyle(somSelf, OBJSTYLE_NOTDEFAULTICON, OBJSTYLE_NOTDEFAULTICON);
                // V1.0.1 (2002-12-14) [umoeller]

    return brc;
}

/*
 *@@ wpAddToObjUseList:
 *      this WPObject instance method adds a new item to the
 *      object's in-use list.
 *
 *      Every object in the system has an in-use list. The in-use list
 *      is a linked list of USEITEM structures. The USEITEM structure
 *      consists of an item type and a pointer to the next USEITEM
 *      structure and is followed immediately by an item type-specific
 *      structure:
 *
 *      --  USAGE_LINK: followed by LINKITEM. This specifies a shadow
 *          that is currently awake and points to this object.
 *
 *      --  USAGE_MEMORY: followed by MEMORYITEM. One of this is
 *          allocated for each wpAllocMem call on the object.
 *
 *      --  USAGE_NOTIFY: followed by LINKITEM. This is new with Warp 4,
 *          and I don't really know what it does.
 *
 *      --  USAGE_OPENVIEW: followed by VIEWITEM. This gets added for
 *          each view of the object.
 *
 *      --  USAGE_OPENFILE: followed by VIEWFILE. This gets added for
 *          data files only when a program is opened from an association.
 *
 *      --  USAGE_RECORD: followed by RECORDITEM. This gets added for
 *          every container that the object is currently inserted in.
 *
 *      We replace this method for the USAGE_OPENVIEW case for data
 *      files because the WPS creates a handle for each data file
 *      in this method here. We do this only if the user has disabled
 *      the "handles" setting on the "File types" page.
 *
 *@@added V0.9.20 (2002-08-04) [umoeller]
 *@@changed V1.0.5 (2006-06-04) [pr]: Disabled - causes many problems with false in-use items
 */

SOM_Scope BOOL  SOMLINK xo_wpAddToObjUseList(XFldObject *somSelf,
                                             PUSEITEM pUseItem)
{
    BOOL    fLocked = FALSE,
            brc = FALSE;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpAddToObjUseList");

#if 0
#ifndef __NOTURBOFOLDERS__
    TRY_LOUD(excpt1)
    {
        PIBMOBJECTDATA pod;

        if (    (!(_flObject & OBJFL_WPDATAFILE))
             || (pUseItem->type != USAGE_OPENVIEW)
             || (cmnQuerySetting(sfDatafileOBJHANDLE))
             || (!(pod = (PIBMOBJECTDATA)_pvWPObjectData))
           )
#endif
#endif
            brc = XFldObject_parent_WPObject_wpAddToObjUseList(somSelf,
                                                               pUseItem);
#if 0
#ifndef __NOTURBOFOLDERS__
        else
        {
            // object handles for data files disabled,
            // AND this is a data file,
            // AND this is a USAGE_OPENVIEW:
            // run our replacement

            PMPF_ASSOCS(("[%s] entering", _wpQueryTitle(somSelf)));

            if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
            {
                PUSEITEM    puiThis = NULL,
                            puiFirst = NULL;
                PVIEWITEM   pvi = (PVIEWITEM)(pUseItem + 1);
                PRECORDITEM priThis;

                pvi->hwndCnr = NULLHANDLE;
                pvi->pRecord = NULL;

                do
                {
                    if (puiThis = _wpFindUseItem(somSelf, USAGE_RECORD, puiThis))
                    {
                        if (!puiFirst)
                            puiFirst = puiThis;

                        priThis = (PRECORDITEM)(puiThis + 1);

                        if (    WinQueryWindow(priThis->hwndCnr, QW_PARENT)
                             == WinQueryActiveWindow(HWND_DESKTOP)
                           )
                        {
                            pvi->hwndCnr = priThis->hwndCnr;
                            pvi->pRecord = priThis->pRecord;

                            brc = TRUE;
                            break;
                        }
                    }
                } while (puiThis);

                if (    puiFirst
                     && !pvi->hwndCnr
                     && !pvi->pRecord
                   )
                {
                    priThis = (PRECORDITEM)(puiFirst + 1);
                    pvi->hwndCnr = priThis->hwndCnr;
                    pvi->pRecord = priThis->pRecord;
                }

                // the useitems are in a linked list,
                // so do list management
                pUseItem->pNext = NULL;

                if (!(puiThis = pod->puiFirst))
                {
                    PMPF_ASSOCS(("    adding hwnd %lX as first ui",
                           ((PVIEWITEM)(pUseItem + 1))->handle
                         ));

                    pod->puiFirst = pUseItem;
                }
                else
                {
                    while (puiThis->pNext)
                        puiThis = puiThis->pNext;

                    PMPF_ASSOCS(("    adding hwnd %lX as non-first ui",
                           ((PVIEWITEM)(pUseItem + 1))->handle
                         ));

                    puiThis->pNext = pUseItem;
                }

                if (!pod->hevViewItems)
                {
                    PMPF_ASSOCS(("    creating event sem"));

                    DosCreateEventSem(NULL, &pod->hevViewItems, 0, FALSE);
                }

                if (fLocked)
                    fLocked = (BOOL)_wpReleaseObjectMutexSem(somSelf);

                _wpCnrSetEmphasis(somSelf, CRA_INUSE, TRUE);

                _wpLockObject(somSelf);

                brc = TRUE;
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);
#endif
#endif

    return brc;
}

/*
 *@@ wpDeleteFromObjUseList:
 *      reverse to WPObject::wpAddToObjUseList, this removes
 *      a USEITEM from the object's list again.
 *
 *@@added V0.9.20 (2002-08-08) [umoeller]
 *@@changed V1.0.5 (2006-06-04) [pr]: Disabled - causes many problems with false in-use items
 */

SOM_Scope BOOL  SOMLINK xo_wpDeleteFromObjUseList(XFldObject *somSelf,
                                                  PUSEITEM pUseItem)
{
    BOOL    fLocked = FALSE,
            brc = FALSE;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpDeleteFromObjUseList");

#if 0
#ifndef __NOTURBOFOLDERS__
    TRY_LOUD(excpt1)
    {
        PIBMOBJECTDATA pod;

        if (    (cmnQuerySetting(sfDatafileOBJHANDLE))
             || (!(_flObject & OBJFL_WPDATAFILE))
             || (pUseItem->type != USAGE_OPENVIEW)
             || (!(pod = (PIBMOBJECTDATA)_pvWPObjectData))
           )
#endif
#endif
            brc = XFldObject_parent_WPObject_wpDeleteFromObjUseList(somSelf,
                                                                    pUseItem);
#if 0
#ifndef __NOTURBOFOLDERS__
        else
        {
            // object handles for data files disabled,
            // AND this is a data file,
            // AND this is a USAGE_OPENVIEW:
            // run our replacement

            PMPF_ASSOCS(("[%s] entering", _wpQueryTitle(somSelf)));

            if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
            {
                PUSEITEM    puiThis = pod->puiFirst,
                            puiPrev = NULL;

                while (puiThis)
                {
                    if (puiThis == pUseItem)
                    {
                        // found:
                        if (puiPrev)
                            puiPrev->pNext = puiThis->pNext;        // can be NULL
                        else
                            // we were the first in the list:
                            pod->puiFirst = puiThis->pNext;    // can be NULL

                        brc = TRUE;

                        break;
                    }

                    puiPrev = puiThis;
                    puiThis = puiThis->pNext;
                }

                if (brc)
                {
                    // turn off CRA_INUSE if this was the last USAGE_OPENVIEW
                    BOOL fAnotherInuse = FALSE;
                    puiThis = pod->puiFirst;
                    while (puiThis)
                    {
                        if (puiThis->type == USAGE_OPENVIEW)
                        {
                            fAnotherInuse = TRUE;
                            break;
                        }

                        puiThis = puiThis->pNext;
                    }

                    if (!fAnotherInuse)
                        _wpCnrSetEmphasis(somSelf, CRA_INUSE, FALSE);

                    DosPostEventSem(pod->hevViewItems);

                    _wpUnlockObject(somSelf);
                }
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);
#endif
#endif

    return brc;
}

/*
 *@@ wpCnrSetEmphasis:
 *      this WPObject method changes the emphasis flags of
 *      the object's MINIRECORDCORE (and updates all views
 *      where this object is inserted).
 *
 *      We override this method to be able to intercept the
 *      CRA_INUSE emphasis in case the object is currently
 *      used in an XCenter object button widget, which then
 *      needs to be repainted.
 *
 *      With V0.9.13, I tried overriding
 *      wpAddTo/DeleteFromObjectUseList, which didn't quite
 *      work.
 *
 *@@added V0.9.14 (2001-07-30) [umoeller]
 *@@changed V0.9.19 (2002-05-07) [umoeller]: reversed call order to fix XCenter object button display
 */

SOM_Scope BOOL  SOMLINK xo_wpCnrSetEmphasis(XFldObject *somSelf,
                                            ULONG ulEmphasisAttr,
                                            BOOL fTurnOn)
{
    XFldObjectMethodDebug("XFldObject","xo_wpCnrSetEmphasis");

    // moved parent call to top
    // V0.9.19 (2002-05-07) [umoeller]
    if (XFldObject_parent_WPObject_wpCnrSetEmphasis(somSelf,
                                                    ulEmphasisAttr,
                                                    fTurnOn))
    {
        XFldObjectData *somThis = XFldObjectGetData(somSelf);

        if (    (ulEmphasisAttr & CRA_INUSE)
             && (_pvllWidgetNotifies)
           )
        {
            // we have windows that requested notifications:
            // go thru list
            PLISTNODE pNode = lstQueryFirstNode(_pvllWidgetNotifies);
            while (pNode)
            {
                WinPostMsg((HWND)pNode->pItemData,
                           WM_CONTROL,
                           MPFROM2SHORT(ID_XCENTER_CLIENT,
                                        XN_INUSECHANGED),
                           (MPARAM)somSelf);
                pNode = pNode->pNext;
            }
        }

        return TRUE;
    }

    return FALSE;
}

/*
 *@@ FigureOutWPSProc:
 *      this function figures out the PFNWP address of
 *      the window procedure that the WPS uses for
 *      subclassing container owners. This will only
 *      get called once in the WPS's lifetime, hopefully.
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

PFNWP FigureOutWPSProc(WPObject *pobjTest)
{
    // first call: create test frame to figure
    // out WPS subclass proc
    ULONG   flCreate = FCF_BORDER;
    HWND    hwndFrame,
            hwndTestCnr;
    PFNWP   pfnwpReturn = NULL;

    if (hwndFrame = WinCreateStdWindow(HWND_DESKTOP,
                                       0,       // NOT VISIBLE!
                                       &flCreate,
                                       WC_CONTAINER,
                                       "dummy",
                                       WS_VISIBLE,
                                       NULLHANDLE,
                                       0,
                                       &hwndTestCnr))
    {
        POINTL ptlIcon = {10, 10};
        if (XFldObject_parent_WPObject_wpCnrInsertObject(pobjTest,
                                                         hwndTestCnr,
                                                         &ptlIcon,
                                                         NULL,
                                                         NULL))
        {
            // _wpCnrInsertObject can return a record even though
            // the thing wasn't even inserted, but what the hell

            pfnwpReturn = (PFNWP)WinQueryWindowPtr(hwndFrame, QWP_PFNWP);
            PMPF_ICONREPLACEMENTS(("test frame was subclassed with 0x%lX", pfnwpReturn));

            _wpCnrRemoveObject(pobjTest, hwndTestCnr);
        }

        WinDestroyWindow(hwndFrame);
    }

    return pfnwpReturn;
}

/*
 *@@ ContainerSubclassHack:
 *      subclasses the given container owner (which should be
 *      a WC_FRAME) with the window procedure that the
 *      WPS normally subclasses windows with in the original
 *      _wpCnrInsertObject method.
 *
 *      Do not look at this code too closely, it can do great
 *      damage to your programming ethics.
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

STATIC BOOL ContainerSubclassHack(HWND hwndOwner,
                                  WPObject *pobjTest)
{
    static HMTX     s_hmtxCnr = NULLHANDLE;
    static PFNWP    s_pfnwpCnrSubclass = NULL;
    BOOL            fLocked = FALSE,
                    brc = FALSE;

    TRY_LOUD(excpt1)
    {
        if (!s_hmtxCnr)
            fLocked = !DosCreateMutexSem(NULL, &s_hmtxCnr, 0, TRUE);
        else
            fLocked = !DosRequestMutexSem(s_hmtxCnr, SEM_INDEFINITE_WAIT);

        if (fLocked)
        {
            PFNWP pfnwp;

            if (!(pfnwp = (PFNWP)WinQueryWindowPtr(hwndOwner,
                                                   -6)))        // interesting window word here...
            {
                // alright, here's a container owner that has _not_ yet
                // been subclassed: then we must do it ourselves. This
                // is really ugly, since the WPS container subclass
                // proc does not seem to be exported... so on the
                // first call, we create a stupid test frame with
                // a container, insert the object by calling the
                // original WPS method, and remember the window proc,
                // which (hopefully) is the one the WPS uses for
                // subclassing.

                // This case is very expensive on the first call,
                // but very rare also, and from my testing it works
                // (see the HPFS386 driver dialog in "OS/2 kernel"
                // for example, which inserts an object into a
                // plain container).

                PMPF_ICONREPLACEMENTS(("got NULL pfnwp!"));

                if (    (s_pfnwpCnrSubclass)
                     || (s_pfnwpCnrSubclass = FigureOutWPSProc(pobjTest))
                   )
                {
                    // alright, be daring
                    if (pfnwp = WinSubclassWindow(hwndOwner,
                                                  s_pfnwpCnrSubclass))
                    {
                        // store original pfnwp in that special window word
                        brc = WinSetWindowPtr(hwndOwner, -6, (PVOID)pfnwp);
                    }

                    PMPF_ICONREPLACEMENTS(("WinSubclassWindow(0x%lX) returned 0x%lX",
                            s_pfnwpCnrSubclass,
                            pfnwp));
                }
            }
            else
            {
                PMPF_ICONREPLACEMENTS(("got orig proc 0x%lX", pfnwp));
                brc = TRUE;
            }
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        DosReleaseMutexSem(s_hmtxCnr);

    if (!brc)
    {
        cmnLog(__FILE__, __LINE__, __FUNCTION__,
               "Cannot subclass container owner.");
        G_fInsertRecordCrashed = TRUE;
    }

    return brc;
}

/*
 *@@ wpCnrInsertObject:
 *      this WPObject method inserts an object into a container.
 *
 *      We override this method with a giant amount of code for
 *      the tiny little feature to allow for setting the CRA_OWNERDRAW
 *      bit in MINIRECORDCORE.flRecordAttr.
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

SOM_Scope PMINIRECORDCORE  SOMLINK xo_wpCnrInsertObject(XFldObject *somSelf,
                                                        HWND hwndCnr,
                                                        PPOINTL pptlIcon,
                                                        PMINIRECORDCORE preccParent,
                                                        PRECORDINSERT pRecInsert)
{
#ifndef __NOTURBOFOLDERS__
    PMINIRECORDCORE pmrc;
    HWND            hwndOwner;
#endif

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpCnrInsertObject");

#ifndef __NOTURBOFOLDERS__
    // some sanity checks first
    if (    (!G_fInsertRecordCrashed)
         && (cmnQuerySetting(sfTurboFolders))
         && (pmrc = (((PIBMOBJECTDATA)_pvWPObjectData)->pmrc))
         && (hwndCnr)
         && (hwndOwner = WinQueryWindow(hwndCnr, QW_OWNER))
            // now, it is _this_ method that subclasses the
            // container owner to give any container all
            // the WPS features...
         && (ContainerSubclassHack(hwndOwner, somSelf))
       )
    {
        PMINIRECORDCORE pmrcReturn = NULL;
        PUSEITEM        pui = NULL;

        TRY_LOUD(excpt1)
        {
            ULONG   flStyle = (((PIBMOBJECTDATA)_pvWPObjectData)->flStyle);

            // the WPS allocates a special ULONG before the
            // actual record, which is zero until the object
            // has been inserted at least once
            PULONG  pulEverInserted = (((PULONG)pmrc) - 1);

            // allocate USEITEM with RECORDITEM and
            // set up the flRecordAttr fields
            if (pui = _xwpPrepareInsertRecord(somSelf,
                                              hwndCnr,
                                              pmrc,
                                              flStyle,
                                              !(*pulEverInserted)))
            {
                // 2) set up the MINIRECORDCORE

                pmrc->ptlIcon.x = pptlIcon->x;
                pmrc->ptlIcon.y = pptlIcon->y;

                // 3) now go INSERT the damn thing; here I have finally
                // found the flag I have been looking for for so long,
                // if this instance data BOOL is FALSE, the WPS does
                // _not_ insert the record automatically (during folder
                // populate, I guess)
                if (((PIBMOBJECTDATA)_pvWPObjectData)->fAutoInsert)
                {
                    if (_wpAddToObjUseList(somSelf, pui))
                    {
                        // if we were given a NULL RECORDINSERT,
                        // fake one cos CM_INSERTRECORD needs it

                        RECORDINSERT ri2;

                        if (!pRecInsert)
                        {
                            static const RECORDINSERT s_riTemplate =
                                {
                                    sizeof(RECORDINSERT),       // cb
                                    (PRECORDCORE)CMA_END,       // pRecordOrder
                                    NULL,                       // record parent
                                    TRUE,                       // fInvalidateRecord
                                    CMA_TOP,                    // zOrder
                                    1                           // cRecordsInsert
                                };

                            memcpy(&ri2, &s_riTemplate, sizeof(RECORDINSERT));
                            ri2.pRecordParent = (PRECORDCORE)preccParent;
                            pRecInsert = &ri2;
                        }

                        if (WinSendMsg(hwndCnr,
                                       CM_INSERTRECORD,
                                       (MPARAM)pmrc,
                                       (MPARAM)pRecInsert))
                            pui = NULL;
                        else
                            _wpDeleteFromObjUseList(somSelf, pui);
                            // pui freed below
                    }
                } // if (((PIBMOBJECTDATA)_pvWPObjectData)->fAutoInsert)
                else
                    if (_wpAddToObjUseList(somSelf, pui))
                        pui = NULL;

                // interestingly, the WPS returns the record even
                // if the insert failed! if we don't do this, the
                // whole WPS goes boom...

                pmrcReturn = pmrc;

            } // if (pui = (PUSEITEM)ShlMalloc(CBITEM, NULL))
        }
        CATCH(excpt1)
        {
            pmrcReturn = NULL;
            G_fInsertRecordCrashed = TRUE;      // don't _ever_ call this again!
        } END_CATCH();

        // thou shalt not leak
        if (pui)
            ShlFreeMem(pui);

        return pmrcReturn;
    }
#endif

    return XFldObject_parent_WPObject_wpCnrInsertObject(somSelf,
                                                        hwndCnr,
                                                        pptlIcon,
                                                        preccParent,
                                                        pRecInsert);
}

/*
 *@@ wpRequestObjectMutexSem:
 *
 *@@added V0.9.20 (2002-08-04) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xo_wpRequestObjectMutexSem(XFldObject *somSelf,
                                                    ULONG ulTimeout)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    // XFldObjectMethodDebug("XFldObject","xo_wpRequestObjectMutexSem");

#ifdef USE_FASTMUTEX
    return semRequest((PFASTMTX)&_mtxObject);
#else
    return XFldObject_parent_WPObject_wpRequestObjectMutexSem(somSelf,
                                                              ulTimeout);
#endif
}

/*
 *@@ wpAssertObjectMutexSem:
 *      this WPObject instance method verifies that an object's mutex
 *      semaphore is held for the current thread.
 *
 *@@added V0.9.20 (2002-08-04) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpAssertObjectMutexSem(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    // XFldObjectMethodDebug("XFldObject","xo_wpAssertObjectMutexSem");

#ifdef USE_FASTMUTEX
    return semAssert((PFASTMTX)&_mtxObject);
#else
    return XFldObject_parent_WPObject_wpAssertObjectMutexSem(somSelf);
#endif
}

/*
 *@@ wpReleaseObjectMutexSem:
 *
 *@@added V0.9.20 (2002-08-04) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xo_wpReleaseObjectMutexSem(XFldObject *somSelf)
{
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    // XFldObjectMethodDebug("XFldObject","xo_wpReleaseObjectMutexSem");

#ifdef USE_FASTMUTEX
    return semRelease((PFASTMTX)&_mtxObject);
#else
    return XFldObject_parent_WPObject_wpReleaseObjectMutexSem(somSelf);
#endif
}

/*
 *@@ wpFilterMenu:
 *      this WPObject instance method was new with Warp 4
 *      allows the object to filter out standard menu items
 *      in a more fine-grained way than wpModifyPopupMenu.
 *      For one, this gives the object the menu type that
 *      is being built, and secondly this allows for
 *      filtering more items than the 32 bits of the filter
 *      flag in wpFilterPopupMenu have room for.
 *
 *      Once again however, the description of this method
 *      in WPSREF is unusable. As input, this method
 *      receives an array of ULONGs in pFlags. wpobject.h
 *      in the 4.5 Toolkit lists the CTXT_* flags together
 *      with the ULONG array item index where each flag
 *      applies. pFlags->Flags[0] is equivalent to the flags
 *      byte of the old wpFilterPopupMenu method.
 *
 *      Note also that the WPS appears to ignore the return
 *      value of this function.
 *
 *      With V1.0.1, we override this instead of
 *      wpFilterPopupMenu for folders too.
 *
 *@@added V1.0.0 (2002-08-31) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpFilterMenu(XFldObject *somSelf,
                                        FILTERFLAGS* pFlags,
                                        HWND hwndCnr,
                                        BOOL fMultiSelect,
                                        ULONG ulMenuType,
                                        ULONG ulView,
                                        ULONG ulReserved)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpFilterMenu");

    PMPF_MENUS(("flags[0]: %08X, flags[1]: %08X, flags[2]: %08X",
                pFlags->Flags[0], pFlags->Flags[1], pFlags->Flags[2]));

    XFldObject_parent_WPObject_wpFilterMenu(somSelf,
                                            pFlags,
                                            hwndCnr,
                                            fMultiSelect,
                                            ulMenuType,
                                            ulView,
                                            ulReserved);

    PMPF_MENUS(("parent flags[0]: %08X, flags[1]: %08X, flags[2]: %08X",
                pFlags->Flags[0], pFlags->Flags[1], pFlags->Flags[2]));

    // if object has been deleted already (ie. is in trashcan),
    // remove delete... not that I can see how we can get a context
    // menu for the object then ;-)
    if (_xwpQueryDeletion(somSelf, NULL, NULL))    // V0.9.20 (2002-07-25) [umoeller]
        pFlags->Flags[0] &= ~CTXT_DELETE; // V0.9.5 (2000-09-20) [pr]

    // now suppress default menu items according to
    // Global Settings;
    // the global "WPSSetting" settings are ready-made
    // for this function; the "Workplace Shell" notebook page
    // for removing menu items set these fields with the proper
    // CTXT_xxx flags
    pFlags->Flags[0] &= ~(cmnQuerySetting(mnuQueryMenuWPSSetting(somSelf)));

    if (cmnQuerySetting(mnuQueryMenuXWPSetting(somSelf)) & XWPCTXT_LOCKEDINPLACE)
        // remove WPObject's "Lock in place" submenu
        pFlags->Flags[1] &= ~CTXT_LOCKEDINPLACE;
        // winhDeleteMenuItem(hwndMenu, WPMENUID_LOCKEDINPLACE); // ID_WPM_LOCKINPLACE);

    return TRUE;
}

/*
 *@@ wpModifyMenu:
 *      this WPObject instance method was new with Warp 4 and
 *      allows the object to manipulate its menu in a more
 *      fine-grained way than wpModifyPopupMenu.
 *
 *      With V1.0.0, while adding support for the split view
 *      to the menu methods, I finally got tired of all the
 *      send-msg hacks to get Warp 4 menu items to work and
 *      decided to finally break Warp 3 support for XWorkplace.
 *      It's been fun while it lasted, but enough is enough.
 *      We are now overriding this method directly through
 *      the IDL files.
 *
 *      ulMenuType will be one of the following:
 *
 *      --  MENU_OBJECTPOPUP: Pop-up menu for the object icon.
 *          This can come in for any object.
 *
 *      --  MENU_OPENVIEWPOPUP: Pop-up menu for an open view.
 *          I think this can reasonably only come in for folders,
 *          although it seems to be handled by the WPObject method.
 *
 *      --  MENU_FOLDERPULLDOWN: Pull-down menu for a folder.
 *          This comes in for folders only.
 *
 *      --  MENU_EDITPULLDOWN: Pull-down menu for the Edit menu option.
 *          This comes in TWICE, first on the selected object in
 *          the view's container (if any) with ulView == CLOSED_ICON,
 *          and then a second time on the view's folder with ulView
 *          set to the folder's current view.
 *
 *      --  MENU_VIEWPULLDOWN: Pull-down menu for the View menu option.
 *          This comes in for folders only.
 *
 *      --  MENU_SELECTEDPULLDOWN: Pull-down menu for the Selected menu option.
 *          This comes in for non-folder objects also to let the object
 *          decide what options it wants to present in the folder's
 *          "Selected" pulldown.
 *
 *      --  MENU_HELPPULLDOWN: Pull-down menu for the Help menu option.
 *          This comes in for folders only.
 *
 *      --  MENU_TITLEBARPULLDOWN: this is listed in the toolkit headers,
 *          but not described in WPSREF. I think this is what comes in
 *          if the system menu is being built for an open object view.
 *
 *@@added V1.0.0 (2002-08-31) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpModifyMenu(XFldObject *somSelf,
                                        HWND hwndMenu,
                                        HWND hwndCnr,
                                        ULONG iPosition,
                                        ULONG ulMenuType,
                                        ULONG ulView,
                                        ULONG ulReserved)
{
    BOOL brc;
    XFldObjectMethodDebug("XFldObject","xo_wpModifyMenu");

    if (brc = XFldObject_parent_WPObject_wpModifyMenu(somSelf,
                                                      hwndMenu,
                                                      hwndCnr,
                                                      iPosition,
                                                      ulMenuType,
                                                      ulView,
                                                      ulReserved))
    {
        XFldObjectData *somThis = XFldObjectGetData(somSelf);

        PMPF_MENUS(("[%s] hwndMenu 0x%lX hwndCnr 0x%lX",
                    _wpQueryTitle(somSelf),
                    hwndMenu,
                    hwndCnr));
        PMPF_MENUS(("   type = 0x%lX (%s), view = 0x%lX (%s)",
                    ulMenuType,
                    (ulMenuType == MENU_OBJECTPOPUP) ? "MENU_OBJECTPOPUP"
                    : (ulMenuType == MENU_OPENVIEWPOPUP) ? "MENU_OPENVIEWPOPUP"
                    : (ulMenuType == MENU_TITLEBARPULLDOWN) ? "MENU_TITLEBARPULLDOWN"
                    : (ulMenuType == MENU_TITLEBARPULLDOWNINT) ? "MENU_TITLEBARPULLDOWNINT"
                    : (ulMenuType == MENU_FOLDERPULLDOWN) ? "MENU_FOLDERPULLDOWN"
                    : (ulMenuType == MENU_VIEWPULLDOWN) ? "MENU_VIEWPULLDOWN"
                    : (ulMenuType == MENU_HELPPULLDOWN) ? "MENU_HELPPULLDOWN"
                    : (ulMenuType == MENU_EDITPULLDOWN) ? "MENU_EDITPULLDOWN"
                    : (ulMenuType == MENU_SELECTEDPULLDOWN) ? "MENU_SELECTEDPULLDOWN"
                    : (ulMenuType == MENU_FOLDERMENUBAR) ? "MENU_FOLDERMENUBAR"
                    : (ulMenuType == MENU_USER) ? "MENU_USER"
                    : "unknown",
                    ulView,
                    cmnIdentifyView(ulView)
                    ));

        if (cmnQuerySetting(sfFixLockInPlace)) // V0.9.7 (2000-12-10) [umoeller]
        {
            // get text first... this saves us our own NLS resource
            PSZ pszLockInPlace;
            if (pszLockInPlace = winhQueryMenuItemText(hwndMenu, WPMENUID_LOCKEDINPLACE)) // ID_WPM_LOCKINPLACE))
            {
                MENUITEM mi;
                if (winhQueryMenuItem(hwndMenu,
                                      WPMENUID_LOCKEDINPLACE,
                                      FALSE,
                                      &mi))
                {
                    // delete old (incl. submenu)
                    winhDeleteMenuItem(hwndMenu, WPMENUID_LOCKEDINPLACE);
                    // insert new
                    winhInsertMenuItem(hwndMenu,
                                       mi.iPosition,        // at old position
                                       WPMENUID_LOCKEDINPLACE,
                                       pszLockInPlace,
                                       MIS_TEXT,
                                       (_wpQueryStyle(somSelf) & OBJSTYLE_LOCKEDINPLACE)
                                          ? MIA_CHECKED
                                          : 0);
                }

                free(pszLockInPlace);
            }
        }

        // now that the menu is completely built, let's add hotkey
        // descriptions, but DONT do this for folders or data files,
        // because those menu items will only be added later... for
        // folders, we call this function in XFolder::wpMenuItemSelected
        if (   !(_flObject & OBJFL_WPFILESYSTEM)
            && !(ctsIsDisk(somSelf))  // V1.0.0 (2002-09-23) [pr]: Stop double hotkeys on disks
           )
            fdrAddHotkeysToMenu(somSelf,
                                hwndCnr,
                                hwndMenu,
                                ulMenuType);
    }

    return brc;
}

/*
 *@@ wpMenuItemSelected:
 *      this WPObject method processes menu selections.
 *      This must be overridden to support new menu
 *      items which have been added in wpModifyPopupMenu.
 *
 *      Note that the WPS invokes this method upon every
 *      object which has been selected in the container.
 *      That is, if three objects have been selected and
 *      a menu item has been selected for all three of
 *      them, all three objects will receive this method
 *      call. This is true even if FALSE is returned from
 *      this method by one of the objects.
 *
 *      In order to be able to process all objects at
 *      once, we now have XFolder::xwpProcessViewCommand,
 *      which can intercept the menu ID even before this
 *      method is invoked on each object and process all
 *      objects in one flush.
 *
 *      We override this to move objects into the
 *      trash can instead, if necessary.
 *
 *      Note: This method normally doesn't get called
 *      during the regular WPS file operations once
 *      the trash can has been enabled because XFolder
 *      intercepts all file operations commands and
 *      performs the required actions directly without
 *      calling this method. However, this method still
 *      gets called by WinDestroyObject and the REXX
 *      counterpart, SysDestroyObject.
 *
 *@@changed V0.9.7 (2000-12-10) [umoeller]: added "fix lock in place"
 *@@changed V0.9.7 (2001-01-15) [umoeller]: added WPMENUID_DELETE if trash can is enabled
 *@@changed V0.9.9 (2001-03-10) [pr]: this screwed up print jobs, now checking for WPTransient
 *@@changed V0.9.16 (2001-12-06) [umoeller]: fixed shredder deleting into trash can
 *@@changed V1.0.0 (2002-09-12) [umoeller]: re-enabled WPMENUID_DELETE to catch some more delete situations
 *@@changed V1.0.6 (2006-09-30) [pr]: fix Shredder delete @@fixes 413
 *@@changed V1.0.8 (2008-01-05) [pr]: prevent untrashable objects going to trash @@fixes 1035
 *@@changed V1.0.8 (2008-06-05) [pr]: Shredder delete no longer has shift option - eCS bug 2036
 *@@changed V1.0.8 (2008-06-05) [pr]: Above also fixes MENUITEMSELECTED=109 bug @@fixes 1071
 */

SOM_Scope BOOL  SOMLINK xo_wpMenuItemSelected(XFldObject *somSelf,
                                              HWND hwndFrame,
                                              ULONG ulMenuId)
{
    BOOL        brc = FALSE,
                fCallDefault = FALSE;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpMenuItemSelected");

    switch (ulMenuId)
    {
        // this is normally never reached because fdrWMCommand
        // intercepts this before wpMenuItemSelected gets called
        // for 99,9% of all cases, but NOT in the case where we
        // have a "delete" command from a folder's system menu

        // re-enabled WPMENUID_DELETE catch here
        // V1.0.0 (2002-09-12) [umoeller]

        // From my testing, WinDestroyObject doesn't come through here.
        // Deletes using the Shredder do come through here, so use the
        // Shift key modifier to do the non-default operation and send
        // them to the Trash Can if enabled. As elsewhere, Transient and
        // Printer objects are prevented from going into the TrashCan.
        // V1.0.6 (2006-09-30) [pr]
        // Nah, don't do this any more - it has undesirable effects
        // V1.0.8 (2008-06-05) [pr]

        case WPMENUID_DELETE:
        {
            if (
#ifndef __ALWAYSTRASHANDTRUEDELETE__
                    (cmnQuerySetting(sfReplaceDelete))
                 &&
#endif
                    (!cmnQuerySetting(sfAlwaysTrueDelete))
                 && !hwndFrame  // V1.0.8 (2008-06-05) [pr]: hwndFrame not null if GUI i.e. Shredder
                 && (ctsIsTrashable(somSelf))  // V1.0.8 (2008-01-05) [pr]
               )
            {
                PMPF_FOPS(("WPMENUID_DELETE"));

                cmnDeleteIntoDefTrashCan(somSelf);
                brc = TRUE;     // processed
            }
            else
                fCallDefault = TRUE;
        }
        break;

        case WPMENUID_LOCKEDINPLACE:    // V0.9.7 (2000-12-10) [umoeller]
            if (cmnQuerySetting(sfFixLockInPlace))
            {
                // we have replaced the "lock in place" submenu:
                // we must then intercept this menu item...
                ULONG ulStyle = _wpQueryStyle(somSelf);
                if ((ulStyle & OBJSTYLE_LOCKEDINPLACE) == 0)
                    ulStyle |= OBJSTYLE_LOCKEDINPLACE;
                else
                    ulStyle &= ~OBJSTYLE_LOCKEDINPLACE;
                _wpSetStyle(somSelf, ulStyle);
                _wpSaveDeferred(somSelf);
            }
        break;

        default:
            fCallDefault = TRUE;
    }

    if (fCallDefault)
        brc = XFldObject_parent_WPObject_wpMenuItemSelected(somSelf,
                                                            hwndFrame,
                                                            ulMenuId);
    return brc;
}

/*
 *@@ wpMenuItemHelpSelected:
 *      this instance method gets called when help is
 *      requested for a menu item in the object's context menu.
 *
 *      We are finally giving decent help on the "Delete"
 *      menu item, if the delete replacements are enabled.
 *
 *@@added V0.9.19 (2002-04-02) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpMenuItemHelpSelected(XFldObject *somSelf,
                                                  ULONG MenuId)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpMenuItemHelpSelected");

    if (    (MenuId == WPMENUID_DELETE)
#ifndef __ALWAYSTRASHANDTRUEDELETE__
         && (cmnQuerySetting(sfReplaceDelete))
#endif
       )
    {
        cmnDisplayHelp(somSelf,
                       ID_XSH_FOPS_DELETE);
        return TRUE;
    }

    return XFldObject_parent_WPObject_wpMenuItemHelpSelected(somSelf,
                                                             MenuId);
}

/*
 *@@ wpDisplayHelp:
 *      this WPObject method displays the given help panel
 *      (help library and help panel ID).
 *
 *      We override this method to be able to hack ourselves
 *      into some default WPS help panels that are displayed
 *      from within the original IBM code.
 *
 *@@added V0.9.20 (2002-07-12) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpDisplayHelp(XFldObject *somSelf,
                                         ULONG HelpPanelId,
                                         PSZ HelpLibrary)
{
    static const struct
    {
        ULONG   ulOldId,
                ulReplacementId,
                ulTemplateReplacemendId;
    } aReplacements[] =
    {
        // menu item help

        // shutdown menu item help panel, this sucks:
        4001,   ID_XMH_XSHUTDOWN, 0,
        // "System Setup" menu item
        21644,  ID_XSH_OS2SYSSETUPMENUITEM, 0,
        // "Open parent" menu item
        32112,  ID_XSH_FDR_OPENPARENT, 0,
        // "Create another" menu items
        1200,   ID_XSH_CRANOTHERMENUITEM, 0,
        1201,   ID_XSH_COPYMENUITEM, 0,
        1202,   ID_XSH_MOVEMENUITEM, 0,
        1203,   ID_XSH_CRSHADOWMENUITEM, 0,
        // "Refresh now" menu item
        1296,   ID_XMH_REFRESH, 0,
        // "Check disk" menu item
        8018,   ID_XMH_CHECKDISK, 0,
        // "Format disk" menu item
        8019,   ID_XMH_FORMATDISK, 0,
        // "Manage volumes" menu item
        8020,   ID_XMH_MANAGEVOLUMES, 0,

        // object class default help panels

        // Desktop
        4000,   ID_XSH_DESKTOP, 0,
        // default program object help with template (just to
        // make sure, but see below)
        4083,   ID_XSH_PROGRAMOBJ_MAIN, ID_XSH_PROGRAMOBJ_TEMPLATE,
        // default startup folder help
        8002,   ID_XSH_STARTUPFOLDER, 0,
        // "Templates" folder default help
        15680,  ID_XSH_OS2TEMPLATES, 0,
        // Minimized window viewer default help
        9286,   ID_XSH_OS2MINWINV, 0,
        // "Shredder" default help
        1190,   ID_XSH_SHREDDER, 0,
        // "Drives" folder default help
        8013,   ID_XSH_OS2DRIVESFDR, 0,
        // "Diskette" disk object help
        8014,   ID_XSH_DISK_DISKETTE, 0,
        // "Hard disk" disk object help
        8015,   ID_XSH_DISK_HARDDISK, 0,
        // "CD-ROM" disk object help
        8024,   ID_XSH_DISK_CDROM, 0,
        // Winos2 setup class help
        4203,   ID_XSH_SYSSETUP_WINOS2, 0,
        // "Network drive" disk object help
        8025,   ID_XSH_DISK_NETWORK, 0,
        // WPNetwork class default help
        30000,  ID_XSH_WPNETWORK, 0,
        // WPNetgrp ("Resource Browser") default help
        30001,  ID_XSH_WPNETGRP, 0,

        // object default help panels that are totally brain-dead
        // and display template help even for regular objects:

        // WPBitmap:
        15682,  ID_XSH_WPBITMAP, ID_XSH_WPBITMAP_TEMPLATE,
        // WPIcon:
        15678,  ID_XSH_WPICON, ID_XSH_WPICON_TEMPLATE,
        // WPPointer: use WPIcon too
        21652,  ID_XSH_WPPOINTER, ID_XSH_WPPOINTER_TEMPLATE,
        // WPPif
        4159,   ID_XSH_WPPIF, ID_XSH_WPPIF_TEMPLATE,
        // WPMet
        4160,   ID_XSH_WPMET, ID_XSH_WPMET_TEMPLATE,

        // WPS help panels set via HELPPANEL
        // for specific objects:

        // "OS/2 system" folder
        4002,   ID_XSH_OS2SYSTEM, 0,
        // "System Setup" folder
        1220,   ID_XSH_OS2SYSSETUPFDR, 0,
        // "Command prompts" folder
        8008,   ID_XSH_OS2CMDPROMPTSFDR, 0,
        // program templates automatically receive a different
        // help panel, probably from WPProgram::wpQueryDefaultHelp
        15684,  ID_XSH_PROGRAMOBJ_TEMPLATE, 0,
        // OS/2 full screen etc. objects
        8009,   ID_XSH_OS2CMD_OS2FULL, 0,
        8010,   ID_XSH_OS2CMD_OS2WIN, 0,
        8011,   ID_XSH_OS2CMD_DOSFULL, 0,
        8012,   ID_XSH_OS2CMD_DOSWIN, 0,
        8022,   ID_XSH_OS2CMD_WIN16, 0,
        // "Information folder"
        13092,  ID_XSH_OS2INFORMATIONFDR, 0,
        // "OS/2 Warp Command Reference" object
        9301,   ID_XSH_OS2CMDREFINF, 0,
        // "Games" folder
        13091,  ID_XSH_GAMESFDR, 0,
        // "Utilites" folder
        13090,  ID_XSH_UTILITIESFDR, 0,
        // "Clipboard viewer" program object
        20274,  ID_XSH_CLIPVIEW, 0,
        // "Enhanced editor" program object
        20278,  ID_XSH_EPM, 0,
        // "Icon editor" program object
        20279,  ID_XSH_ICONEDIT, 0,
        // "System editor" program object
        9289,   ID_XSH_EEXE, 0,
        // "Picture viewer" program object (not assigned with eCS!)
        7126,   ID_XSH_PICVIEW, 0,
        // "CPU Monitor" program object
        20284,  ID_XSH_CPUMONITOR, 0,
        // "Seek and scan files" program object
        20285,  ID_XSH_SEEKANDSCAN, 0,
        // "WarpCenter" default help
        33784,  ID_XSH_WARPCENTER, 0,
    };

    PCSZ    pcszXWPHelp = cmnQueryHelpLibrary();
    BOOL    fOurHelp = FALSE;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpDisplayHelp");

#ifndef __ALWAYSREPLACEHELP__
    if (cmnQuerySetting(sfHelpReplacements))
#endif
    {
        // WPS passes WPHELP.HLP as NULL!
        if (    (!HelpLibrary)
             || (!*HelpLibrary)
             || (!stricmp(HelpLibrary, "WPHELP.HLP"))
             || (strhistr(HelpLibrary, "\\WPHELP.HLP"))
           )
        {
            ULONG ul,
                  ulNewHelpPanelId = 0;
            for (ul = 0;
                 ul < ARRAYITEMCOUNT(aReplacements);
                 ++ul)
            {
                if (HelpPanelId == aReplacements[ul].ulOldId)
                {
                    // use template help if set and somSelf is
                    // a template indeed
                    if (    (ulNewHelpPanelId = aReplacements[ul].ulTemplateReplacemendId)
                         && (_wpQueryStyle(somSelf) & OBJSTYLE_TEMPLATE)
                       )
                        break;

                    // not template, or we have no help panel if it is:
                    ulNewHelpPanelId = aReplacements[ul].ulReplacementId;
                    break;
                }
            }

            // there is a bug in the WPS in that the default help
            // for a minimized window in the miniwin viewer is
            // passed as NULL, 0; so display our replacement help
            // for the miniwin viewer instead
            if (    (!HelpPanelId)
                 && (ctsIsMinWin(somSelf))
               )
                ulNewHelpPanelId = ID_XSH_OS2MINWINV;

            if (ulNewHelpPanelId)
            {
                HelpPanelId = ulNewHelpPanelId;
                HelpLibrary = (PSZ)pcszXWPHelp;
            }
            else
                // no help panel ID found:
                // but help is supposed to be in wphelp.hlp
                // so enforce that
                HelpLibrary = "WPHELP.HLP";
        }

        fOurHelp = (!strcmp(HelpLibrary, pcszXWPHelp));

        #if 0
            if ( (!fOurHelp) && (HelpPanelId != 1) )      // used by unlockhelp.cmd
            {
                CHAR sz[500];
                sprintf(sz,
                        "lib: \"%s\", panel: %d",
                        (HelpLibrary) ? HelpLibrary : "NULL",
                        HelpPanelId);
                winhDebugBox(NULLHANDLE,
                             __FUNCTION__,
                             sz);
            }
        #endif
    }

    if (!XFldObject_parent_WPObject_wpDisplayHelp(somSelf,
                                                  HelpPanelId,
                                                  HelpLibrary))
    {
        // FAILED: if this was one of our items, report
        if (fOurHelp)
            cmnHelpNotFound(HelpPanelId);

        return FALSE;
    }

    return TRUE;
}

/*
 *@@ wpSwitchTo:
 *      We override this method to make it work properly.
 *      The original IBM code sometimes activates the window
 *      without raising it to the top of the Z-order. Bizarre!
 *
 *@@added V1.0.6 (2006-11-09) [pr]: @@fixes 319
 *@@changed V1.0.7  (2006-12-22) [pr]: use switch handles on programs @@fixes 902
 */

SOM_Scope BOOL  SOMLINK xo_wpSwitchTo(XFldObject *somSelf, ULONG View)
{
    BOOL brc = FALSE;
    ULONG ulViewType = VIEW_ANY;
    WPObject *pobjLock = NULL;
    HWND hwnd = NULLHANDLE;
    HSWITCH hsw = NULLHANDLE;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSwitchTo");

    if (View == OPEN_DEFAULT)
        View = _wpQueryDefaultView(somSelf);

    switch(View)
    {
        case OPEN_CONTENTS:
            ulViewType = VIEW_CONTENTS;
            break;

        case OPEN_DETAILS:
            ulViewType = VIEW_DETAILS;
            break;

        case OPEN_HELP:
            ulViewType = VIEW_HELP;
            break;

        case OPEN_RUNNING:
            ulViewType = VIEW_RUNNING;
            break;

        case OPEN_SETTINGS:
            ulViewType = VIEW_SETTINGS;
            break;

        case OPEN_TREE:
            ulViewType = VIEW_TREE;
    }

    PMPF_KEYS(("Object: 0x%08X, View: %u", somSelf, View));
    TRY_LOUD(excpt1)
    {
        if (pobjLock = (!_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT)) ? somSelf : NULL)
        {
            PVIEWITEM pViewItem = NULLHANDLE;

            while (   (!hwnd)
                   && (pViewItem = _wpFindViewItem(somSelf, ulViewType, pViewItem))
                  )
            {
                PMPF_KEYS(("view %d, handle 0x%08X", pViewItem->view, pViewItem->handle));
                if (pViewItem->view == View)
                {
                    // V1.0.7  (2006-12-22) [pr]
                    if (View == OPEN_RUNNING)
                        hsw = winhHSWITCHfromHAPP((HAPP) pViewItem->handle);
                    else
                        hwnd = (HWND) pViewItem->handle;

                    break;
                }
            }
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (pobjLock)
        _wpReleaseObjectMutexSem(pobjLock);

    // V1.0.7  (2006-12-22) [pr]
    if (View == OPEN_RUNNING)
    {
        if (hsw)
            brc = !WinSwitchToProgram(hsw);
    }
    else
        if (hwnd)
        {
            SWP swp;

            if (!WinIsWindowVisible(hwnd))
                WinShowWindow(hwnd, TRUE);

            if (   WinQueryWindowPos(hwnd, &swp)
                && (swp.fl & SWP_MINIMIZE)
               )
                WinSetWindowPos(hwnd, NULLHANDLE, 0, 0, 0, 0, SWP_RESTORE);

            brc = WinSetActiveWindow(HWND_DESKTOP, hwnd);
        }

    return (brc ? brc : XFldObject_parent_WPObject_wpSwitchTo(somSelf, View));
}

/*
 *@@ wpAddSettingsPages:
 *      this WPObject instance method gets called by the WPS
 *      when the Settings view is opened to have all the
 *      settings page inserted into hwndNotebook. Override
 *      this method to add new settings pages to either the
 *      top or the bottom of notebooks of a given class.
 *
 *      We replace the base WPObject implementation without
 *      calling the parent if the "Icon" page replacement
 *      is enabled. The reason for this is that some of the
 *      WPS network classes return SETTINGS_PAGE_REMOVED
 *      for the "General" ("Icon") page, and we _still_
 *      want the Icon page then, just without the icon and
 *      title fields.
 *
 *@@added V0.9.19 (2002-06-15) [umoeller]
 *@@changed V1.0.2 (2003-10-14) [pr]: WPNetgroup had no icon page, fixed @@fixes 514
 */

SOM_Scope BOOL  SOMLINK xo_wpAddSettingsPages(XFldObject *somSelf,
                                              HWND hwndNotebook)
{
    BOOL brc = FALSE;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpAddSettingsPages");

    /* return (XFldObject_parent_WPObject_wpAddSettingsPages(somSelf,
                                                          hwndNotebook)); */

    TRY_LOUD(excpt1)
    {
        // add the "Window" page
        if (_wpAddObjectWindowPage(somSelf, hwndNotebook))
        {
            ULONG ul;
            // add the "Icon" page 2 on top if we have animation icons
            if (_wpclsQueryMaxAnimationIcons(_somGetClass(somSelf)))
                    // V0.9.20 (2002-07-25) [umoeller]: we can use the method now
                _wpAddObjectGeneralPage2(somSelf, hwndNotebook);

            // add the "Icon" page on top
            ul = _wpAddObjectGeneralPage(somSelf, hwndNotebook);

            // now, if some wannabe funny WPS class (WPSharedDir)
            // is trying to get rid of the icon page, ignore that:
            // V0.9.19 (2002-06-15) [umoeller]
            if (    (    (ul == SETTINGS_PAGE_REMOVED)
                      || (ul == TRUE)
                        // WPSharedDir returns TRUE, violating the specs...
                    )
#ifndef __ALWAYSREPLACEICONPAGE__
                 && (cmnQuerySetting(sfReplaceIconPage))
#endif
                 // but do this trick only for certain classes, we
                 // better make sure we don't mess with user classes
                 && (    (ctsIsSharedDir(somSelf))
                      || (ctsIsServer(somSelf))         // added V0.9.20 (2002-07-31) [umoeller]
                      || (ctsIsNetgrp(somSelf))         // added V1.0.2 (2003-10-14) [pr]: @@fixes 514
                    )
               )
            {
                _PmpfF(("[%s]{%s} got SETTINGS_PAGE_REMOVED",
                        _wpQueryTitle(somSelf),
                        _somGetClassName(somSelf)));
                ul = _xwpAddReplacementIconPage(somSelf,
                                                hwndNotebook,
                                                // mini icon page
                                                SP_OBJECT_ICONPAGE1_X,
                                                ID_XSH_OBJICONPAGE1_X);
            }

            brc = !!ul;
        }
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    return brc;
}

/*
 *@@ wpAddObjectGeneralPage:
 *      this WPObject instance method adds the "Icon"
 *      page to an object's settings notebook.
 *
 *      Starting with V0.9.16, we completely replace
 *      the "Icon" page to add support for object
 *      hotkeys and object details. The "Object"
 *      page is gone.
 *
 *      Note that some WPS classes such as WPSharedDir
 *      override this method to return SETTINGS_PAGE_REMOVED.
 *      We handle this reprehensible attitude in
 *      XFldObject::wpAddSettingsPages.
 *
 *@@added V0.9.1 (2000-02-17) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xo_wpAddObjectGeneralPage(XFldObject *somSelf,
                                                   HWND hwndNotebook)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpAddObjectGeneralPage");


#ifndef __ALWAYSREPLACEICONPAGE__
    if (    (cmnQuerySetting(sfReplaceIconPage))
            // check if this is a folder;
            // if so, XFolder will insert the page
            // because otherwise this would be between
            // the two "Icon" pages...
         // && (!_somIsA(somSelf, _WPFolder))
                // removed V0.9.16 (2001-10-15) [umoeller]
        )
#endif
    {
        return _xwpAddReplacementIconPage(somSelf,
                                          hwndNotebook,
                                          SP_OBJECT_ICONPAGE1,
                                          ID_XSH_OBJICONPAGE1);
    }

#ifndef __ALWAYSREPLACEICONPAGE__
    return XFldObject_parent_WPObject_wpAddObjectGeneralPage(somSelf,
                                                             hwndNotebook);
#endif
}

/*
 *@@ wpSetTitle:
 *      this WPObject instance method sets a new title
 *      for the object. This gets called during object
 *      instantiation and later if the object title is
 *      changed, e.g. from the settings notebook or
 *      via direct editing in a folder container.
 *
 *      Since this stupid method also resorts the folder
 *      after title changes, I have rewritten this.
 *
 *      From my testing, this is the ONLY place in the
 *      WPS which actually allocates memory for the
 *      title string and stores that in MINIRECORDCORE.pszIcon.
 *      So we can safely override this and allocate the
 *      string memory for our own heap (as long as we free
 *      the memory properly in wpUnInitData).
 *
 *@@added V0.9.16 (2002-01-04) [umoeller]
 *@@changed V0.9.20 (2002-07-25) [umoeller]: extended mutex protection
 */

SOM_Scope BOOL  SOMLINK xo_wpSetTitle(XFldObject *somSelf,
                                      PSZ pszNewTitle)
{
    BOOL    brc = TRUE,
            fLocked = FALSE;

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSetTitle");

    // return (XFldObject_parent_WPObject_wpSetTitle(somSelf, pszNewTitle));

    TRY_LOUD(excpt1)
    {
        if (!pszNewTitle)
            brc = FALSE;
        else
        {
            PMINIRECORDCORE pRecord = _wpQueryCoreRecord(somSelf);
            ULONG       ulNewTitleLen = strlen(pszNewTitle);
            PSZ         pszNewTitleCopy;
            ULONG       ulError;

            // use the WPS heap in order not to clutter up
            // our own heap with all the string data
            if (!(pszNewTitleCopy = _wpAllocMem(somSelf,
                                                ulNewTitleLen + 1,
                                                &ulError)))
                brc = FALSE;
            else
            {
                ULONG           ulStyle = _wpQueryStyle(somSelf);
                BOOL            fIsInitialized = (0 != (_flObject & OBJFL_INITIALIZED)),
                                fRefresh = FALSE;

                // LOCK the object if it is already initialized... no need
                // to do so if this gets called in the process of setting
                // up the object though

                // (moved the lock up here V0.9.20 (2002-07-25) [umoeller])

                if (fIsInitialized)
                    if (!(fLocked = !_wpRequestObjectMutexSem(somSelf, 5000)))
                    {
                        cmnLog(__FILE__, __LINE__, __FUNCTION__,
                               "Couldn't get object mutex sem in five seconds. Aborting object rename.");
                        brc = FALSE;
                    }

                if (    (!fIsInitialized)
                     || (fLocked)
                   )
                {
                    PSZ p;

                    memcpy(pszNewTitleCopy, pszNewTitle, ulNewTitleLen + 1);

                    // replace all '^' with '\n'
                    p = pszNewTitleCopy;
                    while (p = strchr(p, '^'))
                        *p = '\n';

                    if (strhcmp(pRecord->pszIcon, pszNewTitleCopy))
                    {
                        // new title is different:
                        if (    (ulStyle & OBJSTYLE_TEMPLATE)
                             && (fIsInitialized)
                           )
                        {
                            // renaming a template: template entries in
                            // OS2.INI are based on names, so unset
                            // the template flag; this will nuke the
                            // template entry from OS2.INI, we'll add
                            // a new one below
                            _wpModifyStyle(somSelf,
                                           OBJSTYLE_TEMPLATE,
                                           0);
                        }

                        // now go set the new string which we allocated above
                        if (pRecord->pszIcon)
                            _wpFreeMem(somSelf, pRecord->pszIcon);
                        pRecord->pszIcon = pszNewTitleCopy;     // can be NULL V0.9.19 (2002-04-25) [umoeller]

                        // alright, don't free this
                        pszNewTitleCopy = NULL;

                        // this was missing V0.9.17 (2002-02-05) [umoeller]
                        // abstracts don't save themselves otherwise
                        if (    (fIsInitialized)
                             && (_flObject & OBJFL_WPABSTRACT)
                           )
                            _wpSaveDeferred(somSelf);

                        // now go refresh all the views if the object
                        // is already initialized;
                        // no need to do otherwise because then we can neither
                        // have shadows pointing to it nor can it be
                        // inserted into a container yet

                        // note: fLocked is TRUE only if the object _was_ intialized,
                        // see above, so we refresh below only if the title actually
                        // changed on an awake object
                        fRefresh = fLocked;

                        if (    (ulStyle & OBJSTYLE_TEMPLATE)
                             && (fIsInitialized)
                           )
                        {
                            // re-enter the template entry we killed above
                            _wpModifyStyle(somSelf,
                                           OBJSTYLE_TEMPLATE,
                                           OBJSTYLE_TEMPLATE);
                        }
                    }
                }

                if (pszNewTitleCopy)
                    // title hasn't changed, or something else went wrong:
                    _wpFreeMem(somSelf, pszNewTitleCopy);

                if (fRefresh)
                    // using new method now V1.0.1 (2003-01-25) [umoeller]
                    _xwpForceRepaint(somSelf,
                                     REFRESH_RECORDREPAINT
                                     | REFRESH_RECORDTEXT
                                     | REFRESH_SHADOWS
                                     | REFRESH_WINDOWS);

                    // @@todo resort the folder

            } // end else if (!(pszNewTitleCopy = _wpAllocMem(somSelf,
        } // end if (!pszNewTitle)
    }
    CATCH(excpt1)
    {
        brc = FALSE;
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return brc;
}

/*
 *@@ wpConfirmObjectTitle:
 *      this instance method is called by the WPS during file
 *      operations (copy, move, rename etc.) for every single
 *      object that is being processed. This method must verify
 *      that the operation is valid WRT name clashes and display
 *      a confirmation dialog in case it is not.
 *
 *      Apparently, this method is not overridden by subclasses,
 *      not even WPFileSystem -- except WPShadow, which forwards
 *      this method call to the linked object.
 *
 *      XWorkplace implements its own "Object exists" dialog here,
 *      if the Global Settings allow it.
 *
 *      Like most interesting methods in the WPS, this thing is
 *      barely documented, so this is what I found out.
 *
 *      Parameters:
 *
 *      -- *somSelf      in: the object being worked on
 *
 *      -- *Folder       in: the folder being worked on
 *
 *      -- **ppDuplicate in: somSelf (warning, this is not correct
 *                              in the WPS docs);
 *                       out: if we return NAMECLASH_REPLACE,
 *                            this is set to the object to
 *                            be replaced by the caler
 *
 *      -- pszTitle      in: title of somSelf;
 *                       out: if we return NAMECLASH_RENAME,
 *                            this is set to somSelf's new title
 *                            to be set by the caller
 *
 *      -- cbTitle       in: sizeof(*pszTitle)
 *
 *      -- menuID        in: the user's operation, which is:
 *                              0x0065    create another (WPMENUID_CREATEANOTHER)
 *                              0x006B    move (WPMENUID_MOVE)
 *                              0x006C    copy (WPMENUID_COPY)
 *                              0x006E    rename (no menu ID defined in wpobject.h)
 *                              0x013C    create shadow (WPMENUID_CREATESHADOW)
 *
 *      Returns:
 *      -- NAMECLASH_CANCEL (0):
 *                  abort processing, have the WPS do nothing ("Cancel"
 *                  button pressed in confirmation dialog); this will
 *                  just skip the current file;
 *      -- NAMECLASH_NONE (1):
 *                  continue processing, but perform no further
 *                  renaming etc.; we return this if no duplicate file
 *                  exists;
 *      -- NAMECLASH_RENAME (2):
 *                  have the WPS change the title to pszTitle, which we
 *                  need to set here; note that this does NOT change
 *                  the real name! So we do the renaming ourselves here;
 *      -- NAMECLASH_REPLACE (8):
 *                  have the WPS delete *ppDuplicate, which we need to
 *                  set here if we return this code; we need to find the
 *                  duplicate in this method then.
 *
 *      Note: these constants are only defined in the Warp 4 Toolkit
 *      (some of them in wpsystem.h in Warp 3 Toolkit also). So
 *      #include helpers/undoc.h, which defines these.
 *
 *@@changed V0.9.0 [umoeller]: fixed "create another" bug
 */

SOM_Scope ULONG  SOMLINK xo_wpConfirmObjectTitle(XFldObject *somSelf,
                                                 WPFolder* Folder,
                                                 WPObject** ppDuplicate,
                                                 PSZ pszTitle,
                                                 ULONG cbTitle,
                                                 ULONG menuID)
{
    ULONG ulrc = NAMECLASH_NONE;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpConfirmObjectTitle");

    #ifdef __DEBUG__
    {
        CHAR szFolder2[CCHMAXPATH];
        PMPF_TITLECLASH(("Entering wpConfirmObjectTitle"));
        PMPF_TITLECLASH(("  somSelf == 0x%lX", somSelf));
        PMPF_TITLECLASH(("    pszTitle: %s", pszTitle));
        _wpQueryFilename(_wpQueryFolder(somSelf), szFolder2, TRUE);
        PMPF_TITLECLASH(("    in folder: %s", szFolder2));
        PMPF_TITLECLASH(("  menuID: 0x%lX", menuID));
    }
    #endif

    // first of all, check whether the confirmation
    // dialogs have been replaced in the global settings;
#ifndef __ALWAYSREPLACEFILEEXISTS__
    if (cmnQuerySetting(sfReplaceFileExists))
    {
#endif
        // yes: use our replacement (fileops.c)
        ulrc = fopsConfirmObjectTitle(somSelf,
                                      Folder,
                                      ppDuplicate,
                                      pszTitle,
                                      cbTitle,
                                      menuID);
#ifndef __ALWAYSREPLACEFILEEXISTS__
    }
    else
    {
        // global settings do not allow dialog
        // replacement: call default
        PMPF_TITLECLASH(("  calling original"));
        ulrc = XFldObject_parent_WPObject_wpConfirmObjectTitle(somSelf,
                                                               Folder,
                                                               ppDuplicate,
                                                               pszTitle,
                                                               cbTitle,
                                                               menuID);
    }
#endif

    #ifdef __DEBUG__
    {
        CHAR szFolder2[CCHMAXPATH];
        PMPF_TITLECLASH(("  Return value: %d", ulrc));
        PMPF_TITLECLASH(("  New somSelf == 0x%lX", somSelf));
        PMPF_TITLECLASH(("    New pszTitle: %s", pszTitle));
        _wpQueryFilename(_wpQueryFolder(somSelf), szFolder2, TRUE);
        PMPF_TITLECLASH(("    in folder: %s", szFolder2));

        if (ppDuplicate)
        {
            PMPF_TITLECLASH(("  ppDuplicate neu != NULL"));
            if (*ppDuplicate)
            {
                PMPF_TITLECLASH(("    *ppDuplicate neu == 0x%lX", *ppDuplicate));
                PMPF_TITLECLASH(("      Title neu: %s", _wpQueryTitle(*ppDuplicate) ));
                _wpQueryFilename(_wpQueryFolder(*ppDuplicate), szFolder2, TRUE);
                PMPF_TITLECLASH(("      in folder: %s", szFolder2));
            }
        }
        PMPF_TITLECLASH(("Done."));
    }
    #endif

    return ulrc;
}

/* ******************************************************************
 *
 *   here come the XFldObject class methods
 *
 ********************************************************************/

/*
 * @@ xwpclsRemoveObjectHotkey:
 *      this removes the object hotkey for the
 *      given object. This extra function is
 *      necessary because if an object handle
 *      for an object with a hotkey gets lost
 *      (e.g. because the object was deleted),
 *      there's no way to invoke
 *      XFldObject::xwpSetObjectHotkey on it.
 *
 *@@added V0.9.0 (99-11-12) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xoM_xwpclsRemoveObjectHotkey(M_XFldObject *somSelf,
                                                     HOBJECT hobj)
{
    /* M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf); */
    M_XFldObjectMethodDebug("M_XFldObject","xoM_xwpclsRemoveObjectHotkey");

    return objRemoveObjectHotkey(hobj);
}

/*
 *@@ xwpclsCreateList:
 *      this new class method creates a new OBJECTLIST.
 *
 *      An OBJECTLIST is basically a linked list with additional
 *      features to facilitate object handling. These features
 *      are:
 *
 *      --  Automatic locking with an internal mutex.
 *
 *      --  Automatic reference counting. If you add an object
 *          to the list which goes dormant for some reason,
 *          it is automatically removed from the list without
 *          you having to take care of that.
 *          This is done in XFldObject::wpUnInitData.
 *
 *      --  Automatic optional loading and saving of the object
 *          list from OS2.INI.
 *
 *      Use XFldObject::xwpAppendToList, XFldObject::xwpIsOnList,
 *      and XFldObject::xwpRemoveFromList to work on the list.
 *
 *      If pcszIniApp and pcszIniKey are specified, the operates
 *      in "INI" mode; the objects are loaded from the given
 *      OS2.INI location, and every change to the list is
 *      automatically stored in the given key.
 *
 *      Since the list is stored as an array of object handles in
 *      OS2.INI, this then allocates one for every object on the list.
 *
 *      The key strings must be static; this function does not make
 *      copies of them.
 *
 *      Use _somFree to free the list again.
 *
 *      Returns NULL if the list could not be created.
 *
 *@@added V1.0.1 (2002-12-08) [umoeller]
 */

SOM_Scope XWPObjList*  SOMLINK xoM_xwpclsCreateList(M_XFldObject *somSelf,
                                                    PSZ pcszIniApp,
                                                    PSZ pcszIniKey)
{
    XWPObjList* pList;
    BOOL        fLocked = FALSE;

    /* M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf); */
    M_XFldObjectMethodDebug("M_XFldObject","xoM_xwpclsCreateList");

    if (pList = _somNew(_XWPObjList))
    {
        _SetKeys(pList,
                 pcszIniApp,
                 pcszIniKey);
    }

    return pList;
}

/*
 *@@ wpclsInitData:
 *      this M_WPObject class method gets called when a class
 *      is loaded by the WPS (probably from within a
 *      somFindClass call) and allows the class to initialize
 *      itself.
 *
 *      We override this for XFldObject to initialize
 *      XWorkplace altogether.
 *
 *      This is probably the first WPS method called on the
 *      system (for M_WPObject, that is), so we use this
 *      to set up some stuff, most notably, start the
 *      additional XWorkplace threads by calling
 *      initMain, which handles the details.
 *
 *      The beautiful thing is that at this point we appear
 *      to own the computer all alone. It seems that no
 *      additional WPS threads are running _yet_ (or if they
 *      are, they don't care), so we can halt the system,
 *      display graphics, overwrite the master boot record,
 *      whatever. (JUST KIDDING.)
 *
 *@@changed V0.9.0 [umoeller]: added class object to KERNELGLOBALS
 */

SOM_Scope void  SOMLINK xoM_wpclsInitData(M_XFldObject *somSelf)
{
    BOOL    fWPSStartup = TRUE;

    // M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf);
    // M_XFldObjectMethodDebug("M_XFldObject","xoM_wpclsInitData");
    PMPF_SOMMETHODS((__FUNCTION__ " for class %s",
                     _somGetName(somSelf) ));

    M_XFldObject_parent_M_WPObject_wpclsInitData(somSelf);

    // since this code is reached for every single WPS class
    // that gets initialized, we need to check if we're being
    // called for the first time
    if (!G_fXWorkplaceInitialized)
    {
        G_fXWorkplaceInitialized = TRUE;

        fWPSStartup = initMain();

        // initialize the XWPObjList class explicitly
        // V1.0.1 (2002-12-11) [umoeller]
        XWPObjListNewClass(XWPObjList_MajorVersion, XWPObjList_MinorVersion);

        krnClassInitialized(G_pcszXFldObject);
    }

    initLog("Class %s initialized", _somGetName(somSelf));

#ifndef __NOBOOTUPSTATUS__
    if (fWPSStartup)
    {
        // even if not first invocation (i.e. some class other
        // than WPObject gets initialized): notify Speedy thread
        // of class initialization
        if (cmnQuerySetting(sfShowBootupStatus))
            xthrPostBushMsg(QM_BOOTUPSTATUS,
                            (MPARAM)somSelf,       // class object
                            MPNULL);
    }
#endif
}

/*
 *@@ wpclsQueryObject:
 *
 *@@added V0.9.19 (2002-06-12) [umoeller]
 */

SOM_Scope WPObject*  SOMLINK xoM_wpclsQueryObject(M_XFldObject *somSelf,
                                                  HOBJECT hObject)
{
    /* M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf); */
    M_XFldObjectMethodDebug("M_XFldObject","xoM_wpclsQueryObject");

    // _PmpfF(("HOBJECT 0x%lX", hObject));

    return M_XFldObject_parent_M_WPObject_wpclsQueryObject(somSelf,
                                                           hObject);
}

/*
 *@@ wpclsInsertMultipleObjects:
 *
 *@@added V1.0.1 (2003-01-25) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xoM_wpclsInsertMultipleObjects(M_XFldObject *somSelf,
                                                       HWND hwndCnr,
                                                       PPOINTL pptlIcon,
                                                       PVOID* pObjectArray,
                                                       PVOID pRecordParent,
                                                       ULONG NumRecords)
{
    HWND        hwndOwner;
    WPObject    **papObjects;

    /* M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf); */
    M_XFldObjectMethodDebug("M_XFldObject","xoM_wpclsInsertMultipleObjects");

#ifndef __NOTURBOFOLDERS__
    // some sanity checks first
    if (    (!G_fInsertRecordCrashed)
         && (cmnQuerySetting(sfTurboFolders))
         && (hwndCnr)
         && (hwndOwner = WinQueryWindow(hwndCnr, QW_OWNER))
            // now, it is _this_ method that subclasses the
            // container owner to give any container all
            // the WPS features...
         && (ContainerSubclassHack(hwndOwner, somSelf))
         && (NumRecords)
         && (papObjects = (WPObject**)pObjectArray)
       )
    {
        BOOL        brc = TRUE;

        TRY_LOUD(excpt1)
        {
            // now run through all objects in the array;
            // we do this in _reverse_ order so we can
            // set up the linked list of records for
            // the CM_INSERTRECORD message

            PMINIRECORDCORE
                        pmrcFirst = NULL,
                        pmrcPrev = NULL;

            // start with last
            ULONG       ul = NumRecords - 1;

            while (brc)
            {
                WPObject *pobjThis = papObjects[ul];
                XFldObjectData *somThis = XFldObjectGetData(pobjThis);
                PMINIRECORDCORE pmrcThis = (((PIBMOBJECTDATA)_pvWPObjectData)->pmrc);

                // the WPS allocates a special ULONG before the
                // actual record, which is zero until the object
                // has been inserted at least once
                PULONG      pulEverInserted = (((PULONG)pmrcThis) - 1);
                PUSEITEM    pui;

                // allocate USEITEM with RECORDITEM and
                // set up the flRecordAttr fields
                if (pui = _xwpPrepareInsertRecord(pobjThis,
                                                  hwndCnr,
                                                  pmrcThis,
                                                  (((PIBMOBJECTDATA)_pvWPObjectData)->flStyle),
                                                  !(*pulEverInserted)))
                {
                    if (!_wpAddToObjUseList(pobjThis, pui))
                    {
                        ShlFreeMem(pui);
                        brc = FALSE;
                        break;
                    }
                }
                else
                {
                    brc = FALSE;
                    break;
                }

                // link these together; on the first loop
                // (for the last record), pmrcPrev is NULL,
                // otherwise it points to the record from
                // the previous loop, which is the _next_
                // in the record linklist
                pmrcThis->preccNextRecord = pmrcPrev;

                // first record reached?
                if (!ul)
                {
                    if (pptlIcon)
                    {
                        pmrcThis->ptlIcon.x = pptlIcon->x;
                        pmrcThis->ptlIcon.y = pptlIcon->y;
                    }

                    // remember this for CM_INSERTRECORD later
                    pmrcFirst = pmrcThis;
                    break;
                }

                pmrcPrev = pmrcThis;
                --ul;
            }

            if (brc && pmrcFirst)
            {
                RECORDINSERT ri2;

                static const RECORDINSERT s_riTemplate =
                    {
                        sizeof(RECORDINSERT),       // cb
                        (PRECORDCORE)CMA_END,       // pRecordOrder
                        NULL,                       // record parent
                        TRUE,                       // fInvalidateRecord
                        CMA_TOP,                    // zOrder
                        0                           // cRecordsInsert
                    };

                memcpy(&ri2, &s_riTemplate, sizeof(RECORDINSERT));
                ri2.pRecordParent = (PRECORDCORE)pRecordParent;
                ri2.cRecordsInsert = NumRecords;

                WinSendMsg(hwndCnr,
                           CM_INSERTRECORD,
                           (MPARAM)pmrcFirst,
                           (MPARAM)&ri2);
            }
        }
        CATCH(excpt1)
        {
            brc = FALSE;
            G_fInsertRecordCrashed = TRUE;      // don't _ever_ call this again!
        } END_CATCH();

        return brc;
    }
#endif

    return M_XFldObject_parent_M_WPObject_wpclsInsertMultipleObjects(somSelf,
                                                                     hwndCnr,
                                                                     pptlIcon,
                                                                     pObjectArray,
                                                                     pRecordParent,
                                                                     NumRecords);
}

/*
 *@@ wpclsQuerySettingsPageSize:
 *      this M_WPObject class method should return the
 *      size of the largest settings page in dialog
 *      units; if a settings notebook is initially
 *      opened, i.e. no window pos has been stored
 *      yet, the WPS will use this size, to avoid
 *      truncated settings pages.
 *
 *      Since the "Object" page is pretty large,
 *      we return this.
 *
 *@@added V0.9.2 (2000-03-08) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xoM_wpclsQuerySettingsPageSize(M_XFldObject *somSelf,
                                                       PSIZEL pSizl)
{
    /* M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf); */
    M_XFldObjectMethodDebug("M_XFldObject","xoM_wpclsQuerySettingsPageSize");

    /* return M_XFldObject_parent_M_WPObject_wpclsQuerySettingsPageSize(somSelf,
                                                                        pSizl); */
    pSizl->cx = 275;       // size of "Object" page
    pSizl->cy = 150;       // size of "Object" page

    return TRUE;
}

/*
 *@@ wpclsSetIconData:
 *      this M_WPObject class method sets the icon information
 *      for the given class object.
 *
 *      Doesn't make much sense? Welcome to the club. See
 *      icons.c for an introduction to the WPS icon mess.
 *
 *      What I found out is that when the WPS calls
 *      M_WPObject::wpclsQueryIcon to get the default icon of
 *      an object's class (because an object is not using a
 *      custom icon), the icon only gets loaded once. On the
 *      first call _per class_, the WPS then calls
 *      wpclsQueryIconData (which is the method to be overridden
 *      to change the class default icon) and then this method,
 *      wpclsSetIconData, to have the class icon set. For that,
 *      wpclsSetIconData calls wpclsSetIcon to set the _class_
 *      icon handle, which is then returned in subsequent calls
 *      to wpclsQueryIcon.
 *
 *      The problem with this method (wpclsSetIconData) is
 *      that it doesn't support the ICON_FILE format in
 *      ICONINFO. So in order to support ICON_FILE, which
 *      would be handy with the way cmnGetStandardIcon
 *      works, we have to override this method too.
 *
 *@@added V0.9.16 (2002-01-13) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xoM_wpclsSetIconData(M_XFldObject *somSelf,
                                                PICONINFO pIconInfo)
{
    BOOL brc = FALSE;
    BOOL fCallDefault = TRUE;

    /* M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf); */
    M_XFldObjectMethodDebug("M_XFldObject","xoM_wpclsSetIconData");

#ifndef __NOICONREPLACEMENTS__
    // if icon replacements are enabled, we support ICON_FILE
    // too; for all other cases, call the parent method
    if (    (pIconInfo)
         && (pIconInfo->fFormat == ICON_FILE)
         && (cmnQuerySetting(sfIconReplacements))
       )
    {
        HPOINTER hptr;

        fCallDefault = FALSE;

        // _Pmpf(("        pIconInfo->pszFileName %s", pIconInfo->pszFileName));

        if (!icoLoadICOFile(pIconInfo->pszFileName,
                            &hptr,
                            NULL,
                            NULL))
        {
            brc = _wpclsSetIcon(somSelf, hptr);
        }
    }
#endif

    if (fCallDefault)
        brc = M_XFldObject_parent_M_WPObject_wpclsSetIconData(somSelf,
                                                              pIconInfo);

    return brc;
}


