
/*
 *@@sourcefile xtrashobj.c:
 *      This file contains SOM code for the following XWorkplace classes:
 *
 *      --  XWPTrashObject: a subclass of WPTransient. One instance
 *             of this class is created by XWPTrashCan for every object
 *             that was placed into the trash can. The trash can folder
 *             always only contains objects of the XWPTrashObject class,
 *             which mirror the objects which have been moved to the
 *             hidden "\trash" directories on each drive. See xtrash.c
 *             for details.
 *
 *             I have chosen to make XWPTrashObject a subclass of
 *             WPTransient in order not to clutter up OS2.INI with
 *             abstract objects which have no data to save anyway.
 *
 *      XWPTrashObject must be installed if XWPTrashCan is installed.
 *
 *      XWPTrashObject instances are created:
 *
 *      1) when the trash can is first populated; XWPTrashCan::wpPopulate
 *         then goes over the "\TRASH" directories on each drive and
 *         creates trash objects according to their contents.
 *
 *      2) if the trash is already populated, whenever more objects are
 *         "deleted" into the trash can.
 *
 *      Each trash object has its "related object", that is, the corresponding
 *      object in the "\TRASH" directories, in its member variables.
 *
 *      In turn, XFldObject has a member which points to the corresponding
 *      trash object if the object has been deleted into one of the "\TRASH"
 *      directories.
 *
 *      Trash objects are destroyed:
 *
 *      1)  when a trash object is restored (moved back out of the trash
 *          can), because then the trash object makes no more sense;
 *
 *      2)  if the related object is destroyed, thru XFldObject::wpUnInitData.
 *          If the user selects "Empty trash can" or "Destroy trash object",
 *          the trash can destroys the related object instead, which in
 *          turn destroys the trash object thru XFldObject::wpUnInitData.
 *
 *@@somclass XWPTrashObject xtro_
 *@@somclass M_XWPTrashObject xtroM_
 */

/*
 *      Copyright (C) 1999-2010 Ulrich M”ller.
 *
 *      This file is part of the XWorkplace source package.
 *      XWorkplace is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XWorkplace main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

/*
 *  This file was generated by the SOM Compiler and Emitter Framework.
 *  Generated using:
 *      SOM Emitter emitctm: 2.41
 */

#ifndef SOM_Module_xtrashobj_Source
#define SOM_Module_xtrashobj_Source
#endif
#define XWPTrashObject_Class_Source
#define M_XWPTrashObject_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_DOSSEMAPHORES
#define INCL_DOSEXCEPTIONS
#define INCL_DOSERRORS
#define INCL_WINPOINTERS
#define INCL_WINMENUS
#include <os2.h>

// C library headers
#include <stdio.h>              // needed for except.h
#include <setjmp.h>             // needed for except.h
#include <assert.h>             // needed for except.h

// generic headers
#include "setup.h"                      // code generation and debugging options

// headers in /helpers
#include "helpers\except.h"             // exception handling
#include "helpers\nls.h"                // National Language Support helpers
#include "helpers\winh.h"               // PM helper routines
#include "helpers\stringh.h"            // string helper routines

// SOM headers which don't crash with prec. header files
#include "xtrash.ih"
#include "xtrashobj.ih"

// XWorkplace implementation headers
#include "dlgids.h"                     // all the IDs that are shared with NLS
#include "shared\common.h"              // the majestic XWorkplace include file
#include "shared\helppanels.h"          // all XWorkplace help panel IDs
#include "shared\kernel.h"              // XWorkplace Kernel
#include "shared\notebook.h"            // generic XWorkplace notebook handling

#include "filesys\trash.h"              // trash can implementation

// other SOM headers
#pragma hdrstop
#include "xfobj.h"

/* ******************************************************************
 *
 *   Object details
 *
 ********************************************************************/

/*
 *@@ XTRO_DETAILS:
 *      extended object details for XWPTrashObject
 *      (used in trash can Details view).
 *
 *      The CLASSFIELDINFO's are initialized in
 *      M_XWPTrashObject::wpclsInitData, passed to the
 *      WPS in M_XWPTrashObject::wpclsQueryDetailsInfo,
 *      and filled for each instance in
 *      XWPTrashObject::wpQueryDetailsData.
 *
 *      Note that the "size" column is a bit tricky.
 *      The details view diplays the pszSize data
 *      as a string (since this can also be "calculating"),
 *      while the "sort by size" criterion is implemented
 *      through the llSize value, which is hidden in
 *      details view.
 *
 *      See XWPTrashObject::wpclsQueryDetailsInfo for details.
 *
 *@@changed V0.9.1 (2000-01-29) [umoeller]: added pszOriginalClass
 *@@changed V0.9.12 (2001-05-18) [umoeller]: added ulSize
 *@@changed V1.0.9 (2010-07-17) [pr]: changed ulSize to llSize for large file support @@fixes 586
 */

typedef struct _XTRO_DETAILS
{
    PSZ     pszDeletedFrom;     // where object was deleted from
    LONGLONG
            llSize;             // ULONG size of related object
                                // (this is for sorting only and not displayed)
    PSZ     pszSize;            // formatted size of related object; this points
                                // to the _szTotalSize instance data
    PSZ     pszOriginalClass;   // class of related object
    CDATE   cdateDeleted;       // deletion date (copied from related object)
    CTIME   ctimeDeleted;       // deletion date (copied from related object)

#ifdef __DEBUG__
    PSZ     pszMapping;         // for debugging, show us the mapping for this object
#endif

} XTRO_DETAILS, *PXTRO_DETAILS;

