
/*
 *@@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 XFldObject xo_
 *@@somclass M_XFldObject xoM_
 */

/*
 *      Copyright (C) 1997-2002 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_WINPOINTERS
#define INCL_WINMENUS
#define INCL_WINDIALOGS
#define INCL_WINSTDCNR
#define INCL_WINSTDBOOK
#define INCL_WINPROGRAMLIST
#define INCL_WINSWITCHLIST
#include <os2.h>

// C library headers
#include <stdio.h>
#include <setjmp.h>             // needed for except.h
#include <assert.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\except.h"             // exception handling
#include "helpers\linklist.h"           // linked list 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>

/* ******************************************************************
 *
 *   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;

// object flags mutex V1.0.0 (2002-08-31) [umoeller]
static HMTX         G_hmtxObjFlags = 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;

/* ******************************************************************
 *
 *   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);
}

/* ******************************************************************
 *
 *   Flags mutex
 *
 ********************************************************************/

/*
 *@@ LockObjFlags:
 *
 *@@added V0.9.20 (2002-07-25) [umoeller]
 */

STATIC BOOL LockObjFlags(VOID)
{
    if (G_hmtxObjFlags)
        return !DosRequestMutexSem(G_hmtxObjFlags, SEM_INDEFINITE_WAIT);

    // first call:
    return !DosCreateMutexSem(NULL,
                              &G_hmtxObjFlags,
                              0,
                              TRUE);
}

/*
 *@@ UnlockObjFlags:
 *
 *@@added V0.9.20 (2002-07-25) [umoeller]
 */

STATIC VOID UnlockObjFlags(VOID)
{
    DosReleaseMutexSem(G_hmtxObjFlags);
}

