
/*
 *@@sourcefile menus.c:
 *      This file contains the menu manipulation and selection
 *      logic for most of the XFolder context menu features.
 *      The functions in here are called by the XFolder and
 *      XFldDisk WPS methods for context menus. Since those two
 *      classes share many common menu items, they also share
 *      the routines for handling them.
 *
 *      This file also does the owner-drawing for folder-content
 *      menu items (at the bottom).
 *
 *      All the functions in this file have the mnu* prefix.
 *
 *      This file is new with V0.81. All these functions used to
 *      be in xfldr.c and have now been exported to make their
 *      use more lucid.
 *
 *@@include #define INCL_WINWINDOWMGR
 *@@include #define INCL_WINMENUS
 *@@include #define INCL_WINSTDCNR
 *@@include #include <os2.h>
 *@@include #include "xfldr.ih" // XFolder internals, for some funcs only
 *@@include #include <wppgm.h> // WPProgram, for some funcs only
 *@@include #include <wpobject.h> // only if other WPS headers are not included
 *@@include #include "common.h"
 *@@include #include "menus.h"
 */

/*
 *      Copyright (C) 1997-99 Ulrich M”ller.
 *      This file is part of the XFolder source package.
 *      XFolder is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XFolder main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

/*
 *  Suggested #include order:
 *  1)  os2.h
 *  2)  C library headers
 *  3)  SOM headers which work with precompiled header files
 *  4)  headers in /helpers
 *  5)  headers in /main with dlgids.h and common.h first
 *  6)  #pragma hdrstop to prevent VAC++ crashes
 *  7)  other needed SOM headers
 *  8)  for non-SOM-class files: corresponding header (e.g. classlst.h)
 */

#define INCL_DOSSEMAPHORES      // needed for xthreads.h
#define INCL_DOSEXCEPTIONS      // needed for except.h
#define INCL_DOSERRORS
#define INCL_DOSPROCESS         // DosSleep, priorities, PIDs etc.

#define INCL_WINWINDOWMGR
#define INCL_WINMESSAGEMGR      // WinQueryMsgPos
#define INCL_WINPROGRAMLIST     // needed for WPProgram
#define INCL_WINCLIPBOARD
#define INCL_WINPOINTERS
#define INCL_WINMENUS           // needed for menus.h
#define INCL_WINMLE             // multi-line entry field
#define INCL_WINSYS             // presparams, WinQuerySysValue()
#define INCL_WINCOUNTRY         // WinCompareStrings, WinUpper
#define INCL_WINDIALOGS         // WinSendDlgItemMsg
#define INCL_WININPUT           // WM_BUTTON1DOWN, WM_CONTEXTMENU etc.
#define INCL_WINFRAMEMGR        // WM_FORMATFRAME, SC_CLOSE etc.
#define INCL_WINSTDCNR          // needed for winh.h

#define INCL_GPILOGCOLORTABLE
#define INCL_GPIPRIMITIVES

#define INCL_DEV

#include <os2.h>

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

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

#include "linklist.h"           // linked list helper routines
#include "undoc.h"              // some undocumented stuff

// SOM headers which don't crash with prec. header files

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

#include "except.h"             // XFolder exception handling
#include "module.h"             // XFolder main DLL information
#include "sound.h"              // declarations for SOUND.DLL
#include "xshutdwn.h"           // XFolder eXtended Shutdown

// other SOM headers
#pragma hdrstop                 // VAC++ keeps crashing otherwise

#include "xfldr.ih"
#include "xfdisk.h"
#include "xwps.h"               // XFolder pseudo SOM functions
#include "common.h"             // the majestic XFolder include file
#include "xthreads.h"           // XFolder threads; this includes threads.h
#include  <wppgm.h>             // WPProgram
#include  <wpshadow.h>          // WPShadow
#include  <wpdesk.h>            // WPDesktop
#include  <wprootf.h>           // WPRootFolder

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

// linked lists / counts for variable context menu items
// pliVarItems contains ALL variable items ever inserted
// (i.e. config folder items AND folder content items)
PVARMENULISTITEM        pliVarItems = NULL;
// counts for providing unique menu id's
ULONG       ulVarItemCount = 0;    // number of inserted menu items
SHORT       sNextMenuId = 0;      // next menu item ID to use
// pcmliContentMenus contains ONLY folder content menus
PCONTENTMENULISTITEM    pcmliContentMenus = NULL;

// icon for drawing the little triangle in
// folder content menus (subfolders)
HPOINTER hMenuArrowIcon = NULLHANDLE;

BOOL fIsWarp4;

/* ******************************************************************
 *                                                                  *
 *   Functions for manipulating context menus                       *
 *                                                                  *
 ********************************************************************/

/*
 *@@ mnuAppendMi2List:
 *      this stores a variable XFolder menu item in the
 *      respective global linked list (pliVarItems)
 *      and increases sNextMenuId for the next item.
 *      Returns FALSE if too many items have already been
 *      used and menus should be closed.
 */

BOOL mnuAppendMi2List(WPObject *pObject, USHORT usObjType)
{
    // CHAR* p;
    /* remember program object's data for later use in wpMenuItemSelected
       itemCount is unique for each inserted object */
    PVARMENULISTITEM pNewItem = (PVARMENULISTITEM)malloc(sizeof(VARMENULISTITEM));
    pNewItem->pObject = pObject;
    strncpy(pNewItem->szTitle, _wpQueryTitle(pObject), sizeof(pNewItem->szTitle)-1);

    strhBeautifyTitle(pNewItem->szTitle);

    pNewItem->usObjType = usObjType;
    lstAppendItem((PLISTITEM*)&pliVarItems, NULL, (PLISTITEM)pNewItem);

    if (sNextMenuId < 0x7F00) {
        sNextMenuId++;
        ulVarItemCount++;
        return (TRUE);
    } else {
        xthrPostWorkplaceObjectMsg(XOM_LIMITREACHED, MPNULL, MPNULL);
        return (FALSE);
    }
}

/*
 *@@ mnuAppendFldr2ContentList:
 *      this stores a folder / menu item id in a global linked
 *      list so that the subclassed window procedure can find
 *      it later for populating the folder and displaying its
 *      contents in the menu.
 */

VOID mnuAppendFldr2ContentList(WPFolder *pFolder, SHORT sMenuId)
{
    PCONTENTMENULISTITEM pNewItem = (PCONTENTMENULISTITEM)malloc(sizeof(CONTENTMENULISTITEM));
    pNewItem->pFolder = pFolder;
    pNewItem->sMenuId = sMenuId;
    lstAppendItem((PLISTITEM*)&pcmliContentMenus, NULL, (PLISTITEM)pNewItem);
}

/*
 *@@ mnuInsertOneObjectMenuItem:
 *      this sub-subroutine is called by mnuFillContentSubmenu
 *      whenever a single menu item is to be inserted
 *      (all objects except folders); returns the menu
 *      item id.
 */

ULONG mnuInsertOneObjectMenuItem(HWND       hAddToMenu,   // hwnd of menu to add to
                    USHORT     iPosition,
                    PSZ        pszNewItemString,          // title of new item
                    USHORT     afStyle,
                    WPObject   *pObject,                  // pointer to corresponding object
                    ULONG      usObjType)
{
    ULONG               rc = sNextMenuId;

    winhInsertMenuItem(hAddToMenu, iPosition,
            sNextMenuId, pszNewItemString,
            afStyle, 0);

    if (mnuAppendMi2List(pObject, usObjType))
        // give signal for calling function that we found
        //   something */
        return (rc);
    else {
        return (0); // report error
    }
}

/*
 *@@ mnuPrepareContentSubmenu:
 *      prepares a "folder content" submenu by inserting a
 *      submenu menu item with pszTitle into hwndMenu; this
 *      submenu now only contains the "empty" menu item,
 *      however, this menu will be filled with objects if
 *      the user opens it; this is then done by answering
 *      the WM_INITMENU message in fnwpSubclassedFolderFrame.
 *      This function returns the submenu item ID.
 */

SHORT mnuPrepareContentSubmenu(XFolder *somSelf, // in: folder whose content is to be displayed
                               HWND hwndMenu,    // in: menu to insert submenu into
                               PSZ pszTitle,     // in: submenu item title
                               USHORT iPosition, // in: position to insert at (or MIT_END)
                               BOOL fOwnerDraw)  // in: owner-draw style flag for submenu (ie. display icons)
{
    HWND    hwndNewMenu;
    SHORT   sId = sNextMenuId;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();

    if (hwndNewMenu = winhInsertSubmenu(hwndMenu, iPosition,
                sId, pszTitle, (fOwnerDraw ? MIS_OWNERDRAW : 0),
                (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_DUMMY),
                    (cmnQueryNLSStrings())->pszFldrEmpty,
                    MIS_TEXT, MIA_DISABLED))
    {
        mnuAppendFldr2ContentList(somSelf, sId);
        mnuAppendMi2List(somSelf, OC_CONTENTFOLDER);
        return(sId);
    }

    return (0);
}

typedef struct _MENULISTITEM {
    struct _MENULISTITEM   *pNext, *pPrevious;
    ULONG       ulSize;
    WPObject    *pObject;
    CHAR        szItemString[256];
} MENULISTITEM, *PMENULISTITEM;

/*
 *@@ fncbSortContentMenuItems:
 *      callback sort func for sorting the folder content
 *      menu items alphabetically
 */

SHORT fncbSortContentMenuItems(PVOID pItem1, PVOID pItem2, PVOID hab)
{
    switch (WinCompareStrings((HAB)hab, 0, 0,
                ((PMENULISTITEM)pItem1)->szItemString,
                ((PMENULISTITEM)pItem2)->szItemString, 0))
    {
        case WCS_LT:    return (-1);
        case WCS_GT:    return (1);
        default:        return (0);
    }

}

/*
 *@@ mnuFillContentSubmenu:
 *      this fills a submenu created with mnuPrepareContentSubmenu
 *      with the contents of the corresponding folder; this
 *      func only needs the sMenuId returned by that function
 *      and the hwndMenu of the submenu which will be filled;
 *      it automatically finds the folder contents from the
 *      somSelf which was specified with mnuPrepareContentSubmenu.
 *      The submenu will be subclassed in order to allow opening
 *      objects with MB2.
 *      This function must store the result of WinSubclassWindow
 *      in *ppfnwpFolderContentOriginal, because this is needed
 *      for fnwpFolderContentMenu in xfldr.c.
 */