// extra data fields for XWPTrashObject object details:
#ifdef __DEBUG__
#define XTRO_EXTRAFIELDS    7
#else
#define XTRO_EXTRAFIELDS    6
#endif
        // raised V0.9.12 (2001-05-18) [umoeller]

static CLASSFIELDINFO G_acfiTrashObject[XTRO_EXTRAFIELDS];

/* ******************************************************************
 *
 *   XWPTrashObject instance methods
 *
 ********************************************************************/

/*
 *@@ xwpSetRelatedObject:
 *      this instance method sets the object in the
 *      "\Trash" directories which this trash object
 *      should represent.
 *
 *      This method only gets called once while the
 *      trash object is being initialized (from
 *      XWPTrashObject::wpSetup) and should not be
 *      called manually.
 *
 *      Preconditions: the related object should be
 *      locked.
 *
 *@@changed V0.9.6 (2000-10-25) [umoeller]: fixed icon
 */

SOM_Scope BOOL  SOMLINK xtro_xwpSetRelatedObject(XWPTrashObject *somSelf,
                                                 WPObject* pObject)
{
    BOOL brc = FALSE;
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpSetRelatedObject");

    _pRelatedObject = pObject;

    if (pObject)
    {
        // adjust our own title
        _wpSetTitle(somSelf, _wpQueryTitle(pObject));
        // and icon
        // _wpSetIcon(somSelf, _wpQueryIcon(pObject));

        _wpModifyStyle(somSelf,
                       OBJSTYLE_NOTDEFAULTICON,
                       0);

        // _wpModifyStyle(somSelf, OBJSTYLE_NOTDEFAULTICON, 0);
        /* _wpSetStyle(somSelf,
                    _wpQueryStyle(somSelf) & ~OBJSTYLE_CUSTOMICON); */
            // NEVER CALL ANY OF THESE, this messes up the icon

        // set size of related object to 0 initially;
        // this is properly calculated on the File thread
        // later
        _llTotalSize.ulHi = _llTotalSize.ulLo = 0;  // V1.0.9

        brc = TRUE;
    }
    return brc;
}

/*
 *@@ xwpQueryRelatedObject:
 *      this returns the object in the "\Trash"
 *      directories which this trash object
 *      represents. The result may be an instance
 *      of any WPS class.
 *
 *      Returns NULL upon errors.
 *
 *      This may be called at any time.
 */

SOM_Scope WPObject*  SOMLINK xtro_xwpQueryRelatedObject(XWPTrashObject *somSelf)
{
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpQueryRelatedObject");

    return _pRelatedObject;
}

/*
 *@@ xwpQueryRelatedPath:
 *      this returns a PSZ to the original path where
 *      the related object was deleted from. This PSZ
 *      points to the trash object's instance data,
 *      so you better not modify it.
 *
 *      Note that this source directory might no longer
 *      exist, if a whole folder tree was moved into
 *      the trash can.
 */

SOM_Scope PSZ SOMLINK xtro_xwpQueryRelatedPath(XWPTrashObject *somSelf)
{
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpQueryRelatedPath");

    PMPF_TRASHCAN(("[%s]", _wpQueryTitle(somSelf)));

    if (_pszSourcePath == NULL)
        // source path not queried yet:
        // do it now
        _pszSourcePath = trshComposeRelatedPath(somSelf);

    return _pszSourcePath;
}

/*
 *@@ xwpSetExpandedObjectSize:
 *      this gets called on the File thread when
 *      the total size of an object has been
 *      calculated (possibly including subfolders).
 *      We need to update the instance variables,
 *      refresh the object's details and update
 *      the trash can's data by calling
 *      XWPTrashCan::xwpAddObjectSize.
 *
 *@@added V0.9.2 (2000-02-28) [umoeller]
 *@@changed V0.9.6 (2000-10-25) [umoeller]: no longer storing data statically, func renamed
 *@@changed V0.9.6 (2000-11-12) [umoeller]: now using thousands separator from "Country"
 */

SOM_Scope void  SOMLINK xtro_xwpSetExpandedObjectSize(XWPTrashObject *somSelf,
                                                      ULONG ulNewSize,
                                                      XWPTrashCan* pTrashCan)
{
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpSetExpandedObjectSize");

    _llTotalSize.ulHi = 0;  // V1.0.9
    _llTotalSize.ulLo = ulNewSize;

    // update string for details view, which has been
    // "calculating..." so far; we cannot just use the
    // ULONG in the details field, because we had a
    // string previously
    nlsThousandsULong(_szTotalSize,
                      ulNewSize,
                      cmnQueryThousandsSeparator());

    // refresh all details views this object is
    // inserted into (most probably only the trash can)
    _wpCnrRefreshDetails(somSelf);

    _xwpAddObjectSize(pTrashCan, ulNewSize);
}

/*
 * @@ xwpQueryRelatedSize:
 *      returns the size of the related object. This
 *      is 0 if the related object is not a file-system
 *      object (WPFileSystem or subclasses).
 */

SOM_Scope ULONG  SOMLINK xtro_xwpQueryRelatedSize(XWPTrashObject *somSelf)
{
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpQueryRelatedSize");

    return _llTotalSize.ulHi ? 1 : _llTotalSize.ulLo;  // V1.0.9
}