/* ******************************************************************
 *
 *   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 = LockObjFlags())
        {
            ulrc = _flObject;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        UnlockObjFlags();

    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 = LockObjFlags())
        {
            _flObject     = (
                                // copy all unaffected
                                (_flObject & ~flFlags)
                                // OR with masked new ones
                              | (flFlags & flMask)
                            );
            brc = TRUE;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        UnlockObjFlags();

    return brc;
}

/*
 *@@ xwpAddWidgetNotify:
 *      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 = LockObjFlags())
        {
            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)
        UnlockObjFlags();

    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 = LockObjFlags())
        {
            XFldObjectData *somThis = XFldObjectGetData(somSelf);
            if (_pvllWidgetNotifies)
            {
                lstRemoveItem((PLINKLIST)_pvllWidgetNotifies,
                              (PVOID)hwnd);
            }

            brc = TRUE;
        }
    }
    CATCH(excpt1) {} END_CATCH();

    if (fFlagsLocked)
        UnlockObjFlags();

    return brc;
}

/*
 *@@ xwpQueryObjectHotkey:
 *      this returns the global object hotkey which has been defined
 *      to be monitored for in the XWorkplace hook.
 *
 *      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 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 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
 */

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))
        {
            // 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 needs to be called using SOM name-lookup
 *      resolution to support overriding it in subclasses
 *      of XFldObject (WPObject), which xwpQuerySetup does.
 *
 *      In other words:
 *
 *      --  _call_ xwpQuerySetup for getting a setup string.
 *
 *      --  _override_ xwpQuerySetup2 for adding setup-string support
 *          to your class.
 *
 *      Guidelines:
 *
 *      1.  You cannot simply use _parent_xwpQuerySetup2
 *          to call the parent method, because there's no C binding
 *          for this. The SOM header files do not know that WPObject
 *          has been replaced with XFldObject and therefore have no
 *          idea that XFldObject is actually a parent class of all
 *          other WPS classes. You must manually resolve the SOM
 *          method pointer for the parent class of your class;
 *          wpshParentQuerySetup2 has been provided to make this
 *          easier. See the code sample below.
 *
 *      2.  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 XFldObject method.
 *
 *      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
 *          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 (wpshParentQuerySetup2(somSelf,
 +                                        _somGetParent(_XWPMyClass),
 +                                        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 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:
 *
 *      --  hab is the anchor block of WPS thread 1.
 *
 *      --  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.
 *
 *      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]
 */

SOM_Scope HWND  SOMLINK xo_xwpHotkeyOrBorderAction(XFldObject *somSelf,
                                                   ULONG hab,
                                                   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 (?!?)
                         OPEN_DEFAULT,
                         0);           // "optional parameter" (?!?)

    PMPF_KEYS(("opened hwnd 0x%lX", hwnd));

    if (hwnd)
    {
        if (WinIsWindow(hab,
                        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;
}

/*
 *@@ 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
    _flObject = 0;
    ctsSetClassFlags(somSelf, &_flObject);

    _pvTrashData = NULL;            // V0.9.20 (2002-07-25) [umoeller]

    _pWszOriginalObjectID = NULL;

    _pvllWidgetNotifies = 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.
 *
 *      We support the WRITEREXXSETUP string here.
 *
 *@@added V0.9.9 (2001-04-06) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xo_wpSetup(XFldObject *somSelf, PSZ pszSetupString)
{
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpSetup");

    if (XFldObject_parent_WPObject_wpSetup(somSelf, pszSetupString))
        return objSetup(somSelf,
                        pszSetupString);

    return FALSE;
}

/*
 *@@ 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
 */

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]
    {
        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 or being deleted. All allocated resources
 *      should be freed here.
 *      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;
    PMINIRECORDCORE pmrc;
    PTRASHDATA  p;
    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpUnInitData");

    TRY_LOUD(excpt1)
    {
        // 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;
        }

        // 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();
            }

#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

            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 (fAwakeSem)
        UnlockAwakeObjectsList();

    if (fIconSem)
        icomUnlockIconShares();

    // 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 = LockObjFlags())
           )
        {
            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;
            }

            UnlockObjFlags();       // 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)
        UnlockObjFlags();       // 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]
 */

SOM_Scope BOOL  SOMLINK xo_wpAddToObjUseList(XFldObject *somSelf,
                                             PUSEITEM pUseItem)
{
    BOOL    fLocked = FALSE,
            brc = FALSE;
#ifndef __NOTURBOFOLDERS__

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpAddToObjUseList");

    TRY_LOUD(excpt1)
    {
        PIBMOBJECTDATA pod;

        if (    (!(_flObject & OBJFL_WPDATAFILE))
             || (pUseItem->type != USAGE_OPENVIEW)
             || (cmnQuerySetting(sfDatafileOBJHANDLE))
             || (!(pod = (PIBMOBJECTDATA)_pvWPObjectData))
           )
#endif
            brc = XFldObject_parent_WPObject_wpAddToObjUseList(somSelf,
                                                               pUseItem);
#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->pUseItemFirst))
                {
                    PMPF_ASSOCS(("    adding hwnd %lX as first ui",
                           ((PVIEWITEM)(pUseItem + 1))->handle
                         ));

                    pod->pUseItemFirst = 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

    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]
 */

SOM_Scope BOOL  SOMLINK xo_wpDeleteFromObjUseList(XFldObject *somSelf,
                                                  PUSEITEM pUseItem)
{
    BOOL    fLocked = FALSE,
            brc = FALSE;
#ifndef __NOTURBOFOLDERS__

    XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpDeleteFromObjUseList");

    TRY_LOUD(excpt1)
    {
        PIBMOBJECTDATA pod;

        if (    (cmnQuerySetting(sfDatafileOBJHANDLE))
             || (!(_flObject & OBJFL_WPDATAFILE))
             || (pUseItem->type != USAGE_OPENVIEW)
             || (!(pod = (PIBMOBJECTDATA)_pvWPObjectData))
           )
#endif
            brc = XFldObject_parent_WPObject_wpDeleteFromObjUseList(somSelf,
                                                                    pUseItem);
#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->pUseItemFirst,
                            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->pUseItemFirst = 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->pUseItemFirst;
                    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

    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;
}

/*
 *@@ 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.
 *
 *@@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)
{
    BOOL    brc;
    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpFilterMenu");

    if (brc = XFldObject_parent_WPObject_wpFilterMenu(somSelf,
                                                      pFlags,
                                                      hwndCnr,
                                                      fMultiSelect,
                                                      ulMenuType,
                                                      ulView,
                                                      ulReserved))
    {
        // 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 brc;
}

/*
 *@@ 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,
                    mnuQueryViewName(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
 */

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)
    {

        // re-enabled WPMENUID_DELETE catch here
        // V1.0.0 (2002-09-12) [umoeller]

        // 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
        case WPMENUID_DELETE:
        {
            if (
#ifndef __ALWAYSTRASHANDTRUEDELETE__
                    (cmnQuerySetting(sfReplaceDelete))
                 &&
#endif
                    (!ctsIsTransient(somSelf))
                            // V0.9.9 (2001-03-10) [pr]: fix print object delete
               )
            {
                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;
}

/*
 *@@ wpViewObject:
 *      overridden for debugging.
 *
 *@@added V0.9.20 (2002-08-04) [umoeller]
 */

SOM_Scope HWND  SOMLINK xo_wpViewObject(XFldObject *somSelf,
                                        HWND hwndCnr, ULONG ulView,
                                        ULONG param)
{
    HWND hwnd;

    // XFldObjectData *somThis = XFldObjectGetData(somSelf);
    XFldObjectMethodDebug("XFldObject","xo_wpViewObject");

    PMPF_ASSOCS(("[%s] entering", _wpQueryTitle(somSelf)));

    hwnd = XFldObject_parent_WPObject_wpViewObject(somSelf,
                                                   hwndCnr,
                                                   ulView,
                                                   param);

    PMPF_ASSOCS(("[%s] returning hwnd 0x%lX", _wpQueryTitle(somSelf), hwnd));

    return hwnd;
}

/*
 *@@ 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.
 *
 *      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));

                // 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
                        if (fLocked)
                            objRefreshUseItems(somSelf,
                                               pRecord->pszIcon,
                                               NULLHANDLE);     // no new icon V0.9.20 (2002-07-31) [umoeller]

                        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);

            } // 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);
}

/*
 *@@ wpclsInitData:
 *      this 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    fOpenFoldersFound = FALSE;

    // M_XFldObjectData *somThis = M_XFldObjectGetData(somSelf);
    // M_XFldObjectMethodDebug("M_XFldObject","xoM_wpclsInitData");
    #ifdef DEBUG_SOMMETHODS
        _Pmpf((__FUNCTION__ " for class %s",
                    _somGetName(somSelf) ));
    #endif

    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)
    {
        HENUM   henum;
        HWND    hwndThis;

        G_fXWorkplaceInitialized = TRUE;

        // check if we have any open folder windows;
        // if so, we're not really in the process of starting
        // up. This check is necessary because this class
        // method also gets called when the classes are installed
        // by WinRegisterObjectClass, unfortunately, and we don't
        // want to start threads etc. then.
        henum = WinBeginEnumWindows(HWND_DESKTOP);
        while (     (!fOpenFoldersFound)
                 && (hwndThis = WinGetNextWindow(henum))
              )
        {
            CHAR    szClass[200];
            if (WinQueryClassName(hwndThis, sizeof(szClass), szClass))
                if (!strcmp(szClass, "wpFolder window"))
                    // folder window:
                    fOpenFoldersFound = TRUE;
        }
        WinEndEnumWindows(henum);

        if (!fOpenFoldersFound)
        {
            #ifdef DEBUG_SOMMETHODS
            _PmpfF(("initializing class %s", _somGetName(somSelf)));
            #endif

            // only if no open folders are found:
            // initialize the kernel (kernel.c)
            initMain();
        }

        krnClassInitialized(G_pcszXFldObject);
    }

#ifndef __NOBOOTUPSTATUS__
    if (!fOpenFoldersFound)
    {
        // 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);
}

/*
 *@@ wpclsQuerySettingsPageSize:
 *      this 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 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;
}