VOID mnuFillContentSubmenu(SHORT sMenuId,
                            HWND hwndMenu,
                            PFNWP *ppfnwpFolderContentOriginal)
{
    // tons of declarations...
    WPObject        *pObject, *pObject2;
    WPFolder        *pFolder = NULL;
    MENUITEM        mi;
    HPOINTER        hptrOld, hptrWait;
    RECTL           rtlItem;

    PCONTENTMENULISTITEM pcmli = pcmliContentMenus;

    HAB             habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    PTHREADGLOBALS  pThreadGlobals = xthrQueryGlobals();
    ULONG           *pulMenuFunc2 = &(pThreadGlobals->ulMenuFunc2);
    BOOL            fInsert;

    // first check if the menu contains the "[empty]" item;
    // this means that it's one of the folder content menus
    // and hasn't been filled yet (it could have been re-clicked,
    // and we don't want double menu items)

    if ((ULONG)WinSendMsg(hwndMenu, MM_ITEMIDFROMPOSITION, (MPARAM)0, MPNULL)
        == (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_DUMMY))
    {
        // get folder to be populated from the linked list
        // (pcmliContentMenus)
        *pulMenuFunc2 = 1000;
        while (pcmli)
            if (pcmli->sMenuId == sMenuId)
            {
                pFolder = pcmli->pFolder;
                break;
            }
            else
                pcmli = pcmli->pNext;

        // pFolder now contains the folder, pcmli contains
        // the CONTENTMENULISTITEM
        *pulMenuFunc2 = 1010;
        if (pFolder)
        {   // folder found: populate

            // show "Wait" pointer
            hptrOld = WinQueryPointer(HWND_DESKTOP);
            hptrWait = WinQuerySysPointer(HWND_DESKTOP,
                   SPTR_WAIT, FALSE);
            WinSetPointer(HWND_DESKTOP, hptrWait);

            // if pFolder is a disk object: get root folder
            *pulMenuFunc2 = 1020;
            if (_somIsA(pFolder, _WPDisk))
                pFolder = xwpsQueryRootFolder(pFolder, NULL);

            *pulMenuFunc2 = 1030;
            if (pFolder)
            {
                // populate
                xwpsCheckIfPopulated(pFolder);

                *pulMenuFunc2 = 1040;
                if (_wpQueryContent(pFolder, NULL, QC_FIRST))
                {
                    // folder does contain objects: go!
                    // The menu is separated into two sections (for
                    // folders and other objects), each of which
                    // is sorted alphabetically.
                    // We will first create two lists in memory
                    // for all folders and non-folders; we will
                    // then sort these lists alphabetically and
                    // finally insert their content into the menu
                    PMENULISTITEM   pmliFoldersFirst = NULL,
                                    pmliFoldersLast = NULL,   // folder list
                                    pmliObjectsFirst = NULL,
                                    pmliObjectsLast = NULL,   // non-folder list
                                    pmli;

                    SHORT       sItemSize, sItemsPerColumn, sItemId, sItemCount,
                                sColumns,
                                s;

                    // subclass menu window to allow MB2 clicks
                    *pulMenuFunc2 = 1050;
                    *ppfnwpFolderContentOriginal =
                            WinSubclassWindow(hwndMenu, fnwpFolderContentMenu);

                    // remove "empty" item
                    *pulMenuFunc2 = 1060;
                    WinSendMsg(hwndMenu, MM_REMOVEITEM,
                        MPFROM2SHORT((pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_DUMMY), TRUE), NULL);

                    *pulMenuFunc2 = 1070;
                    // now collect all objects in folder
                    for (pObject = _wpQueryContent(pFolder, NULL, QC_FIRST);
                         pObject;
                         pObject = _wpQueryContent(pFolder, pObject, QC_Next))
                    {
                        *pulMenuFunc2 = 1080;
                        // dereference shadows, if necessary
                        pObject2 = pObject;
                        while (pObject2) {
                            if (_somIsA(pObject2, _WPShadow))
                                pObject2 = _wpQueryShadowedObject(pObject2, TRUE);
                            else
                                break;
                        }

                        *pulMenuFunc2 = 1090;
                        if (pObject2)
                        {
                            // CHAR    *p;
                            fInsert = TRUE;

                            // exclude hidden file system objects
                            if (_somIsA(pObject, _WPFileSystem)) {
                                // _Pmpf(( "%s attr: %lX", szNewItemString, _wpQueryAttr(pObject2) ));
                                if (_wpQueryAttr(pObject2)
                                     & (FILE_HIDDEN)
                                   )
                                   fInsert = FALSE;
                            }

                            // exclude hidden WPS objects
                            if (_wpQueryStyle(pObject2) & OBJSTYLE_NOTVISIBLE)
                                   fInsert = FALSE;

                            if (fInsert) {
                                PMENULISTITEM pmliNew = malloc(sizeof(MENULISTITEM));
                                pmliNew->pObject = pObject2;
                                strcpy(pmliNew->szItemString, _wpQueryTitle(pObject2));
                                // remove line breaks
                                strhBeautifyTitle(pmliNew->szItemString);

                                if (    (_somIsA(pObject2, _WPFolder))
                                     || (_somIsA(pObject2, _WPDisk))
                                   )
                                {
                                    // folder/disk: append to folder list
                                    lstAppendItem((PLISTITEM*)&pmliFoldersFirst,
                                                  (PLISTITEM*)&pmliFoldersLast,
                                                  (PLISTITEM)pmliNew);
                                } else {
                                    // folder/disk: append to folder list
                                    lstAppendItem((PLISTITEM*)&pmliObjectsFirst,
                                                  (PLISTITEM*)&pmliObjectsLast,
                                                  (PLISTITEM)pmliNew);
                                }
                            }
                        }
                    } // end for pObject

                    // now sort the lists alphabetically
                    lstQuickSort((PLISTITEM*)&pmliFoldersFirst,
                            (PLISTITEM*)&pmliFoldersLast,
                            fncbSortContentMenuItems,
                            (PVOID)habDesktop);
                    lstQuickSort((PLISTITEM*)&pmliObjectsFirst,
                            (PLISTITEM*)&pmliObjectsLast,
                            fncbSortContentMenuItems,
                            (PVOID)habDesktop);

                    // insert folders into menu
                    pmli = pmliFoldersFirst;
                    while (pmli) {
                        // folder items
                        sItemId = mnuPrepareContentSubmenu(pmli->pObject,
                            hwndMenu,
                            pmli->szItemString,
                            MIT_END,
                            pGlobalSettings->FCShowIcons); // OwnerDraw flag

                        // next folder
                        pmli = pmli->pNext;
                    }

                    // if we have both objects and folders:
                    // insert separator between them
                    if ((pmliFoldersFirst) && (pmliObjectsFirst))
                       winhInsertMenuSeparator(hwndMenu, MIT_END,
                            (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

                    // insert objects into menu
                    pmli = pmliObjectsFirst;
                    while (pmli) {
                        sItemId = mnuInsertOneObjectMenuItem(hwndMenu,
                                     MIT_END,
                                     pmli->szItemString,
                                     ((pGlobalSettings->FCShowIcons)
                                        ? MIS_OWNERDRAW
                                        : MIS_TEXT),
                                     pmli->pObject,
                                     OC_CONTENT);
                        if (sItemId) {
                            pmli = pmli->pNext;
                        } else
                            pmli = NULL; // error: abort loop
                    }

                    // calculate maximum number of items per column by looking
                    // at the screen and item sizes
                    WinSendMsg(hwndMenu, MM_QUERYITEMRECT,
                        MPFROM2SHORT(sItemId, FALSE),
                        (MPARAM)&rtlItem);
                    sItemSize = (rtlItem.yTop-rtlItem.yBottom);
                    if (sItemSize == 0) sItemSize = 20;
                    sItemsPerColumn = (USHORT)(
                        (WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN)-30) / sItemSize );
                    if (sItemsPerColumn == 0)
                        sItemsPerColumn = 20;

                    // sItemsPerColumn now contains the items which will
                    // beautifully fit into one column; we now reduce this
                    // number if the last column would contain white space
                    sItemCount = (USHORT)WinSendMsg(hwndMenu, MM_QUERYITEMCOUNT, MPNULL, MPNULL);
                    // calculate number of resulting columns
                    sColumns = (sItemCount / sItemsPerColumn) + 1;
                    // distribute remainder in last column to all columns
                    sItemsPerColumn -= (sItemsPerColumn - (sItemCount % sItemsPerColumn)) / sColumns;

                    // now, for through every (sItemsPerColumn)'th menu item,
                    // set MIS_BREAK style
                    *pulMenuFunc2 = 1200;
                    for (s = sItemsPerColumn;
                         s < sItemCount;
                         s += sItemsPerColumn)
                    {
                        sItemId = (USHORT)WinSendMsg(
                                    hwndMenu, MM_ITEMIDFROMPOSITION, (MPARAM)s, MPNULL);
                        WinSendMsg(hwndMenu, MM_QUERYITEM,
                            MPFROM2SHORT(sItemId, FALSE),
                            &mi);
                        mi.afStyle |= MIS_BREAK;
                        WinSendMsg(hwndMenu, MM_SETITEM,
                            MPFROM2SHORT(sItemId, FALSE),
                            &mi);
                    }

                    // clean up
                    lstClear((PLISTITEM*)&pmliFoldersFirst,
                            (PLISTITEM*)&pmliFoldersLast);
                    lstClear((PLISTITEM*)&pmliObjectsFirst,
                            (PLISTITEM*)&pmliObjectsLast);
                }
            }

            *pulMenuFunc2 = 1299;
            // reset the mouse pointer
            WinSetPointer(HWND_DESKTOP, hptrOld);
        }
    }

    *pulMenuFunc2 = 0;
}

/*
 *@@ mnuFillMenuWithObjects:
 *      this subroutine is called by mnuModifyPopupMenu with
 *      *objFolder pointing to the main XFolder config folder.
 *      hAddToMenu then has the main context menu handle.
 *      It calls itself recursively when folders are encountered
 *      in the config folder, passing their pointers in *objFolder
 *      again. hAddToMenu then has new submenus.
 */

LONG mnuFillMenuWithObjects(WPObject   *somSelf,   // in: folder of context menu
                            WPFolder   *pFolder,   // in: config folder to process
                            HWND       hAddToMenu, // in: menu to add items to
                            HWND       hwndCnr)    // in: needed for wpInsertPopupMenuItems
{
    ULONG       ulStyle;
    WPObject    *pObject, *pObject2;
    PSZ         pszNewItemString = NULL;
    HWND        hNewMenu;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();

    LONG       lDefaultItem = 0;
    LONG       rc, lReturnDefaultItem = 0;

    // we iterate over the content of *pFolder;
    // remember, on the first call to this routine,
    // pFolder points to the config folder itself.
    // This routine will call itself recursively for each
    // folder encountered in the config folder, then passing
    // this folder in pFolder.

    for ( pObject = _xfQueryOrderedContent(pFolder, NULL, (ULONG)QC_FIRST);
          pObject;
          pObject = _xfQueryOrderedContent(pFolder, pObject, (ULONG)QC_NEXT)
        )
    {
        pszNewItemString = _wpQueryTitle(pObject);

        // if the object is a shadow, we will only de-reference
        //   it if it's a template
        if (_somIsA(pObject, _WPShadow)) {
            pObject2 = _wpQueryShadowedObject(pObject, FALSE);
            if (pObject2)
                if ((_wpQueryStyle(pObject2) & OBJSTYLE_TEMPLATE) == 0)
                    pObject2 = pObject;
        }
        else pObject2 = pObject;

        if (pObject2) { // eliminate broken shadows
            // now we check on the type of the class that we found and
            // remember the relevant types in the linked object list:
            // -- template: insert name, mark as QC_TEMPLATE
            // -- WPProgram: insert name into menu and mark as QC_PROGRAM;
            // -- WPFolder: insert submenu and recurse this routine;
            // -- others: insert name, mark as QC_OTHER;
            // we mark WPPrograms separately, since we will perform some
            // tricks on them in mnuMenuItemSelected when selected

            if (_wpQueryStyle(pObject2) & OBJSTYLE_TEMPLATE) {

                rc = mnuInsertOneObjectMenuItem(hAddToMenu,
                             MIT_END,
                             pszNewItemString,
                             MIS_TEXT,
                             pObject2,
                             OC_TEMPLATE);

                if (lReturnDefaultItem == 0)
                    lReturnDefaultItem = rc;

            } // end template
            else if (_somIsA(pObject2, _WPProgram)) {
                /* program object: get the title of it and insert it at the end of
                   the popup menu we're currently working on (can either be the
                   folder's context menu or submenu, when this routine is called
                   recursively; it's contained in hAddToMenu);
                   mark this item as OC_PROGRAM so we can perform tricks on it */

                if (strncmp(pszNewItemString, "---", 3) != 0)
                    rc = mnuInsertOneObjectMenuItem(hAddToMenu,
                                         MIT_END,
                                         pszNewItemString,
                                         MIS_TEXT,
                                         pObject2,
                                         OC_PROGRAM);
                else // title == "---": insert separator
                    rc = mnuInsertOneObjectMenuItem(hAddToMenu,
                                         MIT_END,
                                         pszNewItemString,
                                         MIS_SEPARATOR,
                                         pObject2,
                                         OC_SEPARATOR);

                if (lReturnDefaultItem == 0)
                    lReturnDefaultItem = rc;

            } // end else if (_somIsA(pObject2, _WPProgram))
            else if (_somIsA(pObject2, _WPFolder)) {
                //  if we find another folder, we add a submenu containing
                //  "[Config folder empty]"), which will be removed if more
                //  objects are found in the respective config folder

                hNewMenu = winhInsertSubmenu(hAddToMenu, MIT_END,
                            sNextMenuId, _wpQueryTitle(pObject2), MIS_TEXT,
                            (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_BORED),
                            (cmnQueryNLSStrings())->pszBored,
                                MIS_TEXT, 0);

                mnuAppendMi2List(pObject2, OC_FOLDER);

                // now call this function recursively with the new
                // folder and the new submenu handle */
                lDefaultItem = mnuFillMenuWithObjects(somSelf, pObject2, hNewMenu, hwndCnr);

                // now we're back: check if error occured; if so, exit
                // immediately to stop recursing
                if (lDefaultItem == -1)
                    return (-1);

                if (lDefaultItem) {
                    // items were inserted:
                    // remove static "config folder empty" menu item
                    WinSendMsg(hNewMenu,            // hAddToMenu,
                            MM_DELETEITEM,
                            MPFROM2SHORT((pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_BORED), TRUE),
                            (MPARAM)NULL);

                    if ((pGlobalSettings->MenuCascadeMode) && (lDefaultItem != 1)) {
                        // make the XFolder submenu cascading;
                        // stolen from the Warp Toolkit WPS Guide */
                        ulStyle = WinQueryWindowULong(hNewMenu, QWL_STYLE);
                        ulStyle |= MS_CONDITIONALCASCADE;
                        WinSetWindowULong(hNewMenu, QWL_STYLE, ulStyle);

                        // make the first item in the subfolder
                        // the default of cascading submenu */
                        WinSendMsg(hNewMenu,
                                MM_SETDEFAULTITEMID,
                                (MPARAM)(lDefaultItem), 0L);
                    }
                }

                if (lReturnDefaultItem == 0)
                    lReturnDefaultItem = 1;

            } // end else if (_somIsA(pObject2, _WPFolder))
            else {
                // types other than WPFolder and WPProgram:
                // simply add them to menu and mark them as OC_OTHER
                // (will simply be opened)
                rc = mnuInsertOneObjectMenuItem(hAddToMenu,
                             MIT_END,
                             pszNewItemString,
                             MIS_TEXT,
                             pObject2,
                             OC_OTHER);

                if (lReturnDefaultItem == 0)
                    lReturnDefaultItem = rc;
            }
        } // end if (pObject2)
    } // end for
    return (lReturnDefaultItem);
}

/*
 *@@ mnuCheckDefaultSortItem:
 *      checks/unchecks a sort item in the "Sort" submenu.
 */

VOID mnuCheckDefaultSortItem(PGLOBALSETTINGS pGlobalSettings,
                            HWND hwndSortMenu,
                            ULONG ulDefaultSort)
{
    winhSetMenuItemChecked(hwndSortMenu,
            WinSendMsg(hwndSortMenu, MM_QUERYDEFAULTITEMID,
                    MPNULL, MPNULL),        // find current default
            FALSE);                         // uncheck

    WinSendMsg(hwndSortMenu,
            MM_SETDEFAULTITEMID,
            (MPARAM)
                    ((ulDefaultSort == SV_NAME) ? ID_WPMI_SORTBYNAME
                    : (ulDefaultSort == SV_TYPE) ? ID_WPMI_SORTBYTYPE
                    : (ulDefaultSort == SV_CLASS) ? pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTBYCLASS
                    : (ulDefaultSort == SV_REALNAME) ? ID_WPMI_SORTBYREALNAME
                    : (ulDefaultSort == SV_SIZE) ? ID_WPMI_SORTBYSIZE
                    : (ulDefaultSort == SV_LASTWRITEDATE) ? ID_WPMI_SORTBYWRITEDATE
                    : (ulDefaultSort == SV_LASTACCESSDATE) ? ID_WPMI_SORTBYACCESSDATE
                    : (ulDefaultSort == SV_CREATIONDATE) ? ID_WPMI_SORTBYCREATIONDATE
                    : (ulDefaultSort == SV_EXT) ? pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTBYEXT
                    : pGlobalSettings->VarMenuOffset +ID_XFMI_OFS_SORTFOLDERSFIRST),
            MPNULL);
}

/*
 *@@ mnuModifySortMenu:
 *      this modifies the "Sort" submenu. Used for both
 *      folder context menus (mnuModifyPopupMenu below)
 *      and folder menu bars (WM_INITMENU message in
 *      fnwpSubclassedFolderFrame). For speedier operation,
 *      this func takes a lot of parameters.
 *
 *      Note that hwndMenu is NOT the window handle of the
 *      "Sort" menu, but the one of the parent menu, i.e.
 *      the context menu itself or the "View" menu in
 *      menu bars.
 */

VOID mnuModifySortMenu(HWND hwndMenu,               // parent of "Sort" menu
                XFolderData *somThis,               // XFolderGetData
                PGLOBALSETTINGS pGlobalSettings,    // cmnQueryGlobalSettings
                PNLSSTRINGS pNLSStrings)            // cmnQueryNLSStrings
{
    // work on "Sort" menu, if allowed
    if (pGlobalSettings->ReplaceSort)
    {
        if ((pGlobalSettings->DefaultMenuItems & CTXT_SORT) == 0)
        {
            HWND        hwndSortMenu;
            // SHORT       sItem, sItemCount;
            ULONG       ulDefaultSort = DEFAULT_SORT;
            MENUITEM    mi;

            if ((BOOL)WinSendMsg(hwndMenu,
                MM_QUERYITEM,
                MPFROM2SHORT(WPMENUID_SORT, TRUE),
                (MPARAM)&mi))
            {
                hwndSortMenu = mi.hwndSubMenu;
                // now contains "Sort" submenu handle;
                // insert the XFolder sort criteria
                winhInsertMenuItem(hwndSortMenu, 2, // after "type"
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTBYCLASS),
                        pNLSStrings->pszSortByClass,
                        MIS_TEXT, 0);
                winhInsertMenuItem(hwndSortMenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTBYEXT),
                        pNLSStrings->pszSortByExt,
                        MIS_TEXT, 0);
                winhInsertMenuItem(hwndSortMenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTFOLDERSFIRST),
                        pNLSStrings->pszSortFoldersFirst,
                        MIS_TEXT, 0);

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

                // insert "Always sort"
                winhInsertMenuItem(hwndSortMenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_ALWAYSSORT),
                        pNLSStrings->pszAlwaysSort,
                        MIS_TEXT,
                        (ALWAYS_SORT)           // check item if "Always sort" is on
                            ? (MIA_CHECKED)
                            : 0);

                mnuCheckDefaultSortItem(pGlobalSettings,
                                        hwndSortMenu,
                                        ulDefaultSort);

            } // end if MM_QUERYITEM
        }
    }
}