/*
 *@@ xwpValidateTrashObject:
 *      this performs all kinds of checks on the trash
 *      object and its related object and returns an APIRET
 *      with the result.
 *
 *      If any errors are found, a value != NO_ERROR is returned and
 *      the trash object destroys itself by calling wpFree().
 *
 *      Return values:
 *      --  ERROR_INVALID_HANDLE: internal pointer to related object
 *                                      is NULL
 *      --  ERROR_FILE_NOT_FOUND: related object no longer exists
 */

SOM_Scope ULONG  SOMLINK xtro_xwpValidateTrashObject(XWPTrashObject *somSelf)
{
    APIRET  arc = NO_ERROR;

    // XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpValidateTrashObject");

    arc = trshValidateTrashObject(somSelf);
    return arc;
}

/*
 *@@ xwpRestoreFromTrashCan:
 *      this restores the object which this trash
 *      object represents from the "\Trash" directories.
 *
 *      If (pTargetFolder != NULL), the object will
 *      be restored in the specified folder.
 *
 *      If (pTargetFolder == NULL), the object will
 *      be restored in the folder where it was originally
 *      deleted from. If that folder no longer exists,
 *      it will be recreated as a standard folder.
 *
 *      After that, if the restore was successful,
 *      the trash object (somSelf) destroys itself by
 *      calling wpFree (since it has no further
 *      meaning) and returns TRUE.
 *
 *      As a consequence, the somSelf pointer to the trash
 *      object is no longer valid if TRUE is returned here.
 */

SOM_Scope BOOL  SOMLINK xtro_xwpRestoreFromTrashCan(XWPTrashObject *somSelf,
                                                    WPFolder* pTargetFolder)
{
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpRestoreFromTrashCan");

    return trshRestoreFromTrashCan(somSelf, pTargetFolder);
}

/*
 *@@ xwpSetExpandedObjectSizeL:
 *      See xwpSetExpandedObjectSize for details.
 *
 *@@added V1.0.9 (2010-07-17) [pr]
 */

SOM_Scope void  SOMLINK xtro_xwpSetExpandedObjectSizeL(XWPTrashObject *somSelf,
                                                       PLONGLONG pllNewSize,
                                                       XWPTrashCan* pTrashCan)
{
    double dSize;

    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpSetExpandedObjectSizeL");

    _llTotalSize = *pllNewSize;
    dSize = 65536.0 * 65536.0 * pllNewSize->ulHi + pllNewSize->ulLo;
    nlsThousandsDouble(_szTotalSize,
                       dSize,
                       cmnQueryThousandsSeparator());

    // refresh all details views this object is
    // inserted into (most probably only the trash can)
    _wpCnrRefreshDetails(somSelf);
    _xwpAddObjectSizeL(pTrashCan, pllNewSize);
}

/*
 *@@ xwpQueryRelatedSizeL:
 *      See xwpQueryRelatedSize for details.
 *
 *@@added V1.0.9 (2010-07-17) [pr]
 */

SOM_Scope void  SOMLINK xtro_xwpQueryRelatedSizeL(XWPTrashObject *somSelf,
                                                  LONGLONG* pllSize)
{
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_xwpQueryRelatedSizeL");

    *pllSize = _llTotalSize;
}

/*
 *@@ 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.
 */

SOM_Scope void  SOMLINK xtro_wpInitData(XWPTrashObject *somSelf)
{
    // PNLSSTRINGS     pNLSStrings = cmnQueryNLSStrings();
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpInitData");

    // initialize this first, because wpInitData calls
    // wpSetup, which apparently sets up the Details data...
    _pRelatedObject = NULL;
    _pszSourcePath = NULL;
    _szTotalSize[0] = '\0';
    /* cmnGetString2(_szTotalSize,
                  ID_XTSI_CALCULATING,
                  sizeof(_szTotalSize)); */

    XWPTrashObject_parent_WPTransient_wpInitData(somSelf);
}

/*
 *@@ wpUnInitData:
 *      this WPObject instance method is called when the object
 *      is destroyed as a SOM object, either because it's being
 *      made dormant (via wpMakeDormant) or being deleted for
 *      good (via wpFree). All allocated in-memory resources
 *      should be freed here, but to destroy the physical
 *      representation of the object, override wpDestroyObject
 *      instead.
 *
 *      The parent method must always be called last.
 *
 *@@changed V0.9.7 (2000-11-29) [umoeller]: fixed memory leak
 */

SOM_Scope void  SOMLINK xtro_wpUnInitData(XWPTrashObject *somSelf)
{
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpUnInitData");

    if (_pszSourcePath)
    {
        free(_pszSourcePath);
        _pszSourcePath = NULL;
    }

    XWPTrashObject_parent_WPTransient_wpUnInitData(somSelf);
}

/*
 *@@ 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.
 *
 *      This is where we need to set the "related object"
 *      which this trash object points to, because this
 *      must be done while the object is being initialized.
 *      For this, we use the RELATEDOBJECT setup string.
 *      If we do this later, wpQueryDetailsData would return
 *      garbage, and we'd need an extra wpCnrRefreshDetails,
 *      causing the details view to flicker like crazy.
 *
 *      The syntax of RELATEDOBJECT is "RELATEDOBJECT=xxx",
 *      with "xxx" being the SOM object pointer as a plain
 *      hex string... looks ugly, but there's no other way
 *      to get this done.
 *
 *@@added V0.9.3 (2000-04-09) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xtro_wpSetupOnce(XWPTrashObject *somSelf,
                                         PSZ pszSetupString)
{
    // XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpSetupOnce");

    // call parent first
    if (XWPTrashObject_parent_WPTransient_wpSetupOnce(somSelf,
                                                      pszSetupString))
    {
        // OK:
        return trshSetupOnce(somSelf, pszSetupString);
    }

    return FALSE;
}

/*
 *@@ wpQueryIcon:
 *      this WPObject instance method returns the HPOINTER
 *      with the current icon of the object. For some WPS
 *      classes, icon loading is deferred until the first
 *      call to this method.
 *      See icons.c for an introduction.
 *
 *      Before V0.9.16, we used to set our icon directly
 *      in xwpSetRelatedObject when the trash object was
 *      created. That was quite slow; we now defer this
 *      until the icon is really needed.
 *
 *@@added V0.9.16 (2002-01-09) [umoeller]
 */

SOM_Scope HPOINTER  SOMLINK xtro_wpQueryIcon(XWPTrashObject *somSelf)
{
    PMINIRECORDCORE pmrc = _wpQueryCoreRecord(somSelf);
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpQueryIcon");

    if (_pRelatedObject)
    {
        // on the first call, copy icon from related object
        if (!pmrc->hptrIcon)
            _wpSetIcon(somSelf,
                       _wpQueryIcon(_pRelatedObject));
        return pmrc->hptrIcon;
    }

    return XWPTrashObject_parent_WPTransient_wpQueryIcon(somSelf);
                // dull red default icon
}

/*
 * wpQueryDetailsData:
 *      this instance method must fill in the details
 *      data which was abstractly defined by
 *      M_XWPTrashObject::wpclsQueryDetailsInfo.
 *      We'll fill in the trash object details here.
 */

SOM_Scope ULONG  SOMLINK xtro_wpQueryDetailsData(XWPTrashObject *somSelf,
                                                 PVOID* ppDetailsData,
                                                 PULONG pcp)
{
    // return value: TRUE or FALSE, even though it's a ULONG
    ULONG ulrc;

    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpQueryDetailsData");

    // call the parent first; this moves ppDetailsData to
    // point to the new data added by XWPTrashObject
    // (in the wpclsQueryDetailsInfo structure)
    ulrc = XWPTrashObject_parent_WPTransient_wpQueryDetailsData(somSelf,
                                                                ppDetailsData,
                                                                pcp);

    // pointer valid?
    if (ppDetailsData)
    {
        PXTRO_DETAILS pDetails = (PXTRO_DETAILS)*ppDetailsData;
        pDetails->pszDeletedFrom = _xwpQueryRelatedPath(somSelf);
        pDetails->llSize = _llTotalSize;  // V1.0.9
        pDetails->pszSize = _szTotalSize;
        if (_pRelatedObject)
        {
            // set deletion date and time fiels by calling
            // the XFldObject method:
            _xwpQueryDeletion(_pRelatedObject,
                              &pDetails->cdateDeleted,
                              &pDetails->ctimeDeleted);
            pDetails->pszOriginalClass = _somGetName(_somGetClass(_pRelatedObject));

#ifdef __DEBUG__
            pDetails->pszMapping = _wpQueryTitle(_wpQueryFolder(_pRelatedObject));
#endif
        }

        // move the pointer past our details structure
        *ppDetailsData = ((PBYTE)(*ppDetailsData)) + sizeof(XTRO_DETAILS);
    }
    else
    {
        // ppDetailsData == NULL:
        // caller is querying size of buffer
        *pcp += sizeof(XTRO_DETAILS);
    }

    return ulrc;
}

/*
 *@@ wpFilterPopupMenu:
 *      this WPObject instance method allows the object to
 *      filter out unwanted menu items from the context menu.
 *      This gets called before wpModifyPopupMenu.
 *
 *      We remove "Open" and "Create another".
 *
 *@@changed V0.9.1 (2000-01-12) [umoeller]: now removing "Create another" as well
 */

SOM_Scope ULONG  SOMLINK xtro_wpFilterPopupMenu(XWPTrashObject *somSelf,
                                                ULONG ulFlags,
                                                HWND hwndCnr,
                                                BOOL fMultiSelect)
{
    /* XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf); */
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpFilterPopupMenu");

    return (XWPTrashObject_parent_WPTransient_wpFilterPopupMenu(somSelf,
                                                                ulFlags,
                                                                hwndCnr,
                                                                fMultiSelect)
            // remove the whole "Open" menu, since
            // trash objects cannot be opened;
            // remove "create another" as well
            & ~(CTXT_OPEN | CTXT_NEW)
        );
}

/*
 *@@ wpModifyPopupMenu:
 *      this WPObject instance methods gets called by the WPS
 *      when a context menu needs to be built for the object
 *      and allows the object to manipulate its context menu.
 *      This gets called _after_ wpFilterPopupMenu.
 *
 *      We add the trash object menu items here.
 *
 *@@changed V0.9.3 (2000-04-26) [umoeller]: now disabling menu items if trash can is busy
 */