/*
 *@@ mnuInsertFldrViewItems:
 *      this inserts the folder view menu items
 *      ("Small icons", "Flowed" etc.)
 *      into hwndViewSubmenu if the current cnr view
 *      makes sense for this.
 *      This is called by mnuModifyPopupMenu. The
 *      code has been moved to a separate subroutine
 *      because we also need this functionality for
 *      Warp 4 menu bars.
 *      hwndViewSubmenu contains the submenu to add
 *      items to:
 *      --  on Warp 4, this is the default "View" submenu
 *      --  on Warp 3, mnuModifyPopupMenu creates a new
 *          "View" submenu, which is passed to this func.
 *      Returns TRUE if the menu items were inserted.
 */

BOOL mnuInsertFldrViewItems(XFolder *somSelf,       // in: folder w/ context menu
                            HWND hwndViewSubmenu,   // in: submenu to add items to
                            BOOL fInsertNewMenu,    // in: insert "View" into hwndViewSubmenu
                            HWND hwndCnr,           // in: cnr hwnd passed to
                                                    //     mnuModifyPopupMenu
                            ULONG ulView)           // in: OPEN_* flag
{
    BOOL        brc = FALSE;

    #ifdef DEBUG_MENUS
        _Pmpf(("mnuInsertFldrViewItems, hwndCnr: 0x%X", hwndCnr));
    #endif

    if (hwndCnr)
    {
        // we have a valid open view:

        XFolderData *somThis = XFolderGetData(somSelf);
        PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();
        PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
        PUSEITEM    pUseItem = NULL;
        ULONG       ulAttr = 0;
        USHORT      usIconsAttr;
        CNRINFO     CnrInfo;
        winhQueryCnrInfo(hwndCnr, CnrInfo);

        // add "small icons" item for all view types,
        // but disable for Details view

        if (ulView == OPEN_DETAILS) {
            // Details view: disable and check
            // "mini icons"
            usIconsAttr = (MIA_DISABLED | MIA_CHECKED);
        } else {
            // otherwise: set "mini icons" to cnr info data
            usIconsAttr = (CnrInfo.flWindowAttr & CV_MINI)
                                  ? MIA_CHECKED
                                  : 0;
        }

        if (fInsertNewMenu)
        {
            // on Warp 3, we need to add a "View" submenu
            // for the folder view flags, because Warp 3
            // doesn't have one
            hwndViewSubmenu = winhInsertSubmenu(hwndViewSubmenu,
                                    MIT_END,
                                    (pGlobalSettings->VarMenuOffset
                                            + ID_XFM_OFS_WARP3FLDRVIEW),
                                    pNLSStrings->pszWarp3FldrView,
                                        MIS_SUBMENU,
                                    // item
                                    (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SMALLICONS),
                                    pNLSStrings->pszSmallIcons,
                                    MIS_TEXT,
                                    usIconsAttr);
        } else
            winhInsertMenuItem(hwndViewSubmenu, MIT_END,
                    (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SMALLICONS),
                            pNLSStrings->pszSmallIcons, MIS_TEXT, usIconsAttr);

        brc = TRUE; // modified flag

        /* if (    ((pCnrInfo->flWindowAttr & (CV_ICON | CV_TREE)) == CV_ICON)
             || ((pCnrInfo->flWindowAttr & CV_NAME) == CV_NAME)
             || ((pCnrInfo->flWindowAttr & CV_TEXT) == CV_TEXT)
           ) */

        if (ulView == OPEN_CONTENTS)
        {
            // icon view:
            winhInsertMenuSeparator(hwndViewSubmenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));


            winhInsertMenuItem(hwndViewSubmenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_NOGRID),
                                pNLSStrings->pszNoGrid, MIS_TEXT,
                                ((CnrInfo.flWindowAttr & (CV_ICON | CV_TREE)) == CV_ICON)
                                    ? MIA_CHECKED
                                    : 0);
            winhInsertMenuItem(hwndViewSubmenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_FLOWED),
                                pNLSStrings->pszFlowed, MIS_TEXT,
                                ((CnrInfo.flWindowAttr & (CV_NAME | CV_FLOW)) == (CV_NAME | CV_FLOW))
                                    ? MIA_CHECKED
                                    : 0);
            winhInsertMenuItem(hwndViewSubmenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_NONFLOWED),
                                pNLSStrings->pszNonFlowed, MIS_TEXT,
                                ((CnrInfo.flWindowAttr & (CV_NAME | CV_FLOW)) == (CV_NAME))
                                    ? MIA_CHECKED
                                    : 0);
        }

        // for all views:
        winhInsertMenuSeparator(hwndViewSubmenu, MIT_END,
                    (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

        // status bar
        if (_somIsA(somSelf, _WPDesktop))
            ulAttr = MIA_DISABLED;
        else if (_somIsA(somSelf, _WPRootFolder))
            ulAttr = MIA_DISABLED
                        | ((pGlobalSettings->StatusBar)
                            ? MIA_CHECKED
                            : 0);
        else
            ulAttr = (
                       (_bStatusBar == STATUSBAR_ON)
                    || (    (_bStatusBar == STATUSBAR_DEFAULT)
                         && (pGlobalSettings->StatusBar)
                       )
                     )
                        ? MIA_CHECKED
                        : 0;
        winhInsertMenuItem(hwndViewSubmenu, MIT_END,
                    (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SHOWSTATUSBAR),
                            pNLSStrings->pszShowStatusBar, MIS_TEXT,
                                ulAttr);
    }
    return (brc);
}

/*
 *@@ mnuModifyPopupMenu:
 *      this is the general menu modifier routine which gets called
 *      by XFolder::wpModifyPopupMenu and XFldDisk::wpModifyPopupMenu.
 *      Since menu items are mostly the same for these two classes,
 *      the shared code has been moved into this function.
 *
 *      First we remove and insert various menu items according to
 *      the Global and instance settings; we then insert lots of
 *      menu items or (for folder content menus) empty submenus
 *      according to the config folders and folder content settings.
 */