SOM_Scope BOOL  SOMLINK xtro_wpModifyPopupMenu(XWPTrashObject *somSelf,
                                               HWND hwndMenu,
                                               HWND hwndCnr,
                                               ULONG iPosition)
{
    BOOL brc = FALSE;
    /* XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf); */
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpModifyPopupMenu");

    brc = XWPTrashObject_parent_WPTransient_wpModifyPopupMenu(somSelf,
                                                              hwndMenu,
                                                              hwndCnr,
                                                              iPosition);
    if (brc)
    {
        ULONG           ulAttr = 0;
        XWPTrashCan     *pTrashCan;

        if (    (pTrashCan = _wpQueryFolder(somSelf))
             && (_somIsA(pTrashCan, _XWPTrashCan))
           )
        {
            CHAR    szDestroyItem[100];
            ULONG   len;

            if (_xwpTrashCanBusy(pTrashCan,
                                 0))     // query busy
                // currently populating:
                ulAttr = MIA_DISABLED;

            // insert separator
            cmnInsertSeparator(hwndMenu, MIT_END);

            // insert "Restore object"
            winhInsertMenuItem(hwndMenu, MIT_END,
                               (*G_pulVarMenuOfs + ID_XFMI_OFS_TRASHRESTORE),
                               cmnGetString(ID_XTSI_TRASHRESTORE),  // pszTrashRestore
                               MIS_TEXT,   // style
                               ulAttr);    // attributes, can be "disabled"

            // insert "Destroy object"
            len = cmnGetString2(szDestroyItem,
                                ID_XTSI_TRASHDESTROY,
                                sizeof(szDestroyItem) - 4);
            if (cmnQuerySetting(sflTrashConfirmEmpty) & TRSHCONF_DESTROYOBJ)
                // confirm destroy on:
                memcpy(szDestroyItem + len,
                       "...",
                       4);
            winhInsertMenuItem(hwndMenu, MIT_END,
                               (*G_pulVarMenuOfs + ID_XFMI_OFS_TRASHDESTROY),
                               szDestroyItem,
                               MIS_TEXT,   // style
                               ulAttr);    // attributes, can be "disabled"
        }
    }

    return brc;
}

/*
 *@@ wpMenuItemSelected:
 *      this WPObject method processes menu selections.
 *      This must be overridden to support new menu
 *      items which have been added in wpModifyPopupMenu.
 *
 *      See XFldObject::wpMenuItemSelected for additional
 *      remarks.
 *
 *      We need to to support the trash object items.
 *
 *      Actually, this method doesn't get called any longer
 *      for the operations which are checked for here
 *      because XWPTrashCan::xwpProcessViewCommand already
 *      handles those commands.
 *
 *@@changed V0.9.12 (2001-05-22) [umoeller]: added destroy
 */