BOOL mnuModifyPopupMenu(XFolder *somSelf,
                        HWND hwndMenu,      // in: main context menu hwnd
                        HWND hwndCnr,       // in: cnr hwnd
                        ULONG iPosition)    // in: dunno
{
    XFolder             *pConfigFolder;
    XFolder             *pFavorite;
    BOOL                rc = TRUE;
    MENUITEM            mi;

    PNLSSTRINGS pNLSStrings = cmnQueryNLSStrings();
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();

    REGREC2               RegRec2;
    ULONG                 ulExcpt;
    APIRET                arc;
    PSZ                   pszErrMsg = NULL;

    // set the global variable for whether Warp 4 is running
    fIsWarp4 = doshIsWarp4();

    begin:

    // set our extended exception handler
    RegRec2.pfnHandler = (PFN)excHandlerQuiet;
    if ( arc = DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&RegRec2) )
        DosBeep(100, 1000);
    // store a known thread state
    ulExcpt = setjmp(RegRec2.jmpThread);
    if (ulExcpt)
    {
        pszErrMsg = malloc(1000);
        if (pszErrMsg) {
            strcpy(pszErrMsg, "An error occured while XFolder was trying to build "
                    "a folder's context menu. This might be due to the fact "
                    "that you have deleted objects from the Configuration folders, "
                    "but you did "
                    "not have these folders opened in the Icon or Details views "
                    "while doing so. "
                    "You should open and close the configuration folder and all "
                    "of its subfolders once. Make sure that all the folders are either "
                    "in Icon or Details view per default.");
            xthrPostWorkplaceObjectMsg(XOM_EXCEPTIONCAUGHT, (MPARAM)pszErrMsg, MPNULL);
        }
    } else {
        if (somSelf) {
            // some preparations
            XFolderData *somThis = XFolderGetData(somSelf);
            HWND        hwndFrame = WinQueryWindow(hwndCnr, QW_PARENT);
            PUSEITEM    pUseItem = NULL;
            ULONG       ulView = -1;
            POINTL      ptlMouse;

            // _Pmpf(("hwndFrame: %lX, hwndCnr: %lX", hwndFrame, hwndCnr));

            // get container info
            // CNRINFO     CnrInfo;
            // winhQueryCnrInfo(hwndCnr, CnrInfo);

            // store mouse pointer position for creating objects from templates
            WinQueryMsgPos(WinQueryAnchorBlock(hwndCnr), &ptlMouse);
            _MenuMousePosX = ptlMouse.x;
            _MenuMousePosY = ptlMouse.y;

            #ifdef DEBUG_MENUS
                _Pmpf(("mnuModifyPopupMenu, hwndCnr: 0x%lX", hwndCnr));
            #endif

            // determine view we have here; this will set
            // ulView to one of the OPEN_* flags so that
            // we can insert menu items according to the
            // view that was opened
            for (pUseItem = _wpFindUseItem(somSelf, USAGE_OPENVIEW, NULL);
                pUseItem;
                pUseItem = _wpFindUseItem(somSelf, USAGE_OPENVIEW, pUseItem))
            {
                PVIEWITEM pViewItem = (PVIEWITEM)(pUseItem+1);
                if (pViewItem->handle == hwndFrame)
                {
                    ulView = pViewItem->view;
                    break;
                }
            }

            // in wpFilterPopupMenu, because no codes are provided;
            // we only do this if the Global Settings want it

            // "View" submenu
            if (fIsWarp4)
            {
                if (pGlobalSettings->RemoveViewMenu) {
                    #ifdef DEBUG_MENUS
                        _Pmpf(("  Removing 'View' submenu"));
                    #endif
                    WinSendMsg(hwndMenu, MM_REMOVEITEM,
                            MPFROM2SHORT(ID_WPM_VIEW, FALSE),
                            0);
                }

                if (pGlobalSettings->RemovePasteItem) {
                    #ifdef DEBUG_MENUS
                        _Pmpf(("  Removing 'Paste' menu item"));
                    #endif
                    WinSendMsg(hwndMenu, MM_REMOVEITEM,
                            MPFROM2SHORT(ID_WPMI_PASTE, FALSE),
                            0);
                }
            }

            // add product info item to the help menu, if the "Help"
            // menu has not been removed
            if ((pGlobalSettings->DefaultMenuItems & CTXT_HELP) == 0)
            {
                #ifdef DEBUG_MENUS
                    _Pmpf(("  Inserting 'Product info'"));
                #endif
                // get handle to the WPObject's "Help" submenu in the
                // the folder's popup menu
                WinSendMsg(hwndMenu,
                        MM_QUERYITEM,
                        MPFROM2SHORT(WPMENUID_HELP, TRUE),
                        (MPARAM)&mi);
                // mi.hwndSubMenu now contains "Help" submenu handle,
                // which we add items to now
                winhInsertMenuSeparator(mi.hwndSubMenu, MIT_END, (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));
                winhInsertMenuItem(mi.hwndSubMenu, MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_PRODINFO),
                        pNLSStrings->pszProductInfo,
                        MIS_TEXT, 0);
            }

            // work on the "View" submenu; do the following only
            // if the "View" menu has not been removed (Warp 4)
            // or the "Select" menu has not been removed (Warp 3)
            if (
                    ( (fIsWarp4)  && (pGlobalSettings->RemoveViewMenu == 0) )
                 || ( (!fIsWarp4) && ((pGlobalSettings->DefaultMenuItems & CTXT_SELECT) == 0) )
               )
            {

                SHORT sPos = MIT_END;

                #ifdef DEBUG_MENUS
                    _Pmpf(("  'View'/'Select' should be visible (Warp4: %d)", fIsWarp4));
                #endif

                if (WinSendMsg(hwndMenu,
                    MM_QUERYITEM,
                    MPFROM2SHORT( ((fIsWarp4)
                          // in Warp 4, these items are in the "View" submenu;
                          // in Warp 3, "Select" is  a separate submenu
                                ? ID_WPM_VIEW           // 0x68
                                : 0x04),      // WPMENUID_SELECT
                            TRUE),
                    (MPARAM)&mi))
                {
                    // mi.hwndSubMenu now contains "Select"/"View" submenu handle,
                    // which we can add items to now

                    #ifdef DEBUG_MENUS
                        _Pmpf(("  'View'/'Select' hwnd:0x%X", mi.hwndSubMenu));
                    #endif

                    // add "Select by name" only if not in Tree view
                    if (pGlobalSettings->AddSelectSomeItem)
                    {

                        if (fIsWarp4)
                            sPos = (SHORT)WinSendMsg(mi.hwndSubMenu,
                                            MM_ITEMPOSITIONFROMID,
                                            MPFROM2SHORT(WPMENUID_REFRESH, FALSE),
                                            MPNULL);
                        // else: MIT_END (above)

                        #ifdef DEBUG_MENUS
                            _Pmpf(("  Adding 'Select some' @ ofs %d, hwndSubmenu: 0x%lX",
                                        sPos, mi.hwndSubMenu));
                        #endif
                        winhInsertMenuItem(mi.hwndSubMenu,
                                sPos,
                                (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SELECTSOME),
                                pNLSStrings->pszSelectSome,
                                MIS_TEXT, 0);
                        if (fIsWarp4)
                            winhInsertMenuSeparator(mi.hwndSubMenu, sPos+1,
                                (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));
                    }

                    // additional "view" items (icon size etc.)
                    if (pGlobalSettings->ExtendFldrViewMenu) {

                        // rule out possible user views
                        // of WPFolder subclasses
                        if (    (ulView == OPEN_TREE)
                             || (ulView == OPEN_CONTENTS)
                             || (ulView == OPEN_DETAILS)
                           )
                        {
                            if (fIsWarp4)
                            {
                                // for Warp 4, use the existing "View" submenu,
                                // but add an additional separator after
                                // the exiting "View"/"Refresh now" item
                                winhInsertMenuSeparator(mi.hwndSubMenu, MIT_END,
                                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

                                mnuInsertFldrViewItems(somSelf,
                                                mi.hwndSubMenu,
                                                FALSE,
                                                hwndCnr,
                                                ulView);
                                                // &CnrInfo);
                            } else
                                // Warp 3:
                                mnuInsertFldrViewItems(somSelf,
                                                hwndMenu,
                                                TRUE, // on Warp 3, insert new submenu
                                                hwndCnr,
                                                ulView);
                                                // &CnrInfo);
                        }
                    }
                } else {
                    #ifdef DEBUG_MENUS
                        DosBeep(2000, 100);
                        DosBeep(2500, 100);
                        _Pmpf(("  *** 'view' menu not found"));
                            // this happens in the "folder" submenu
                            // of folder menu bars, because the WPS seems
                            // to cut out the View submenu of that
                    #endif
                }
            }

            // work on "Sort" menu; we have put this
            // into a subroutine, because this is also
            // needed for folder menu _bars_
            mnuModifySortMenu(hwndMenu, somThis, pGlobalSettings, pNLSStrings);

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

                winhInsertMenuItem(hwndMenu, MIT_END,
                    (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_COPYFILENAME_MENU),
                    (pNLSStrings)->pszCopyFilename,
                    0, 0);
            }

            // insert the "Refresh now" and "Snap to grid" items only
            // if the folder is currently open
            if (_wpFindUseItem(somSelf, USAGE_OPENVIEW, NULL))
            {
                if (!pGlobalSettings->AddCopyFilenameItem)
                    winhInsertMenuSeparator(hwndMenu, MIT_END,
                                (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

                // "Refresh now"
                if (pGlobalSettings->MoveRefreshNow) {
                    winhInsertMenuItem(hwndMenu, MIT_END,
                            (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_REFRESH),
                            pNLSStrings->pszRefreshNow,
                            MIS_TEXT, 0);
                }

                // "Snap to grid" set locally or globally?
                if (    (_bSnapToGridAllowed == 1)
                     || ((_bSnapToGridAllowed == 2) && (pGlobalSettings->AddSnapToGridItem))
                   )
                {
                    // insert only when sorting is off
                    if (!(ALWAYS_SORT))
                    {
                        if (ulView == OPEN_CONTENTS)
                        {
                            // insert "Snap to grid" only for open icon views
                            winhInsertMenuItem(hwndMenu, MIT_END,
                                    (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SNAPTOGRID),
                                    pNLSStrings->pszSnapToGrid,
                                    MIS_TEXT, 0);
                        }
                    }
                }
            } // end if view open

            // now do necessary preparations for all variable menu items
            // (i.e. folder content and config folder items)
            sNextMenuId = (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_VARIABLE);
            ulVarItemCount = 0;         // reset the number of variable items to 0

            lstClear((PLISTITEM*)&pcmliContentMenus, NULL);
            lstClear((PLISTITEM*)&pliVarItems, NULL);

            /*
             * folder content / favorite folders:
             *
             */

            // get first favorite folder; we will only work on
            // this if either folder content for every folder is
            // enabled or at least one favorite folder exists
            pFavorite = _xfclsQueryFavoriteFolder(_XFolder, NULL);
            if (    (pGlobalSettings->NoSubclassing == 0)
                 && (   (pGlobalSettings->AddFolderContentItem)
                     || (pFavorite)
                    )
               )
            {
                winhInsertMenuSeparator(hwndMenu,
                        MIT_END,
                        (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

                if (pGlobalSettings->FCShowIcons) {
                    // before actually inserting the content submenus, we need a real
                    // awful cheat, because otherwise the owner draw items won't work
                    // right (I don't know why the hell they are not being sent
                    // WM_MEASUREITEM msgs if we don't do this); so we insert a
                    // content submenu and remove it again right away
                    WinSendMsg(hwndMenu, MM_REMOVEITEM,
                        MPFROM2SHORT(mnuPrepareContentSubmenu(somSelf, hwndMenu,
                                 pNLSStrings->pszFldrContent,
                                 MIT_END,
                                 FALSE), // no owner draw
                            FALSE),
                        MPNULL);
                }

                if (pGlobalSettings->AddFolderContentItem)
                {
                    // add "Folder content" only if somSelf is not a favorite folder,
                    // because then we will insert the folder content anyway
                    if (!_xfIsFavoriteFolder(somSelf))
                        // somself is not in favorites list: add "Folder content"
                        mnuPrepareContentSubmenu(somSelf, hwndMenu,
                                pNLSStrings->pszFldrContent,
                                MIT_END,
                                FALSE); // no owner draw in main context menu
                }

                // now add favorite folders
                for (pFavorite = _xfclsQueryFavoriteFolder(_XFolder, NULL);
                     pFavorite;
                     pFavorite = _xfclsQueryFavoriteFolder(_XFolder, pFavorite))
                {
                    mnuPrepareContentSubmenu(pFavorite, hwndMenu,
                            _wpQueryTitle(pFavorite), MIT_END,
                            FALSE); // no owner draw in main context menu
                }
            } // end folder contents

            /*
             * config folders:
             *
             */

            // now, this will start searching for user-defined stuff
            // in the XFolder config folder, which we need to locate first */
            pConfigFolder = _wpclsQueryObjectFromPath(_WPFolder, XFOLDER_CONFIGID);

            if (pConfigFolder != NULL) {
                // config folder found: append another separator first
                // if it contains any objects
                if (_wpQueryContent(pConfigFolder, NULL, QC_FIRST)) {
                    winhInsertMenuSeparator(hwndMenu, MIT_END,
                            (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SEPARATOR));

                    // now insert items in pConfigFolder into main context menu (hwndMenu);
                    // this routine will call itself recursively if it finds subfolders.
                    // Since we have registered an exception handler, if errors occur,
                    // this will lead to a message box only.
                    mnuFillMenuWithObjects(somSelf, pConfigFolder,
                                hwndMenu, hwndCnr);
                }
            } else {
                // config folder not found: give message and create it anew
                xthrPostWorkerMsg(WOM_RECREATECONFIGFOLDER, (MPARAM)RCF_QUERYACTION, MPNULL);
                rc = FALSE;
            }
        } // end if (somSelf)
            else rc = FALSE;
    }

    DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&RegRec2);

    return (rc);
}

/* ******************************************************************
 *                                                                  *
 *   Functions for reacting to menu selections                      *
 *                                                                  *
 ********************************************************************/

/*
 *@@ mnuQuerySelectedObject:
 *      this helper function evaluates a given container
 *      to find out which objects have been selected while
 *      a context menu is open. The WPS gives the items on
 *      which the menu action should be performed upon
 *      container "source" emphasis, so this is what we
 *      evaluate.
 *
 *      This function only works when a context menu is open,
 *      because otherwise WPS cnr items don't have source emphasis.
 *
 *      The result of this evaluation is stored in
 *      *pulSelection, which can be:
 *
 *      --   SEL_WHITESPACE the context menu was opened on the
 *                          whitespace of the container;
 *                          this func then returns the folder.
 *      --   SEL_SINGLESEL  the context menu was opened for a
 *                          single selected object:
 *                          this func then returns that object.
 *      --   SEL_MULTISEL   the context menu was opened on one
 *                          of a multitude of selected objects;
 *                          this func then returns the first of the
 *                          selected objects.
 *      --   SEL_SINGLEOTHER the context menu was opened for a
 *                          single object _other_ than the selected
 *                          objects:
 *                          this func then returns that object.
 *
 *      Note that if this function returns something other than
 *      the folder of the container (SEL_WHITESPACE), the returned
 *      object might be a shadow, which you need to dereference
 *      before working on it.
 */

WPObject* mnuQuerySelectedObject(WPFolder *somSelf,     // in: folder with open menu
                                 HWND hwndCnr,          // in:  cnr
                                 PULONG pulSelection)   // out: selection flags
{
    PMINIRECORDCORE pmrcSource = (PMINIRECORDCORE)
            WinSendMsg(hwndCnr,
                CM_QUERYRECORDEMPHASIS,
                (MPARAM)CMA_FIRST,
                (MPARAM)CRA_SOURCE);
    WPObject   *pObject = NULL;

    if (pmrcSource == NULL) {
        // if CM_QUERYRECORDEMPHASIS returns NULL
        // for source emphasis (CRA_SOUCE),
        // this means the whole container has source
        // emphasis --> context menu on folder whitespace
        pObject = somSelf;   // folder
        *pulSelection = SEL_WHITESPACE;
    }
    else if (((LONG)pmrcSource) != -1) // no error?
    {
        // check if first source object is equal to
        // first selected object, i.e. the menu was
        // opened on one or several selected objects
        PMINIRECORDCORE pmrcSelected = (PMINIRECORDCORE)
            WinSendMsg(hwndCnr,
                CM_QUERYRECORDEMPHASIS,
                (MPARAM)CMA_FIRST,
                (MPARAM)CRA_SELECTED);
        pObject = OBJECT_FROM_PREC(pmrcSource);

        if (pmrcSelected == pmrcSource) {
            // OK, menu on selected object:
            // are several objects selected?
            pmrcSelected = (PMINIRECORDCORE)
                WinSendMsg(hwndCnr,
                    CM_QUERYRECORDEMPHASIS,
                    (MPARAM)pmrcSelected,  // get second obj
                    (MPARAM)CRA_SELECTED);
            if (pmrcSelected)
                // several objects
                *pulSelection = SEL_MULTISEL;
            else
                // only one object
                *pulSelection = SEL_SINGLESEL;
        } else
            // only one object, but not one of
            // the selected ones
            *pulSelection = SEL_SINGLEOTHER;
    }

    // note that we have _not_ dereferenced shadows
    // here, because this will lead to confusion for
    // finding other selected objects in the same
    // folder; dereferencing shadows is therefore
    // the responsibility of the caller
    return (pObject);
}

/*
 *@@ mnuQueryNextSelectedObject:
 *      if mnuQuerySelectedObject above returns SEL_MULTISEL,
 *      you can use this helper func to loop thru all the
 *      selected objects. This will return the next object
 *      after pObject which is selected or NULL if it's the last.
 */

WPObject* mnuQueryNextSelectedObject(HWND hwndCnr, WPObject *pObject)
{
    WPObject *pObject2 = NULL;
    PMINIRECORDCORE pmrcCurrent = _wpQueryCoreRecord(pObject);
    if (pmrcCurrent) {
        PMINIRECORDCORE pmrcNext = (PMINIRECORDCORE)
                       WinSendMsg(hwndCnr,
                           CM_QUERYRECORDEMPHASIS,
                           (MPARAM)pmrcCurrent,
                           (MPARAM)CRA_SELECTED);
        if ((pmrcNext) && ((LONG)pmrcNext != -1) )
            pObject2 = OBJECT_FROM_PREC(pmrcNext);
    }
    return (pObject2);
}

/*
 *@@  mnuProgramObjectSelected:
 *      this subroutine is called by mnuMenuItemSelected whenever a
 *      program object from the config folders is to be handled;
 *      it does all the necessary fuddling with the program object's
 *      data before opening it, ie. changing the working dir to the
 *      folder's, inserting clipboard data and so on.
 */

BOOL mnuProgramObjectSelected(WPObject *somSelf, WPProgram *pProgram)
{
    PPROGDETAILS    pProgDetails;
    ULONG           ulSize;

    WPFolder        *pFolder = NULL;
    CHAR            szRealName[CCHMAXPATH];    // Buffer for wpQueryFilename()

    BOOL            ValidRealName,
                    StartupChanged = FALSE,
                    ParamsChanged = FALSE,
                    TitleChanged = FALSE,
                    brc = TRUE;

    PSZ             pszOldParams = NULL,
                    pszOldTitle = NULL;
    CHAR            szPassRealName[CCHMAXPATH];
    CHAR            szNewParams[1024] = "";
    CHAR            szNewTitle[1024] = "";

    CHAR            szClipBuf[CCHMAXPATH];          // buffer for copying data
    ULONG           Ofs = 0;

    HAB             hab;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();

    // get program object data
    if ((_wpQueryProgDetails(pProgram, (PPROGDETAILS)NULL, &ulSize))) {
        if ((pProgDetails = (PPROGDETAILS)_wpAllocMem(somSelf, ulSize, NULL)) != NULL) {
            if ((_wpQueryProgDetails(pProgram, pProgDetails, &ulSize))) {

                pFolder = somSelf;

                // dereference folder/disk shadows
                /* if (_somIsA(pFolder, _WPFolder))
                    pFolder = somSelf; */

                // dereference disk objects
                if (_somIsA(pFolder, _WPDisk))
                    pFolder = xwpsQueryRootFolder(somSelf, NULL);

                if (pFolder)
                    ValidRealName = (_wpQueryFilename(pFolder, szRealName, TRUE) != NULL);
                // now we have the folder's full path

                // there seems to be a bug in wpQueryFilename for
                // root folders, so we might need to append a "\" */
                if (strlen(szRealName) == 2)
                    strcat(szRealName, "\\");

                // *** first trick:
                // if the program object's startup dir has not been
                // set, we will set it to szRealName
                // temporarily; this will start the
                // program object in the directory
                // of the folder whose context menu was selected */
                if (ValidRealName && (pProgDetails->pszStartupDir == NULL)) {
                    StartupChanged = TRUE;
                    pProgDetails->pszStartupDir = szRealName;
                }

                // start playing with the object's parameter list,
                // if the global settings allow it */
                if (pGlobalSettings->AppdParam) {
                    // if the folder's real name contains spaces,
                    // we need to enclose it in quotes */
                    if (strchr(szRealName, ' ')) {
                        strcpy(szPassRealName, "\"");
                        strcat(szPassRealName, szRealName);
                        strcat(szPassRealName, "\"");
                    } else
                        strcpy(szPassRealName, szRealName);

                    // backup prog data for later restore
                    pszOldParams = pProgDetails->pszParameters;

                    if (pszOldParams) { // parameter list not empty
                        // *** second trick:
                        // we will append the current folder path to the parameters
                        // if the program object's parameter list does not
                        // end in "%" ("Netscape support") */
                        if (ValidRealName && (pProgDetails->pszParameters[strlen(pProgDetails->pszParameters)-1] != '%')) {
                            ParamsChanged = TRUE;

                            strcpy(szNewParams, pszOldParams);
                            strcat(szNewParams, " ");
                            strcat(szNewParams, szPassRealName);
                        }

                        // *** third trick:
                        // replace an existing "%**C" in the parameters
                        // with the contents of the clipboard */
                        if (strstr(pszOldParams, CLIPBOARDKEY)) {

                            hab = WinQueryAnchorBlock(HWND_DESKTOP);
                            if (WinOpenClipbrd(hab)) {
                                PSZ pszClipText;
                                if (pszClipText = (PSZ)WinQueryClipbrdData(hab, CF_TEXT))
                                {
                                    PSZ pszPos = NULL;
                                    // Copy text from the shared memory object to local memory.
                                    strncpy(szClipBuf, pszClipText, CCHMAXPATH);
                                    szClipBuf[CCHMAXPATH-2] = '\0'; // make sure the string is terminated
                                    WinCloseClipbrd(hab);

                                    if (ParamsChanged == FALSE) // did we copy already?
                                        strcpy(szNewParams, pszOldParams);
                                    pszPos = strstr(szNewParams, CLIPBOARDKEY);
                                    Ofs = strlen(szClipBuf);
                                    if (Ofs + strlen(szNewParams) > CCHMAXPATH)
                                        Ofs -= strlen(szNewParams);
                                    strcpy(szClipBuf+Ofs, pszPos+strlen(CLIPBOARDKEY));
                                    strcpy(pszPos, szClipBuf);

                                    ParamsChanged = TRUE;
                                }
                                else { // no text data in clipboard
                                    WinCloseClipbrd(hab);
                                    cmnSetHelpPanel(ID_XFH_NOTEXTCLIP);
                                    if (WinDlgBox(HWND_DESKTOP,         // parent is desktop
                                          HWND_DESKTOP,             // owner is desktop
                                          (PFNWP)fnwpDlgGeneric,    // dialog procedure, defd. at bottom
                                          NLS_MODULE,  // from resource file
                                          ID_XFD_NOTEXTCLIP,        // dialog resource id
                                          (PVOID)NULL)             // no dialog parameters
                                       == DID_CANCEL)
                                          brc = FALSE;
                                }
                            }
                            else
                                DebugBox("XFolder", "The clipboard could not be opened.");
                        }
                        if (ParamsChanged)
                            pProgDetails->pszParameters = szNewParams;

                    } else
                        // parameter list is empty: simply set params
                        if (ValidRealName)
                        {
                            ParamsChanged = TRUE;
                            // set parameter list to folder name
                            pProgDetails->pszParameters = szPassRealName;
                            // since parameter list is empty, we need not
                            // search for the clipboard key ("%**C") */
                        }
                } // end if (pGlobalSettings->AppdParam)

                // now remove "~" from title, if allowed
                pszOldTitle = pProgDetails->pszTitle;
                if ((pszOldTitle) && (pGlobalSettings->RemoveX)) {
                    PSZ pszPos = strchr(pszOldTitle, '~');
                    if (pszPos)  {
                        TitleChanged = TRUE;
                        strncpy(szNewTitle, pszOldTitle, (pszPos-pszOldTitle));
                        strcat(szNewTitle, pszPos+1);
                        pProgDetails->pszTitle = szNewTitle;
                    }
                }

                // now apply new settings, if necessary
                if (StartupChanged || ParamsChanged || TitleChanged) {
                    if (!_wpSetProgDetails(pProgram, pProgDetails)) {
                        DebugBox("XFolder", "Unable to set new startup directory.");
                        brc = FALSE;
                    }
                }

                if (brc)
                    // open the object with new settings
                    _wpViewObject(pProgram, NULLHANDLE, OPEN_DEFAULT, 0);

                // now restore the old settings, if necessary
                if (StartupChanged)
                    pProgDetails->pszStartupDir = NULL;
                if (ParamsChanged)
                    pProgDetails->pszParameters = pszOldParams;
                if (TitleChanged)
                    pProgDetails->pszTitle = pszOldTitle;
                if (StartupChanged || ParamsChanged || TitleChanged)
                    _wpSetProgDetails(pProgram, pProgDetails);

            } else
                brc = FALSE;

            _wpFreeMem(somSelf, (PBYTE)pProgDetails);
        } else
            brc = FALSE;
    } else
        brc = FALSE;

    return (brc);
}

/*
 *@@ mnuIsSortMenuItemSelected:
 *      this is used by both mnuMenuItemSelected and
 *      fnwpSubclassedFolderFrame for checking if the selected
 *      menu item is one of the folder things and, if so,
 *      setting the folder sort settings accordingly.
 *      We need to have a separate proc for this because
 *      fnwpSubclassedFolderFrame needs this if the user uses
 *      the mouse and mnuMenuItemSelected gets the folder
 *      hotkeys.
 *
 *      This returns TRUE if the menu item was processed
 *      and then sets *pbDismiss to whether the menu should
 *      be dismissed. If pbDismiss is passed as NULL, this
 *      routine assumes that we're dealing with hotkeys
 *      instead of menu items.
 */

BOOL mnuIsSortMenuItemSelected(XFolder* somSelf,
                        HWND hwndFrame,
                        HWND hwndMenu,      // may be NULLHANDLE if
                                            // pbDismiss is NULL also
                        ULONG ulMenuId,
                        PGLOBALSETTINGS pGlobalSettings,
                        PBOOL pbDismiss)    // out: dismiss flag for fnwpSubclassedFolderFrame
{
    BOOL        brc = FALSE;
    ULONG       ulMenuId2 = ulMenuId - pGlobalSettings->VarMenuOffset;
    USHORT      usAlwaysSort, usDefaultSort;

    switch (ulMenuId2) {
        // new sort items
        case ID_XFMI_OFS_SORTBYCLASS:
        case ID_XFMI_OFS_SORTBYEXT:
        case ID_XFMI_OFS_SORTFOLDERSFIRST:
        {
            USHORT usSort;
            BOOL   fShiftPressed = doshQueryShiftState();
            switch (ulMenuId2) {
                case ID_XFMI_OFS_SORTBYCLASS:       usSort = SV_CLASS;        break;
                case ID_XFMI_OFS_SORTFOLDERSFIRST:  usSort = SV_FOLDERSFIRST; break;
                case ID_XFMI_OFS_SORTBYEXT:         usSort = SV_EXT;          break;
            }
            if ((fShiftPressed) && (pbDismiss))
            {
                _xfQueryFldrSort(somSelf, &usDefaultSort, &usAlwaysSort);
                _xfSetFldrSort(somSelf,   usSort,  usAlwaysSort);
                mnuCheckDefaultSortItem(pGlobalSettings,
                                        hwndMenu,
                                        usSort);
            } else
                _xfSortViewOnce(somSelf,
                        hwndFrame,
                        usSort);

            // do not dismiss menu when shift was pressed
            if (pbDismiss)
                *pbDismiss = (!fShiftPressed);
            brc = TRUE;
        break; }

        // "Always sort"
        case ID_XFMI_OFS_ALWAYSSORT: {
            XFolderData         *somThis = XFolderGetData(somSelf);
            BOOL                fAlwaysSort = ALWAYS_SORT;
            _xfQueryFldrSort(somSelf, &usDefaultSort, &usAlwaysSort);
            _xfSetFldrSort(somSelf, usDefaultSort,
                                !fAlwaysSort);

            winhSetMenuItemChecked(hwndMenu,
                        ulMenuId,
                        !fAlwaysSort);

            // do not dismiss menu
            if (pbDismiss)
                *pbDismiss = FALSE;

            brc = TRUE;
        break; }

        default:
            switch (ulMenuId) { // check original menu id

                // one of the original WPS "sort" menu items:
                case ID_WPMI_SORTBYNAME:
                case ID_WPMI_SORTBYTYPE:
                case ID_WPMI_SORTBYREALNAME:
                case ID_WPMI_SORTBYSIZE:
                case ID_WPMI_SORTBYWRITEDATE:
                case ID_WPMI_SORTBYACCESSDATE:
                case ID_WPMI_SORTBYCREATIONDATE:
                    if (pGlobalSettings->ReplaceSort) { // extended sorting?
                        BOOL   fShiftPressed = doshQueryShiftState();
                        USHORT usSort;
                        switch (ulMenuId) {
                            case ID_WPMI_SORTBYNAME:            usSort = SV_NAME;         break;
                            case ID_WPMI_SORTBYTYPE:            usSort = SV_TYPE;         break;
                            case ID_WPMI_SORTBYREALNAME:        usSort = SV_REALNAME;     break;
                            case ID_WPMI_SORTBYSIZE:            usSort = SV_SIZE;         break;
                            case ID_WPMI_SORTBYWRITEDATE:       usSort = SV_LASTWRITEDATE;    break;
                            case ID_WPMI_SORTBYACCESSDATE:      usSort = SV_LASTACCESSDATE;   break;
                            case ID_WPMI_SORTBYCREATIONDATE:    usSort = SV_CREATIONDATE; break;
                        }
                        if ((fShiftPressed) && (pbDismiss))
                        {
                            // SHIFT pressed when menu item was selected:
                            // set default sort criterion
                            _xfQueryFldrSort(somSelf, &usDefaultSort, &usAlwaysSort);
                            _xfSetFldrSort(somSelf,   usSort,         usAlwaysSort);
                            mnuCheckDefaultSortItem(pGlobalSettings,
                                                    hwndMenu,
                                                    usSort);
                        } else {
                            // not SHIFT pressed: simply sort
                            _xfSortViewOnce(somSelf,
                                    hwndFrame,
                                    usSort);
                        }

                        // do not dismiss menu if shift was pressed
                        if (pbDismiss)
                            *pbDismiss = (!fShiftPressed);
                        // found flag
                        brc = TRUE;
                    }
                break;
            }
    }
    return (brc);
}

/*
 *@@ mnuMenuItemSelected:
 *      this gets called by XFolder::wpMenuItemSelected and
 *      XFldDisk::wpMenuItemSelected. Since both classes have
 *      most menu items in common, the handling of the
 *      selections can be done for both in one routine.
 *
 *      This routine now checks if one of XFolder's menu items
 *      was selected; if so, executes corresponding action
 *      and returns TRUE, otherwise does nothing and
 *      returns FALSE, upon which the caller should
 *      call its parent method to process the menu item.
 *
 *      Note that when called from XFldDisk, somSelf points
 *      to the "root folder" of the disk object instead of
 *      the disk object itself (wpQueryRootFolder).
 */

BOOL mnuMenuItemSelected(XFolder *somSelf,  // in: folder or root folder
                        HWND hwndFrame,     // in: as in wpMenuItemSelected
                        ULONG ulMenuId)     // in: selected menu item
{
    WPObject            *pObject = NULL;
    ULONG               ulFirstVarMenuId;
    PVARMENULISTITEM    pItem;
    PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();
    ULONG               ulMenuId2 = ulMenuId - pGlobalSettings->VarMenuOffset;
    BOOL                brc = FALSE;     // "not processed" flag

    if (somSelf) {
        BOOL        fDummy;

        /*
         *  "Sort" menu items:
         *
         */

        if (mnuIsSortMenuItemSelected(somSelf,
                        hwndFrame,
                        NULLHANDLE,     // we don't know the menu hwnd
                        ulMenuId,
                        pGlobalSettings, &fDummy))
            brc = TRUE;

        // else: no sort menu item
        else switch (ulMenuId2)
        {
            /*
             * ID_XFMI_OFS_PRODINFO:
             *      "Product Information"
             */

            case ID_XFMI_OFS_PRODINFO:
            {
                // advertise for myself
                CHAR szGPLInfo[2000];
                HWND hwndInfo = WinLoadDlg(HWND_DESKTOP, HWND_DESKTOP,
                                  fnwpDlgGeneric,
                                  NLS_MODULE,
                                  ID_XFD_PRODINFO,
                                  NULL);
                xthrPlaySystemSound(MMSOUND_SYSTEMSTARTUP);

                // load GPL info message into prodinfo MLE
                cmnGetMessage(NULL, 0,
                        szGPLInfo, sizeof(szGPLInfo),
                        140);
                WinSetDlgItemText(hwndInfo, ID_XSDI_SC_TEXT2, szGPLInfo);
                WinSendDlgItemMsg(hwndInfo, ID_XSDI_SC_TEXT2,
                        MLM_SETFIRSTCHAR,       // scroll MLE to top
                        (MPARAM)0,
                        MPNULL);

                cmnSetHelpPanel(0);
                winhCenterWindow(hwndInfo);
                WinProcessDlg(hwndInfo);
                WinDestroyWindow(hwndInfo);
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_SELECTSOME:
             *      "Select by name"
             */

            case ID_XFMI_OFS_SELECTSOME:
            {
                HWND hwndSelectSome = WinLoadDlg(HWND_DESKTOP,    // parent
                        hwndFrame,         // owner
                        (PFNWP)fnwpSelectSome,
                        NLS_MODULE,
                        ID_XFD_SELECTSOME,
                        (PVOID)hwndFrame);    // dlg params
                cmnSetHelpPanel(ID_XFH_SELECTSOME);
                WinShowWindow(hwndSelectSome, TRUE);
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_COPYFILENAME_SHORT:
             * ID_XFMI_OFS_COPYFILENAME_FULL:
             *      these are no real menu items, but only
             *      pseudo-commands posted by the corresponding
             *      folder hotkeys
             */

            case ID_XFMI_OFS_COPYFILENAME_SHORT:
            case ID_XFMI_OFS_COPYFILENAME_FULL: {
                // if the user presses hotkeys for "copy filename",
                // he doesn't want the filename of the folder
                // (which somSelf points to here...), but of the
                // selected objects, so we repost the msg to
                // the first selected object, which will handle
                // the rest
                HWND hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);
                if (hwndCnr) {
                    PMINIRECORDCORE pmrc = WinSendMsg(hwndCnr,
                                CM_QUERYRECORDEMPHASIS,
                                (MPARAM)CMA_FIRST, // query first
                                (MPARAM)CRA_SELECTED);
                    if ((pmrc != NULL) && ((ULONG)pmrc != -1)) {
                        WPObject *pObject = OBJECT_FROM_PREC(pmrc);
                        if (pObject) {
                            xwpsCopyObjectFileName(pObject, hwndFrame,
                                (ulMenuId2 == ID_XFMI_OFS_COPYFILENAME_FULL));
                        }
                    }
                }
            break; }

            /*
             * ID_XFMI_OFS_SNAPTOGRID:
             *      "Snap to grid"
             */

            case ID_XFMI_OFS_SNAPTOGRID: {
                _xfSnapToGrid(somSelf, TRUE);
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_OPENPARENT:
             *      "Open parent folder":
             *      only used by folder hotkeys also
             */

            case ID_XFMI_OFS_OPENPARENT: {
                _wpViewObject(_wpQueryFolder(somSelf), NULLHANDLE, OPEN_DEFAULT, 0);
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_OPENPARENTANDCLOSE:
             *      "open parent, close current"
             *      only used by folder hotkeys also
             */

            case ID_XFMI_OFS_OPENPARENTANDCLOSE: {
                _wpViewObject(_wpQueryFolder(somSelf), NULLHANDLE, OPEN_DEFAULT, 0);
                _wpClose(somSelf);
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_CONTEXTMENU:
             *      "Show context menu":
             *      only used by folder hotkeys also
             */

            case ID_XFMI_OFS_CONTEXTMENU: {
                HWND hwndCnr = xwpsQueryCnrFromFrame(hwndFrame);
                POINTS pts = {0, 0};
                WinPostMsg(hwndCnr, WM_CONTEXTMENU,
                        (MPARAM)&pts,
                        MPFROM2SHORT(0, TRUE));
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_REFRESH:
             *      "Refresh now":
             *      translate and pass to original method
             */

            case ID_XFMI_OFS_REFRESH: {
                _wpMenuItemSelected(somSelf, hwndFrame, WPMENUID_REFRESH);
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_CLOSE:
             *      repost sys command
             */

            case ID_XFMI_OFS_CLOSE: {
                WinPostMsg(hwndFrame, WM_SYSCOMMAND, (MPARAM)SC_CLOSE,
                        MPFROM2SHORT(CMDSRC_MENU, FALSE));
                brc = TRUE;
            break; }

            /*
             * ID_XFMI_OFS_BORED:
             *      "[Config folder empty]"
             */

            case ID_XFMI_OFS_BORED: {
                // explain how to configure XFolder
                cmnMessageBoxMsg(HWND_DESKTOP, 116, 135, MB_OK);
                brc = TRUE;
            break; }

            /*
             * default:
             *      check for variable menu items
             *      (ie. from config folder or folder
             *      content menus)
             */

            default: {
                // anything else: check if it's one of our variable menu items
                ulFirstVarMenuId = pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_VARIABLE;
                if ( (ulMenuId >= ulFirstVarMenuId)
                      && (ulMenuId <  ulFirstVarMenuId + ulVarItemCount)
                   )
                {
                    // yes, variable menu item selected:
                    // get corresponding menu list item from the list that
                    // was created by mnuModifyPopupMenu
                    pItem = (PVARMENULISTITEM)lstItemFromIndex(
                                (PLISTITEM)pliVarItems, (ulMenuId - ulFirstVarMenuId));

                    if (pItem)
                        pObject = pItem->pObject;

                    if (pObject) { // defaults to NULL
                        // OK, we've found the corresponding object
                        switch (pItem->usObjType) {
                            // this data has previously been saved by mnuFillMenuWithObjects when
                            // the context menu was created; it contains a flag telling us
                            // what kind of menu item we're dealing with

                            case OC_TEMPLATE: {
                                // if the object is a template, we create a new object from it
                                // WPObject        *pNewObject;
                                // ULONG           ulCreateFlags = 0;
                                XFolderData     *somThis = XFolderGetData(somSelf);
                                POINTL          ptlMouse;

                                ptlMouse.x = _MenuMousePosX;
                                ptlMouse.y = _MenuMousePosY;

                                xwpsCreateFromTemplate(pObject,  // template
                                        somSelf,                // folder
                                        hwndFrame,
                                        pGlobalSettings->TemplatesOpenSettings,
                                        &ptlMouse);

                            break; } // end OC_TEMPLATE

                            case OC_PROGRAM: {
                                // WPPrograms are handled separately, for we will perform
                                // tricks on the startup directory and parameters */
                                mnuProgramObjectSelected(somSelf, pObject);
                            break; } // end OC_PROGRAM

                            default:
                                // objects other than WPProgram and WPFolder (which is handled by
                                // the OS/2 menu handling) will simply be opened without further
                                // discussion.
                                // This includes folder content menu items,
                                // which are marked as OC_CONTENT; MB2 clicks into
                                // content menus are handled by the subclassed folder wnd proc
                                _wpViewObject(pObject, NULLHANDLE, OPEN_DEFAULT, 0);
                            break;
                        } // end switch
                    } //end else (pObject == NULL)
                    brc = TRUE;
                } // end if ((ulMenuId >= ID_XFM_VARIABLE) && (ulMenuId < ID_XFM_VARIABLE+varItemCount))
                else { // none of our variable menu items:
                    brc = FALSE;  // "not processed" flag
                }
            } // end default;
        } // end switch;
    } // end if (somSelf)

    return (brc);
    // this flag is FALSE by default; it signals to the caller (which
    // is wpMenuItemSelected of either XFolder or XFldDisk) whether the
    // parent method still needs to be called. If TRUE, we have processed
    // something, if FALSE, we havent, then call the parent
}

/*
 *@@ mnuMenuItemHelpSelected:
 *           display help for a context menu item; this routine is
 *           shared by all XFolder classes also, so you'll find
 *           XFldDesktop items in here too
 */

BOOL mnuMenuItemHelpSelected(WPObject *somSelf, ULONG MenuId)
{
    ULONG   ulFirstVarMenuId;
    ULONG   ulPanel = 0;
    PGLOBALSETTINGS pGlobalSettings = cmnQueryGlobalSettings();
    if ((MenuId == WPMENUID_SHUTDOWN) && (pGlobalSettings->ulXShutdownFlags & XSD_ENABLED))
        ulPanel = ID_XMH_XSHUTDOWN;
    else if (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_RESTARTWPS)
        ulPanel = ID_XMH_RESTARTWPS;
    else if (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SNAPTOGRID)
        ulPanel = ID_XMH_SNAPTOGRID;
    else  if (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_REFRESH)
        ulPanel = ID_XMH_REFRESH;
    else  if (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SELECTSOME)
        ulPanel = ID_XFH_SELECTSOME;
    else  if (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_COPYFILENAME_MENU)
        ulPanel = ID_XMH_COPYFILENAME;
    else if (   (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTBYEXT)
             || (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTBYCLASS)
             || (MenuId == pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_SORTFOLDERSFIRST)
             || (   (pGlobalSettings->ReplaceSort)
                 && (   (MenuId == ID_WPMI_SORTBYNAME)
                     || (MenuId == ID_WPMI_SORTBYREALNAME)
                     || (MenuId == ID_WPMI_SORTBYTYPE)
                     || (MenuId == ID_WPMI_SORTBYSIZE)
                     || (MenuId == ID_WPMI_SORTBYWRITEDATE)
                     || (MenuId == ID_WPMI_SORTBYACCESSDATE)
                     || (MenuId == ID_WPMI_SORTBYCREATIONDATE)
                    )
                )
            )
        ulPanel = ID_XSH_SETTINGS_FLDRSORT;
    else {
        // if F1 was pressed over one of the variable menu items,
        // open a help panel with generic help on XFolder */
        ulFirstVarMenuId = (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_VARIABLE);
        if ( (MenuId >= ulFirstVarMenuId)
                && (MenuId < ulFirstVarMenuId + ulVarItemCount)
             )
        {
            PVARMENULISTITEM pItem = (PVARMENULISTITEM)lstItemFromIndex(
                        (PLISTITEM)pliVarItems, (MenuId - ulFirstVarMenuId));

            if (pItem) {
                // OK, we've found the corresponding object
                switch (pItem->usObjType) {
                    // this data has previously been saved by mnuFillMenuWithObjects when
                    // the context menu was created; it contains a flag telling us
                    // what kind of menu item we're dealing with

                    case OC_CONTENTFOLDER:
                    case OC_CONTENT:
                        ulPanel = ID_XMH_FOLDERCONTENT;
                    break;

                    default:
                        ulPanel = ID_XMH_VARIABLE;
                    break;
                }
            }
        }
    }

    if (ulPanel)
    {
        PSZ pszHelpLibrary;
        BOOL fProcessed = FALSE;
        // now open the help panel we've set above
        if (pszHelpLibrary = cmnQueryHelpLibrary())
            // path found: display help panel
            if (_wpDisplayHelp(somSelf, ulPanel, pszHelpLibrary))
                fProcessed = TRUE;

        if (!fProcessed)
            cmnMessageBoxMsg(HWND_DESKTOP, 104, 134, MB_OK);

        return TRUE;
    } else { // none of our items: pass on to parent
        return FALSE;
    }
}

/* ******************************************************************
 *                                                                  *
 *   "Selecting" menu items functions                               *
 *                                                                  *
 ********************************************************************/

/*
 *  The following functions are called on objects even before
 *  wpMenuItemSelected is called, i.e. right after a
 *  menu item has been selected by the user, and before
 *  the menu is dismissed.
 */

/*
 *@@ mnuFileSystemSelectingMenuItem:
 *      this is called for file-system objects (folders and
 *      data files) even before wpMenuItemSelected.
 *
 *      This call is the result of a WM_MENUSELECT intercept
 *      of the subclassed frame window procedure of an open folder
 *      (fnwpSubclassedFolderFrame, xfldr.c).
 *
 *      We can intercept certain menu item selections here so
 *      that they are not passed to wpMenuItemSelected. This is
 *      the only way we can react to a menu selection and _not_
 *      dismiss the menu (ie. keep it visible after the selection).
 *
 *      Return value:
 *      -- TRUE          the menu item was handled here; in this case,
 *                       we set *pfDismiss to either TRUE or FALSE.
 *                       If TRUE, the menu will be dismissed and, if
 *                       if (fPostCommand), wpMenuItemSelected will be
 *                       called later.
 *      -- FALSE         the menu item was _not_ handled.
 *                       We do _not_ touch *pfDismiss then.
 *
 *      Note that somSelf might be a file-system object, but
 *      it might also be a shadow pointing to one, so we might
 *      need to dereference it.
 *
 */

BOOL mnuFileSystemSelectingMenuItem(WPObject *somSelf,
                                    // in: file-system object on which the menu was opened
                                 USHORT usItem,
                                    // in: selected menu item
                                 BOOL fPostCommand,
                                    // in: this signals whether wpMenuItemSelected can be
                                    // called afterwards
                                 HWND hwndMenu,
                                    // in: current menu control
                                 HWND hwndCnr,
                                    // in: cnr hwnd involved in the operation
                                 ULONG ulSelection,
                                    // one of the following:
                                    // -- SEL_WHITESPACE the context menu was opened on the
                                    //                   whitespace in an open container view
                                    //                   of somSelf (which is a folder then)
                                    // -- SEL_SINGLESEL  the context menu was opened for a
                                    //                   single selected object: somSelf can
                                    //                   be any object then, including folders
                                    // -- SEL_MULTISEL   the context menu was opened on one
                                    //                   of a multitude of selected objects.
                                    //                   Again, somSelf can be any object
                                    // -- SEL_SINGLEOTHER the context menu was opened for a
                                    //                   single object _other_ than the selected
                                    //                   objects
                                 BOOL *pfDismiss)
                                    // out: if TRUE is returned (ie. the menu item was handled
                                    // here), this determines whether the menu should be dismissed
{
    PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();
    ULONG               ulMenuId2 = usItem - pGlobalSettings->VarMenuOffset;
    BOOL                fHandled = TRUE;
    // BOOL                brc = TRUE;     // "dismiss menu" flag

    WPObject *pObject = somSelf;
    WPFileSystem *pFileSystem = pObject;

    if (_somIsA(pObject, _WPShadow))
        pFileSystem = _wpQueryShadowedObject(pObject, TRUE);

    #ifdef DEBUG_MENUS
        _Pmpf(("mnuFileSystemSelectingMenuItem"));
    #endif

    switch (ulMenuId2) {
        case ID_XFMI_OFS_ATTR_ARCHIVED:
        case ID_XFMI_OFS_ATTR_SYSTEM:
        case ID_XFMI_OFS_ATTR_HIDDEN:
        case ID_XFMI_OFS_ATTR_READONLY: {
            ULONG       ulFileAttr;
            ULONG       ulMenuAttr;
            HPOINTER    hptrOld;

            ulFileAttr = _wpQueryAttr(pFileSystem);
            ulMenuAttr = (ULONG)WinSendMsg(hwndMenu, MM_QUERYITEMATTR,
                                        MPFROM2SHORT(usItem, FALSE),
                                        (MPARAM)MIA_CHECKED);

            // toggle "checked" flag in menu
            ulMenuAttr ^= MIA_CHECKED;  // XOR checked flag;
            WinSendMsg(hwndMenu, MM_SETITEMATTR,
                                        MPFROM2SHORT(usItem, FALSE),
                                        MPFROM2SHORT(MIA_CHECKED, ulMenuAttr));

            // toggle file attribute
            ulFileAttr ^= // XOR flag depending on menu item
                      (ulMenuId2 == ID_XFMI_OFS_ATTR_ARCHIVED) ? FILE_ARCHIVED
                    : (ulMenuId2 == ID_XFMI_OFS_ATTR_SYSTEM  ) ? FILE_SYSTEM
                    : (ulMenuId2 == ID_XFMI_OFS_ATTR_HIDDEN  ) ? FILE_HIDDEN
                    : FILE_READONLY;

            // loop thru the selected objects
            // change the mouse pointer to "wait" state
            hptrOld = WinQueryPointer(HWND_DESKTOP);
            WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP, SPTR_WAIT, FALSE));
            while (pObject)
            {
                if (pFileSystem)
                {
                    #ifdef DEBUG_MENUS
                        _Pmpf(("  Settings attrs for %s", _wpQueryTitle(pFileSystem)));
                    #endif

                    _wpSetAttr(pFileSystem, ulFileAttr);
                }

                if (ulSelection == SEL_MULTISEL)
                    pObject = mnuQueryNextSelectedObject(hwndCnr, pObject);
                        // note that we're passing pObject, which might
                        // be the shadow
                else
                    pObject = NULL;

                pFileSystem = pObject;
                if (pObject)
                    if (_somIsA(pObject, _WPShadow))
                        pFileSystem = _wpQueryShadowedObject(pObject, TRUE);
            }

            WinSetPointer(HWND_DESKTOP, hptrOld);

            // prevent dismissal of menu
            *pfDismiss = FALSE;
        break; }

        case ID_XFMI_OFS_COPYFILENAME_MENU: {
            xwpsCopyObjectFileName(pObject, hwndCnr, doshQueryShiftState());
                // note again that we're passing pObject instead
                // of pFileSystem, so that this routine can
                // query all selected objects from shadows too

            // dismiss menu
            *pfDismiss = TRUE;
        break; }

        default:
            fHandled = FALSE;
    }

    return (fHandled);
}

/*
 *@@ mnuFolderSelectingMenuItem:
 *      this is called for folders before wpMenuItemSelected.
 *      See mnuFileSystemSelectingMenuItem for details.
 *
 *      Note that somSelf here will never be a shadow pointing
 *      to a folder. It will always be a folder.
 */

BOOL mnuFolderSelectingMenuItem(WPFolder *somSelf,
                                 USHORT usItem,
                                 BOOL fPostCommand,
                                 HWND hwndMenu,
                                 HWND hwndCnr,
                                 ULONG ulSelection,
                                 BOOL *pfDismiss)
{
    // XFolderData *somThis = XFolderGetData(somSelf);

    PGLOBALSETTINGS     pGlobalSettings = cmnQueryGlobalSettings();
    ULONG               ulMenuId2 = usItem - pGlobalSettings->VarMenuOffset;
    BOOL                fHandled = TRUE;
    // BOOL                brc = TRUE;     // "dismiss menu" flag
    USHORT              usAlwaysSort, usDefaultSort;

    #ifdef DEBUG_MENUS
        _Pmpf(("mnuFolderSelectingMenuItem"));
    #endif

    // first check if it's one of the "Sort" menu items
    fHandled = mnuIsSortMenuItemSelected(somSelf,
                    WinQueryWindow(hwndCnr, QW_PARENT), // frame window
                    hwndMenu,
                    usItem,
                    pGlobalSettings,
                    pfDismiss);              // dismiss flag == return value

    if (!fHandled)
    {
        fHandled = TRUE;

        // no "sort" menu item:
        switch (ulMenuId2)
        {
            case ID_XFMI_OFS_SMALLICONS: {
                // toggle small icons for folder; this menu item
                // only exists for open Icon and Tree views
                CNRINFO CnrInfo;
                ULONG ulViewAttr, ulCnrView;
                ULONG ulMenuAttr = (ULONG)WinSendMsg(hwndMenu, MM_QUERYITEMATTR,
                                            MPFROM2SHORT(usItem, FALSE),
                                            (MPARAM)MIA_CHECKED);
                // toggle "checked" flag in menu
                ulMenuAttr ^= MIA_CHECKED;  // XOR checked flag;
                WinSendMsg(hwndMenu, MM_SETITEMATTR,
                                            MPFROM2SHORT(usItem, FALSE),
                                            MPFROM2SHORT(MIA_CHECKED, ulMenuAttr));

                // toggle cnr flags
                winhQueryCnrInfo(hwndCnr, CnrInfo);
                ulCnrView = (CnrInfo.flWindowAttr & CV_TREE) ? OPEN_TREE : OPEN_CONTENTS;
                ulViewAttr = _wpQueryFldrAttr(somSelf, ulCnrView);
                ulViewAttr ^= CV_MINI;      // XOR mini-icons flag
                _wpSetFldrAttr(somSelf,
                        ulViewAttr,
                        ulCnrView);

                *pfDismiss = FALSE;
            break; }

            case ID_XFMI_OFS_FLOWED:
            case ID_XFMI_OFS_NONFLOWED:
            case ID_XFMI_OFS_NOGRID: {
                // these items exist for icon views only
                ULONG ulViewAttr = _wpQueryFldrAttr(somSelf, OPEN_CONTENTS);
                switch (ulMenuId2) {
                    case ID_XFMI_OFS_FLOWED:
                        // == CV_NAME | CV_FLOW; not CV_ICON
                        ulViewAttr = (ulViewAttr & ~CV_ICON) | CV_NAME | CV_FLOW;
                    break;

                    case ID_XFMI_OFS_NONFLOWED:
                        // == CV_NAME only; not CV_ICON
                        ulViewAttr = (ulViewAttr & ~(CV_ICON | CV_FLOW)) | CV_NAME;
                    break;

                    case ID_XFMI_OFS_NOGRID:
                        ulViewAttr = (ulViewAttr & ~(CV_NAME | CV_FLOW)) | CV_ICON;
                    break;
                }
                _wpSetFldrAttr(somSelf,
                        ulViewAttr,
                        OPEN_CONTENTS);

                winhSetMenuItemChecked(hwndMenu,
                            pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_FLOWED,
                            (ulMenuId2 == ID_XFMI_OFS_FLOWED));
                winhSetMenuItemChecked(hwndMenu,
                            pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_NONFLOWED,
                            (ulMenuId2 == ID_XFMI_OFS_NONFLOWED));
                winhSetMenuItemChecked(hwndMenu,
                            pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_NOGRID,
                            (ulMenuId2 == ID_XFMI_OFS_NOGRID));

                // do not dismiss menu
                *pfDismiss = FALSE;
            break; }

            case ID_XFMI_OFS_SHOWSTATUSBAR: {
                // toggle status bar for folder
                ULONG ulMenuAttr = (ULONG)WinSendMsg(hwndMenu, MM_QUERYITEMATTR,
                                            MPFROM2SHORT(usItem, FALSE),
                                            (MPARAM)MIA_CHECKED);
                _xfSetStatusBarVisibility(somSelf,
                            (ulMenuAttr & MIA_CHECKED)
                                ? STATUSBAR_OFF
                                : STATUSBAR_ON,
                            TRUE);  // update open folder views

                // toggle "checked" flag in menu
                ulMenuAttr ^= MIA_CHECKED;  // XOR checked flag;
                WinSendMsg(hwndMenu, MM_SETITEMATTR,
                                            MPFROM2SHORT(usItem, FALSE),
                                            MPFROM2SHORT(MIA_CHECKED, ulMenuAttr));

                // do not dismiss menu
                *pfDismiss = FALSE;
            break; }

            default:
                fHandled = FALSE;
        }
    }

    return (fHandled);
}

/* ******************************************************************
 *                                                                  *
 *   Functions for folder content menu ownerdraw                    *
 *                                                                  *
 ********************************************************************/

#define CX_ARROW 21

ULONG   ulMiniIconSize = 0;
RECTL   rtlMenuItem;
LONG    lHiliteBackground, lBackground, lHiliteText, lText;
PSZ     pszFontName = NULL;

ULONG           *pulMenuFunc2;

/*
 *@@ mnuPrepareOwnerDraw:
 *      this is called from the subclassed folder frame procedure
 *      (fnwpSubclassedFolderFrame in xfldr.c) when it receives
 *      WM_INITMENU for a folder content submenu. We can now
 *      do a few queries to get important data which we need for
 *      owner-drawing later.
 */

VOID mnuPrepareOwnerDraw(MPARAM mp1, // from WM_INITMENU: SHORT mp1 submenu id
                         MPARAM mp2) // from WM_INITMENU: HWND  mp2 menu window handle
{
    ULONG abValue[24];
    ULONG attrFound;

    PTHREADGLOBALS  pThreadGlobals = xthrQueryGlobals();
    pulMenuFunc2 = &(pThreadGlobals->ulMenuFunc2);

    // query bounding rectangle of "[empty]" item, according to
    // which we will format our own items
    WinSendMsg((HWND)mp2, MM_QUERYITEMRECT,
        MPFROM2SHORT(WinSendMsg((HWND)mp2, MM_ITEMIDFROMPOSITION, (MPARAM)0, MPNULL),
                FALSE),
        &rtlMenuItem);

    // query presentation parameters (colors and font) for menu
    *pulMenuFunc2 = 220;
    lBackground = winhQueryPresColor((HWND)mp2,
            PP_MENUBACKGROUNDCOLOR, "Menu", "130 130 130");
    *pulMenuFunc2 = 230;
    lHiliteBackground = winhQueryPresColor((HWND)mp2,
            PP_MENUHILITEBGNDCOLOR, "MenuHilite", "0 0 128");
    *pulMenuFunc2 = 240;
    lText = winhQueryPresColor((HWND)mp2,
            PP_MENUFOREGROUNDCOLOR, "MenuText", "0 0 0");
    *pulMenuFunc2 = 250;
    lHiliteText = winhQueryPresColor((HWND)mp2,
            PP_MENUHILITEFGNDCOLOR, "MenuHiliteText", "255 255 255");

    *pulMenuFunc2 = 260;
    WinQueryPresParam((HWND)mp2,
                    PP_FONTNAMESIZE,
                    0,
                    &attrFound,
                    (ULONG)sizeof(abValue),
                    (PVOID)&abValue,
                    0);
    pszFontName = (PSZ)attrFound;

    *pulMenuFunc2 = 0;
}

/*
 *@@ mnuMeasureItem:
 *      this is called from the subclassed folder frame procedure
 *      (fnwpSubclassedFolderFrame in xfldr.c) when it receives
 *      WM_MEASUREITEM for each owner-draw folder content menu item.
 *      We will use the data queried above to calculate the dimensions
 *      of the items we're going to draw later.
 *      We must return the MRESULT for WM_MEASUREITEM here, about
 *      different documentation exists... I have chosen to return
 *      the height of the menu bar only.
 */

MRESULT mnuMeasureItem(POWNERITEM poi,      // owner-draw info structure
                        PGLOBALSETTINGS pGlobalSettings) // shortcut to global settings
{
    MRESULT mrc = (MRESULT)FALSE;
    POINTL aptlText[TXTBOX_COUNT];

    // get the item from the linked list of variable menu items
    // which corresponds to the menu item whose size is being queried
    PVARMENULISTITEM pItem = (PVARMENULISTITEM)lstItemFromIndex(
                (PLISTITEM)pliVarItems,
                (poi->idItem - (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_VARIABLE)));

    if (ulMiniIconSize == 0)
        // not queried yet?
        ulMiniIconSize = WinQuerySysValue(HWND_DESKTOP, SV_CYICON) / 2;

    if (pItem) {
        *pulMenuFunc2 = 305;
        // find out the space required for drawing this item with
        // the current font and fill the owner draw structure (mp2)
        // accordingly
        GpiQueryTextBox(poi->hps, strlen(pItem->szTitle), pItem->szTitle,
                TXTBOX_COUNT, (PPOINTL)&aptlText);
        poi->rclItem.xLeft = 0;
        poi->rclItem.yBottom = 0;
        poi->rclItem.xRight = aptlText[TXTBOX_TOPRIGHT].x
                                + ulMiniIconSize
                                - 15
                                + CX_ARROW;

        poi->rclItem.yTop = rtlMenuItem.yTop-rtlMenuItem.yBottom;

    }
    *pulMenuFunc2 = 310;
    mrc = MRFROMSHORT(poi->rclItem.yTop); //(MPARAM)poi->rclItem.yTop;

    *pulMenuFunc2 = 0;
    return (mrc);
}

/*
 *@@ mnuDrawItem:
 *      this is called from the subclassed folder frame procedure
 *      (fnwpSubclassedFolderFrame in xfldr.c) when it receives
 *      WM_DRAWITEM for each owner-draw folder content menu item.
 *      We will draw one menu item including the icons with each
 *      call of this function.
 *      This must return TRUE if the item was drawn.
 */

BOOL mnuDrawItem(PGLOBALSETTINGS pGlobalSettings,   // shortcut to global settings
                    MPARAM mp1,     // from WM_DRAWITEM: USHORT menu item id
                    MPARAM mp2)     // from WM_DRAWITEM: POWNERITEM structure
{
    BOOL brc = FALSE;
    // HBITMAP hbm;
    RECTL      rcl;
    // ULONG      x;
    LONG       lColor, lBmpBackground;
    POWNERITEM poi = (POWNERITEM)mp2;
    POINTL     ptl;

    // get the item from the linked list of variable menu items
    // which corresponds to the menu item being drawn
    PVARMENULISTITEM pItem = (PVARMENULISTITEM)lstItemFromIndex(
               (PLISTITEM)pliVarItems,
               (poi->idItem - (pGlobalSettings->VarMenuOffset + ID_XFMI_OFS_VARIABLE)));
    HPOINTER hIcon;

    if (pItem) {
        *pulMenuFunc2 = 405;

        // get the item's (object's) icon
        hIcon = _wpQueryIcon(pItem->pObject);

        // switch to RGB mode
        GpiCreateLogColorTable(poi->hps, 0,
            LCOLF_RGB,
            0, 0, NULL);

        *pulMenuFunc2 = 410;
        // find out the background color, which depends
        // on whether the item is highlighted (= selected);
        // these colors have been initialized by WM_INITMENU
        // above
        if (poi->fsAttribute & MIA_HILITED)
        {
            lColor = lHiliteBackground;
            lBmpBackground = lBackground;
        }
        else {
            lColor = lBackground;
            lBmpBackground = lHiliteBackground;
        }

        *pulMenuFunc2 = 440;
        // draw rectangle in lColor, size of whole item
        rcl = poi->rclItem;
        WinFillRect(poi->hps, &rcl, lColor);

        // print the item's text
        ptl.x = poi->rclItem.xLeft+5+(ulMiniIconSize);
        ptl.y = poi->rclItem.yBottom+4;
        GpiMove(poi->hps, &ptl);
        GpiSetColor(poi->hps,
            (poi->fsAttribute & MIA_HILITED) ? lHiliteText : lText);
        *pulMenuFunc2 = 450;
        GpiCharString(poi->hps, strlen(pItem->szTitle), pItem->szTitle);

        // draw the item's icon
        *pulMenuFunc2 = 460;
        WinDrawPointer(poi->hps, poi->rclItem.xLeft+2,
                poi->rclItem.yBottom
                +(
                    (rtlMenuItem.yTop-rtlMenuItem.yBottom-ulMiniIconSize) / 2
                ),
                hIcon,
                DP_MINI);

        *pulMenuFunc2 = 470;
        if (poi->fsAttribute != poi->fsAttributeOld) {
            // if the attribute has changed, i.e. item's
            // hilite state has been altered: we then need
            // to repaint the little "submenu" arrow, because
            // this has been overpainted by the WinFilLRect
            // above. We do this using icons from the XFLDR.DLL
            // resources, because no system bitmap has been
            // defined for the little Warp 4 triangle.
            if (pItem->usObjType == OC_CONTENTFOLDER)
            {
                if (hMenuArrowIcon == NULLHANDLE) {
                    hMenuArrowIcon = WinLoadPointer(HWND_DESKTOP,
                                        modQueryHandle(),
                                        fIsWarp4
                                            // on Warp 4, load the triangle
                                          ? ID_ICONMENUARROW4
                                            // on Warp 3, load the arrow
                                          : ID_ICONMENUARROW3);
                }
                // _Pmpf(("hIcon: 0x%lX", hMenuArrowIcon));
                if (hMenuArrowIcon) {
                    // DosBeep(10000, 10);
                    WinDrawPointer(poi->hps,
                            poi->rclItem.xRight - CX_ARROW,
                            poi->rclItem.yBottom
                            +(
                                (rtlMenuItem.yTop-rtlMenuItem.yBottom-ulMiniIconSize) / 2
                            ),
                            hMenuArrowIcon,
                            DP_MINI);

                }
            }
        }

        // now, this is funny: we need to ALWAYS delete the
        // MIA_HILITED flag in both the old and new attributes,
        // or PM will invert the item again for some reason;
        // this must be code from the stone ages (i.e. Microsoft)
        poi->fsAttributeOld = (poi->fsAttribute &= ~MIA_HILITED);

        brc = TRUE;
    }
    else
        brc = FALSE;

    *pulMenuFunc2 = 0;
    return (brc);
}