SOM_Scope BOOL  SOMLINK xtro_wpMenuItemSelected(XWPTrashObject *somSelf,
                                                HWND hwndFrame,
                                                ULONG ulMenuId)
{
    BOOL    brc = FALSE;
    ULONG   ulOfs = *G_pulVarMenuOfs;
    XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpMenuItemSelected");

    // Note, these menu items should never be called really...
    // the subclassed folder frame window proc already intercepts
    // these.
    if (ulMenuId == (ulOfs + ID_XFMI_OFS_TRASHRESTORE))
    {
        // "Restore object":
        brc = _xwpRestoreFromTrashCan(somSelf,
                                      NULL);       // use original folder
    }
    else if (ulMenuId == (ulOfs + ID_XFMI_OFS_TRASHDESTROY))
    {
        // "Destroy object":
        // free related object
        if (_pRelatedObject)
            _wpFree(_pRelatedObject);
                    // this kills somSelf in turn thru XFldObject
    }
    // none of our menu items: call default
    else
        brc = XWPTrashObject_parent_WPTransient_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 need to display help for our new menu items here.
 *
 *@@added V0.9.4 (2000-08-03) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xtro_wpMenuItemHelpSelected(XWPTrashObject *somSelf,
                                                    ULONG MenuId)
{
    ULONG   ulOfs = *G_pulVarMenuOfs;
    /* XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf); */
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpMenuItemHelpSelected");

    if (    (MenuId == (ulOfs + ID_XFMI_OFS_TRASHRESTORE))
         || (MenuId == (ulOfs + ID_XFMI_OFS_TRASHDESTROY))
       )
    {
        // now open the help panel we've set above
        cmnDisplayHelp(somSelf,
                       ID_XSH_SETTINGS_TRASHCAN);
        return TRUE;
    }

    return (XWPTrashObject_parent_WPTransient_wpMenuItemHelpSelected(somSelf,
                                                                     MenuId));
}

/*
 *@@ wpMoveObject:
 *      this instance method gets called by the WPS on an
 *      object if it is to be moved.
 *
 *      Since we don't really want to move trash objects
 *      out of a trash can, but rather restore the object
 *      to the target folder instead, we will do this here.
 *      This way we need not mess with the drag'n'drop
 *      stuff, and the other move invocations will work
 *      also.
 *
 *      So we call XWPTrashObject::xwpRestoreFromTrashCan
 *      here.
 */

SOM_Scope BOOL  SOMLINK xtro_wpMoveObject(XWPTrashObject *somSelf,
                                          WPFolder* Folder)
{
    // PTASKREC pTaskRec;

    // XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf);
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpMoveObject");

    return _xwpRestoreFromTrashCan(somSelf,
                                   Folder);  // move-target folder to recreate object in
        // this destroys somSelf
}

/*
 *@@ wpOpen:
 *      this WPObject instance method gets called when
 *      a new view needs to be opened. Normally, this
 *      gets called after wpViewObject has scanned the
 *      object's USEITEMs and has determined that a new
 *      view is needed.
 *
 *      This _normally_ runs on thread 1 of the WPS, but
 *      this is not always the case. If this gets called
 *      in response to a menu selection from the "Open"
 *      submenu or a double-click in the folder, this runs
 *      on the thread of the folder (which _normally_ is
 *      thread 1). However, if this results from WinOpenObject
 *      or an OPEN setup string, this will not be on thread 1.
 *
 *      Since trash objects have no views, we'll always
 *      return NULLHANDLE if some dumbass tries to open
 *      this.
 */

SOM_Scope HWND  SOMLINK xtro_wpOpen(XWPTrashObject *somSelf,
                                    HWND hwndCnr, ULONG ulView,
                                    ULONG param)
{
    /* XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf); */
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpOpen");

    return NULLHANDLE;

}

/*
 *@@ wpDragOver:
 *      this instance method is called to inform the object
 *      that other objects are being dragged over it.
 *      We'll make sure that nothing can be dropped upon
 *      a trash object.
 */

SOM_Scope MRESULT  SOMLINK xtro_wpDragOver(XWPTrashObject *somSelf,
                                           HWND hwndCnr,
                                           PDRAGINFO pdrgInfo)
{
    /* XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf); */
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpDragOver");

    return (MRESULT)DOR_NEVERDROP;   // object does not accept d'n'd
}

/*
 *@@ wpDrop:
 *      this instance method is called to inform an object that
 *      another object has been dropped on it.
 *      This corresponds to the DM_DROP message received by
 *      the object.
 *
 *      We'll make sure that nothing can be dropped upon
 *      a trash object.
 */

SOM_Scope MRESULT  SOMLINK xtro_wpDrop(XWPTrashObject *somSelf,
                                       HWND hwndCnr,
                                       PDRAGINFO pdrgInfo,
                                       PDRAGITEM pdrgItem)
{
    /* XWPTrashObjectData *somThis = XWPTrashObjectGetData(somSelf); */
    XWPTrashObjectMethodDebug("XWPTrashObject","xtro_wpDrop");

    return (MRESULT)RC_DROP_ERROR;
}

/* ******************************************************************
 *
 *   XWPTrashObject class methods
 *
 ********************************************************************/

/*
 *@@ CompareTrashSize:
 *      WPS sort comparison function for "sort by size".
 *      As opposed to the standard ULONG sort function,
 *      this sorts in descending order so that if we sort
 *      by size, the largest objects get on top.
 *
 *      NOTE: This is a WPS comparison func, NOT a cnr
 *      comparison func. This gets called from
 *      fnCompareDetailsColumn (if extended sorting is
 *      enabled), or from the WPS somewhere (if not).
 *
 *@@added V0.9.12 (2001-05-18) [umoeller]
 *@@changed V1.0.9 (2010-07-17) [pr]: added large file support @@fixes 586
 */

LONG EXPENTRY CompareTrashSize(PLONGLONG pll1,
                               PLONGLONG pll2)
{
    if ((pll1->ulHi < pll2->ulHi) ||
        ((pll1->ulHi == pll2->ulHi) && (pll1->ulLo < pll2->ulLo))
       )
        return CMP_GREATER;

    if ((pll1->ulHi > pll2->ulHi) ||
        ((pll1->ulHi == pll2->ulHi) && (pll1->ulLo > pll2->ulLo))
       )

        return CMP_LESS;

    return CMP_EQUAL;
}

/*
 *@@ wpclsInitData:
 *      this M_WPObject class method gets called when a class
 *      is loaded by the WPS (probably from within a
 *      somFindClass call) and allows the class to initialize
 *      itself.
 *
 *@@changed V0.9.4 (2000-08-03) [umoeller]: KERNELGLOBALS flag was wrong, fixed
 *@@changed V0.9.12 (2001-05-18) [umoeller]: fixed sort by size and date
 */

SOM_Scope void  SOMLINK xtroM_wpclsInitData(M_XWPTrashObject *somSelf)
{
    PCLASSFIELDINFO pcfi;
    ULONG           i;
    // PNLSSTRINGS     pNLSStrings = cmnQueryNLSStrings();

    /* M_XWPTrashObjectData *somThis = M_XWPTrashObjectGetData(somSelf); */
    M_XWPTrashObjectMethodDebug("M_XWPTrashObject","xtroM_wpclsInitData");

    M_XWPTrashObject_parent_M_WPTransient_wpclsInitData(somSelf);

    krnClassInitialized(G_pcszXWPTrashObject);

    // initialize the extra data file details
    // in the global variable at the top of this file
    for (i = 0, pcfi = G_acfiTrashObject;
         i < XTRO_EXTRAFIELDS;
         i++, pcfi++)
    {
        memset((PCH)pcfi, 0, sizeof(CLASSFIELDINFO));

        pcfi->cb        = sizeof(CLASSFIELDINFO);

        // data column flags
        pcfi->flData    = CFA_SEPARATOR
                            | CFA_FIREADONLY
                            | CFA_VCENTER       // vertical align
                            // | CFA_LEFT          // horizontal align
                            // | CFA_INVISIBLE
                            ;

        // column title flags
        pcfi->flTitle   = CFA_FITITLEREADONLY
                            | CFA_VCENTER
                            | CFA_CENTER
                            ;

        pcfi->pNextFieldInfo = pcfi + 1;       /* point to next CLASSFIELDINFO */

        switch (i)
        {
            // first item: original path
            case 0:
                pcfi->flCompare   = COMPARE_SUPPORTED | SORTBY_SUPPORTED;
                pcfi->flData            |= CFA_STRING | CFA_LEFT;
                pcfi->pTitleData        = (PSZ)cmnGetString(ID_XTSI_ORIGFOLDER);
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, pszDeletedFrom));
                pcfi->ulLenFieldData    = sizeof(PSZ);
                pcfi->DefaultComparison = CMP_GREATER;
            break;

            // second item: size of related object (ULONG)
            // this column is NOT displayed in details view,
            // but used for sorting! (we'd rather not sort by string)
            case 1:
                pcfi->flCompare         = COMPARE_SUPPORTED | SORTBY_SUPPORTED;
                pcfi->pfnSort           = (PFNCOMPARE)CompareTrashSize;
                                            // V0.9.12 (2001-05-18) [umoeller]
                pcfi->flData            |= CFA_ULONG | CFA_INVISIBLE;
                pcfi->pTitleData        = (PSZ)cmnGetString(ID_XTSI_SIZE);  // pszSize
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, llSize));  // V1.0.9
                pcfi->ulLenFieldData    = sizeof(LONGLONG);
                pcfi->DefaultComparison = CMP_GREATER;
            break;

            // third item: size of related object (string)
            // the string is displayed in details view
            case 2:
                pcfi->flCompare   = 0; // COMPARE_SUPPORTED | SORTBY_SUPPORTED;
                pcfi->flData            |= CFA_STRING | CFA_RIGHT;
                pcfi->pTitleData        = (PSZ)cmnGetString(ID_XTSI_SIZE);  // pszSize
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, pszSize));
                pcfi->ulLenFieldData    = sizeof(PSZ);
                pcfi->DefaultComparison = CMP_GREATER;
            break;

            // fourth item: class of related object
            case 3:
                pcfi->flCompare         = COMPARE_SUPPORTED | SORTBY_SUPPORTED;
                pcfi->flData            |= CFA_STRING | CFA_LEFT;
                pcfi->pTitleData        = (PSZ)cmnGetString(ID_XTSI_ORIGCLASS);  // pszOrigClass
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, pszOriginalClass));
                pcfi->ulLenFieldData    = sizeof(PSZ);
                pcfi->DefaultComparison = CMP_GREATER;
            break;

            // fifth item: deletion date
            case 4:
                pcfi->flCompare   = COMPARE_SUPPORTED | SORTBY_SUPPORTED;
                pcfi->flData            |= CFA_DATE | CFA_RIGHT;
                pcfi->pTitleData        = (PSZ)cmnGetString(ID_XTSI_DELDATE);  // pszDelDate
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, cdateDeleted));
                pcfi->ulLenFieldData    = sizeof(CDATE);
                pcfi->DefaultComparison = CMP_GREATER;
            break;

            // sixth item: deletion time
            case 5:
                pcfi->flCompare   = COMPARE_SUPPORTED;
                                // removed SORTBY_SUPPORTED because sort by date
                                // takes time into account as well V0.9.12 (2001-05-20) [umoeller]
                pcfi->flData            |= CFA_TIME | CFA_RIGHT;
                pcfi->pTitleData        = (PSZ)cmnGetString(ID_XTSI_DELTIME);  // pszDelTime
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, ctimeDeleted));
                pcfi->ulLenFieldData    = sizeof(CTIME);
                pcfi->DefaultComparison = CMP_GREATER;
            break;

#ifdef __DEBUG__
            // seventh item in debug mode: trash mapping belong to us
            case 6:
                pcfi->flCompare   = 0; // COMPARE_SUPPORTED | SORTBY_SUPPORTED;
                pcfi->flData            |= CFA_STRING | CFA_LEFT;
                pcfi->pTitleData        = "Mapping";
                pcfi->offFieldData      = (ULONG)(FIELDOFFSET(XTRO_DETAILS, pszMapping));
                pcfi->ulLenFieldData    = sizeof(PSZ);
                pcfi->DefaultComparison = CMP_GREATER;
            break;
#endif

        }   // end for
    } // end for

    // finally, terminate the linked list
    G_acfiTrashObject[XTRO_EXTRAFIELDS-1].pNextFieldInfo = NULL;
}

/*
 *@@ wpclsCreateDefaultTemplates:
 *      this M_WPObject class method is called by the
 *      Templates folder to allow a class to
 *      create its default templates.
 *
 *      The default WPS behavior is to create new templates
 *      if the class default title is different from the
 *      existing templates.
 *
 *      Since we never want templates for trash objects,
 *      we'll have to suppress this behavior.
 */

SOM_Scope BOOL  SOMLINK xtroM_wpclsCreateDefaultTemplates(M_XWPTrashObject *somSelf,
                                                          WPObject* Folder)
{
    /* M_XWPTrashObjectData *somThis = M_XWPTrashObjectGetData(somSelf); */
    M_XWPTrashObjectMethodDebug("M_XWPTrashObject","xtroM_wpclsCreateDefaultTemplates");

    return TRUE;
    // means that the Templates folder should _not_ create templates
    // by itself; we pretend that we've done this
}

/*
 * wpclsQueryDetailsInfo:
 *      this class method is called by the WPS to find out
 *      what details info objects of this class can provide.
 *      The "abstract" data returned by this function needs
 *      to be filled with instance information by
 *      XWPTrashObject::wpQueryDetailsData.
 *
 *      We'll define additional details for the trash objects
 *      here.
 *      Note that the CLASSFIELDINFO's have been initialized
 *      in M_XWPTrashObject::wpclsInitData already. This
 *      method only links the data into the details list
 *      given to us by the WPS.
 */

SOM_Scope ULONG  SOMLINK xtroM_wpclsQueryDetailsInfo(M_XWPTrashObject *somSelf,
                                                     PCLASSFIELDINFO* ppClassFieldInfo,
                                                     PULONG pSize)
{
    ULONG           ulParentColumns;
    PCLASSFIELDINFO pcfi;
    ULONG           i;

    /* M_XWPTrashObjectData *somThis = M_XWPTrashObjectGetData(somSelf); */
    M_XWPTrashObjectMethodDebug("M_XWPTrashObject","xtroM_wpclsQueryDetailsInfo");

    // always call the parent method first to retrieve the number of
    // details columns and any data already defined in the details buffer.
    // For WPTransient, these are probably only the default two fields
    // defined by WPObject (object title, object class).
    ulParentColumns = M_XWPTrashObject_parent_M_WPTransient_wpclsQueryDetailsInfo(
                                                somSelf,
                                                ppClassFieldInfo,
                                                pSize);

    // if pSize is non-NULL, we must add the size of our details
    // column data structure (that's "Query 2" in the WPS ref)
    if (pSize)
        *pSize += sizeof(XTRO_DETAILS);

    // if the request was for the chained fieldinfo structures
    // (ppClassFieldInfo is non-NULL), link the new ones in
    if (ppClassFieldInfo)
    {
        // if the beginning of the chain is 0, assign the address
        // of the first CLASSFIELDINFO structure to *ppClassFieldInfo.
        // Otherwise *pp points to the first column description in the
        // chain.  We need to walk the chain and link our CLASSFIELDINFO
        // structures at the end.
        if (*ppClassFieldInfo)
        {
            // find the last link in the chain; then add our CLASSFIELDINFO
            // structures to the chain.
            pcfi = *ppClassFieldInfo;
            for (i = 0;
                 i < ulParentColumns;
                 i++)
            {
                pcfi = (pcfi->pNextFieldInfo)
                            ? pcfi->pNextFieldInfo
                            : pcfi;     // appears to be a security check
            }

            // append our new field info to the list;
            // this data has been initialized in wpclsInitData
            pcfi->pNextFieldInfo = G_acfiTrashObject;
        }
        else
            // no fields defined yet (very improbable):
            // make ours the first
            *ppClassFieldInfo = G_acfiTrashObject;
    }

    return (ulParentColumns + XTRO_EXTRAFIELDS);
}

/*
 *@@ wpclsQueryTitle:
 *      this M_WPObject class method tells the WPS the clear
 *      name of a class, which is shown in the third column
 *      of a Details view and also used as the default title
 *      for new objects of a class.
 */

SOM_Scope PSZ  SOMLINK xtroM_wpclsQueryTitle(M_XWPTrashObject *somSelf)
{
    // PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();
    /* M_XWPTrashObjectData *somThis = M_XWPTrashObjectGetData(somSelf); */
    M_XWPTrashObjectMethodDebug("M_XWPTrashObject","xtroM_wpclsQueryTitle");

    return (PSZ)cmnGetString(ID_XTSI_TRASHOBJECT);
}

/*
 *@@ wpclsQueryStyle:
 *      we return lots of flags to make sure the user cannot
 *      mess with the trash objects.
 *
 *@@changed V0.9.16 (2001-11-25) [umoeller]: added nevertemplate
 */

SOM_Scope ULONG  SOMLINK xtroM_wpclsQueryStyle(M_XWPTrashObject *somSelf)
{
    /* M_XWPTrashObjectData *somThis = M_XWPTrashObjectGetData(somSelf); */
    M_XWPTrashObjectMethodDebug("M_XWPTrashObject","xtroM_wpclsQueryStyle");

    return (CLSSTYLE_NEVERTEMPLATE      // V0.9.16 (2001-11-25) [umoeller]
                | CLSSTYLE_NEVERCOPY    // but allow move
                | CLSSTYLE_NEVERLINK
                | CLSSTYLE_NEVERDELETE  // we have a "destroy" menu item already
                | CLSSTYLE_NEVERDROPON
                | CLSSTYLE_NEVERPRINT
                | CLSSTYLE_NEVERRENAME
                | CLSSTYLE_NEVERSETTINGS);
}

/*
 *@@ wpclsQueryDefaultHelp:
 *      this M_WPObject class method returns the default help
 *      panel for objects of this class. This gets called
 *      from WPObject::wpQueryDefaultHelp if no instance
 *      help settings (HELPLIBRARY, HELPPANEL) have been
 *      set for an individual object. It is thus recommended
 *      to override this method instead of the instance
 *      method to change the default help panel for a class
 *      in order not to break instance help settings (fixed
 *      with 0.9.20).
 *
 *      Since this is a subclass of WPTransient which doesn't
 *      have any help at all, we return help for trash objects
 *      here.
 *
 *@@added V0.9.20 (2002-07-12) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xtroM_wpclsQueryDefaultHelp(M_XWPTrashObject *somSelf,
                                                    PULONG pHelpPanelId,
                                                    PSZ pszHelpLibrary)
{
    /* M_XWPTrashObjectData *somThis = M_XWPTrashObjectGetData(somSelf); */
    M_XWPTrashObjectMethodDebug("M_XWPTrashObject","xtroM_wpclsQueryDefaultHelp");

    strcpy(pszHelpLibrary, cmnQueryHelpLibrary());
    *pHelpPanelId = ID_XSH_SETTINGS_TRASHCAN;
    return TRUE;
}

