#if 1

    #include "project.h"

    // ----------------------------------------------------------------
    // every function in this source file can be marked as   static
    // except for the "constructor" function
    // ----------------------------------------------------------------

    MAINTYPE MENUVId = SOB_TYPE_NO;

// to allow a widget to change the default handling of a SOB
//  1) create a COPY of the default SOB METHODS TABLE in the widget's code scope
//  2) overwrite the functional pointer, in that COPY, to the address of an
//     appropriate custom function.
//     We do that when the first instance of this widget class is instantiated.
//  3) and then the pointer, in the SOB data, to the default  SOB METHODS TABLE
//     with a pointer to our COPY
//     We do that, after step 2, when the first instance of this widget class is instantiated.

//  here we make SPACE for the COPY of the SOB METHODS TABLE

SOBMETHODSTABLE  MENUVSOBMETHODSTABLE;

WNDPROC    wpOrigMENUVProc;

MENUVOBJECT* MENUVLastCreated;

// ---------------------------------

#if MENUV_HAS_SUBCLASS

    // ----------------------------------------------------------------
    //  example of a custom  interception function     for a  subclassed   widget
    // ----------------------------------------------------------------

    static LRESULT CALLBACK MENUV_SUBCLASS_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // (demo) function causes the widget to change colour when the mouse hovers over the widget.
    // does not work for all widget types

    TRACKMOUSEEVENT tme = { sizeof(tme) };
    static myInt trackingFlag = 0;
    static COLORREF bgRGBenter = 0;

    union
    {
        SOBDATA* thisSob;
        MENUVOBJECT* thisObject;
    } promote = {};

    // be WARNED
    // try to find an OBJECT by the value of a (generic) property, in this case
    // it's  HWND , we default to finding it by it scanning the SOBDATA.
    // If we assume, as in this function, that it was triggered by widget type XYZ
    // then we would need a widget specific search function.
    // So scan the SOBDATA and then "cast" using the union to the appropriate widgettype.

    promote.thisSob = sob_find_hwnd(hwnd);
    if (promote.thisSob)
    {
        switch (msg)
        {
            case WM_NCHITTEST:

                if (!trackingFlag)
                {
                    tme.dwFlags = TME_LEAVE; // TME_HOVER;
                    tme.hwndTrack = hwnd;
                    tme.dwHoverTime = 1000;
                    TrackMouseEvent(&tme);

                    trackingFlag = 1;
                    bgRGBenter = promote.thisObject->bgRGB;

                    MENUV.SetRGB(promote.thisObject, RGB(0xc6, 0xe7, 0xff));
                }
                break;

            case WM_MOUSEHOVER:

                break;

            case WM_MOUSELEAVE:
                if (trackingFlag)
                {
                    trackingFlag = 0;
                    MENUV.SetRGB(promote.thisObject, bgRGBenter);
                }
                break;

            default:
                break;
        }
    }
    return CallWindowProc(wpOrigMENUVProc, hwnd, msg, wParam, lParam);
}

#endif

// ---------------------------------

#if MENUV_HAS_OWNERDRAWN
    // ----------------------------------------------------------------
    //  example of a custom  ownerdrawn handler
    // ----------------------------------------------------------------

    OWNDERDRAWNHANDLER MENUV_OD_HANDLER;

static void  MENUV_OD_HANDLER(SOBDATA* sData, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // when the WINDOW message handler is sent a  WM_DRAWITEM message, it will scan
    // the SOB DATA to find the SOB with the HWND referenced by the WM_DRAWITEM message's
    // (LPDRAWITEMSTRUCT)lParam structure.
    // If the  HWND  is found then that SOB's ownerDrawnHandler field is checked for an
    // entry (i.e.  NEQU NULL). If NEQU NULL then the value is treated as a functional pointer
    // a MENUV_OD_HANDLER function!

    // the code, bDrawButtonRGB,  is common to BUTTONs and STATIC widgets and is fairly involved.
    // So it has been made a common function located in the  ownderdraw_support files.

    bDrawButtonRGB(sData, lpDrawItemStruct);
}

#endif

// ---------------------------------

#if MENUV_HAS_RGB

    MENUVFNC_SETVALUE MENUV_SetRGB;
static  myUInt MENUV_SetRGB(MENUVOBJECT* thisObject, myUInt rgbValue)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return sob_SetRGB(thisObject->psSobData, (COLORREF)rgbValue);
}
#endif

// ---------------------------------

#if MENUV_HAS_STATE
    // some widget types have a  STATE
    // e.g. menu item ticked, radio button, check button
    MENUVFNC_GETVALUE MENUV_GetState;
static  myUInt MENUV_GetState(MENUVOBJECT* thisObject)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return MFS_UNCHECKED;

    SOBDATA* pData = (SOBOBJ thisObject)->ppSobData;

    MENUITEMINFO menuinfo = { 0 };
    menuinfo.cbSize = sizeof(menuinfo);
    menuinfo.fMask = MIIM_STATE;

    GetMenuItemInfo
    (pData->hMenu
    , (UINT)(SOBOBJ thisObject)->sIndex
    , FALSE
    , &menuinfo
    );

    return (menuinfo.fState EQU MFS_CHECKED);
}

// ---------------------------------

MENUVFNC_SETVALUE  MENUV_SetState;
static  myUInt MENUV_SetState(MENUVOBJECT* thisObject, myUInt checkState)
{

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    if ((SOBOBJ thisObject)->type  NEQU MENUVId)  return SOB_NOK;

    MENUITEMINFO menuinfo = { 0 };

    menuinfo.cbSize = sizeof(menuinfo);
    menuinfo.fMask = MIIM_STATE; // MIIM_CHECKMARKS ;

    if (checkState) menuinfo.fState = MFS_CHECKED;
    else menuinfo.fState = MFS_UNCHECKED;

    SOBDATA* pData = (SOBOBJ thisObject)->ppSobData;

    (void)SetMenuItemInfo
    (pData->hMenu
    , (UINT)(SOBOBJ thisObject)->sIndex
    , FALSE
    , &menuinfo
    );

    (void)DrawMenuBar((HWND)appHWND);
    return (menuinfo.fState EQU MFS_CHECKED);
}

// ---------------------------------

MENUVFNC_SHAKE MENUV_UnStateAll;
static  myUInt MENUV_UnStateAll(MENUVOBJECT* thisObject)
{

    if (!thisObject)return SOB_NOK;
    if (!(SOBOBJ thisObject)->sIndex) return SOB_NOK;

    SOBDATA* pData;

    if ((SOBOBJ thisObject)->type EQU MENUVId)
    {
        pData = (SOBOBJ thisObject)->ppSobData;
    }
    else if ((SOBOBJ thisObject)->subType EQU MENUHId)
    {
        pData = (SOBOBJ thisObject);
    }
    else
    {
        return SOB_NOK;
    }

    SOBDATA* childObject = sob_get_first_child(pData);

    MENUITEMINFO menuinfo = { 0 };
    menuinfo.cbSize = sizeof(menuinfo);
    menuinfo.fMask = MIIM_STATE;
    menuinfo.fState = MFS_UNCHECKED;

    while (childObject)
    {
        if (childObject->type EQU MENUVId)
        {
            (void)SetMenuItemInfo
            (pData->hMenu
            , (UINT)childObject->sIndex
            , FALSE
            , &menuinfo
            );

        }

        childObject = sob_get_next_child(childObject);
    }
    return SOB_OK;

}

// ---------------------------------

MENUVFNC_SHAKE MENUV_ToggleState;
static  myUInt MENUV_ToggleState(MENUVOBJECT* thisObject)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  MENUV.SetState(thisObject, (MENUV.GetState(thisObject)  EQU 0));
}

#endif

MENUVFNC_GETSIBLING MENUV_GetChecked;
static MENUVOBJECT* MENUV_GetChecked(MENUVOBJECT* sData)
{

    if (sData EQU NULL) return  NULL;

    SOBDATA* pData = NULL;

    if (sData->subType EQU MENUHId)   pData = SOBOBJ sData;
    else  if (sData->type EQU MENUVId)  pData = sData->ppSobData;
    else  return NULL;

    MENUVOBJECT* childObj = (MENUVOBJECT*)CONTAINER.FirstChild(pData);
    while (childObj)
    {
        if ((childObj->type EQU MENUVId) LAND(MENUV.GetState(childObj)))  return childObj;
    }
    return NULL;
}

// ---------------------------------

#if  MENUV_HAS_EVENTHANDLER

    // example of a EVENTHANDLER: in this case when the widget is clicked on with the mouse
    // Careful this  function is only valid for widgets that receive
    //   WM_COMMAND messages   carrying a  BN_CLICKED notification

    static void MENUV_object_clicked(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, SOBDATA* sData)
{
    MENUVOBJECT* thisObject = (MENUVOBJECT*)lParam;
    WORD NotificationCode = HIWORD(wParam);
    WORD identifier = LOWORD(wParam);

    switch (NotificationCode)
    {
        case CBN_SELCHANGE:
            swprintf_s
            (txtBuffer, szTxtBuffer, L" MENUV clicked 0x%04x  0x%04x  0x%04x\n"
            , thisObject->sIndex
            , NotificationCode
            , identifier);    addText(txtBuffer);

            SOB.Report((SOBDATA*)thisObject);

            //          MENUV.ToggleState(thisObject);

            break;

        default:
            #if DebuggingAid
                swprintf_s
                (txtBuffer, szTxtBuffer, L" MENUV unhandled notification 0x%04x  0x%04x  0x%04x\n"
                , thisObject->sIndex
                , NotificationCode
                , identifier);    addText(txtBuffer);
            #endif

            break;
    }
}

MENUVFNC_SETEVENTHANDLER MENUV_SetEventHandler;
static myUInt MENUV_SetEventHandler(MENUVOBJECT* thisObject, EVENTHANDLER value)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return SOB.SetEventHandler(SOBOBJ thisObject, value);
}

#endif

// ---------------------------------

#if  MENUV_HAS_EMPTY
    // some widget types can be emptied e.g. combo boxes, RichTextEdit ..

    MENUVFNC_SHAKE MENUV_Empty;
static  myUInt MENUV_Empty(MENUVOBJECT* thisObject)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB.Empty(SOBOBJ thisObject);
}

#endif

// ---------------------------------

#if  MENUV_HAS_GREY
    // some widget types can be greyed e.g. menu items, some button types (rare but possible)

    MENUVFNC_GETVALUE MENUV_GetGrey;
static  myUInt MENUV_GetGrey(MENUVOBJECT* thisObject)
{

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return MFS_UNCHECKED;

    SOBDATA* pData = (SOBOBJ thisObject)->ppSobData;

    MENUITEMINFO menuinfo = { 0 };
    menuinfo.cbSize = sizeof(menuinfo);
    menuinfo.fMask = MIIM_STATE;

    GetMenuItemInfo
    (pData->hMenu
    , (UINT)(SOBOBJ thisObject)->sIndex
    , FALSE
    , &menuinfo
    );

    return (menuinfo.fState EQU MFS_CHECKED);
}

MENUVFNC_SETVALUE MENUV_SetGrey;
static  myUInt MENUV_SetGrey(MENUVOBJECT* thisObject, myUInt value)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    SOBDATA* pData = (SOBOBJ thisObject)->ppSobData;

    MENUITEMINFO menuinfo = { 0 };
    menuinfo.cbSize = sizeof(menuinfo);
    menuinfo.fMask = MIIM_STATE;

    menuinfo.fState = MFS_ENABLED;
    if (value)  menuinfo.fState = MFS_DISABLED;

    SetMenuItemInfo
    (pData->hMenu
    , (UINT)((SOBOBJ thisObject)->sIndex)
    , FALSE
    , &menuinfo
    );

    DrawMenuBar((HWND)appHWND);
    return  SOB_OK;
}

#endif

// ---------------------------------

#if  MENUV_HAS_SELECTION
    // some widget types have selectable items
    // e.g. COMBO boxes, edit fields

    MENUVFNC_GETVALUE MENUV_GetSelectionIndex;
static  myUInt MENUV_GetSelectionIndex(MENUVOBJECT* thisObject)
{
    #if MENUV_IS_2D
        return (myInt)SendMessage(thisObject->hWnd, CB_GETCURSEL, 0, 0);
    #endif

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB_OK;
}

MENUVFNC_SETVALUE MENUV_SetSelectionIndex;
static  myUInt MENUV_SetSelectionIndex(MENUVOBJECT* thisObject, myUInt value)
{
    #if MENUV_IS_2D
        // Sets the ZERO based index to be  the currently selected item of the combo
        return (int)SendMessage(thisObject->hWnd, CB_SETCURSEL, value, 0);
    #endif

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB_OK;
}

MENUVFNC_GETVALUE MENUV_GetItemCount;
static  myUInt MENUV_GetItemCount(MENUVOBJECT* thisObject)
{
    #if MENUV_IS_2D

        return (myUInt)SendMessage(thisObject->hWnd, CB_GETCOUNT, 0, 0);
    #endif
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB_OK;
}

MENUVFNC_NEWITEMTEXT MENUV_AddItem;
static myUInt MENUV_AddItem(MENUVOBJECT* thisObject, TCHAR* pText)
{

    #if MENUV_IS_2D
        int index;

        index = (int)SendMessage(thisObject->hWnd, CB_ADDSTRING, 0, (LPARAM)pText);
        SendMessage(thisObject->hWnd, CB_SETITEMDATA, (WPARAM)index, (LPARAM)index);
        return (myUInt)index;
    #endif

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB_OK;
}

MENUVFNC_SETITEMTEXT MENUV_GetItemText;
static TCHAR* MENUV_GetItemText(MENUVOBJECT* thisObject, myUInt value)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return NULL;

    return  (TCHAR*)_TEXT("");
}

MENUVFNC_SETITEMTEXT MENUV_SetItemText;
static myUInt MENUV_SetItemText(MENUVOBJECT* thisObject, myUInt value, TCHAR* pText)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB_OK;
}

MENUVFNC_SETVALUE MENUV_DeleteIndex;
static  myUInt MENUV_DeleteIndex(MENUVOBJECT* thisObject, myUInt value)
{
    #if MENUV_IS_2D
        // Sets the ZERO based index to be  the currently selected item of the combo
        return (int)SendMessage(thisObject->hWnd, CB_DELETESTRING, value, 0);
    #endif
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return  SOB_OK;
}

#endif

// ---------------------------------
#if MENUV_HAS_TITLE

    MENUVFNC_GETTEXT  MENUV_GetTitle;
static TCHAR* MENUV_GetTitle(MENUVOBJECT* thisObject)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return NULL;

    SOBDATA* pData = (SOBOBJ thisObject)->ppSobData;

    #define menuTitleMax 255
    static TCHAR title[menuTitleMax + 1] = { 0 };

    MENUITEMINFO menuinfo = { 0 };
    menuinfo.cbSize = sizeof(menuinfo);
    menuinfo.fMask = MIIM_TYPE;
    menuinfo.fType = MFT_STRING;
    menuinfo.dwTypeData = (LPWSTR)&title;
    menuinfo.cch = menuTitleMax;

    GetMenuItemInfo
    (pData->hMenu
    , (UINT)(SOBOBJ thisObject)->sIndex
    , FALSE
    , &menuinfo
    );
    return title;

    return   SOB.GetTitle(SOBOBJ thisObject);
}

// ---------------------------------

MENUVFNC_SETTEXT  MENUV_SetTitle;
static myUInt MENUV_SetTitle(MENUVOBJECT* thisObject, TCHAR* txt)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return SOB.SetTitle(SOBOBJ thisObject, txt);
}
#endif

// ---------------------------------

SOBFNC_GETTEXT MENUV_SOB_GetType;
static TCHAR* MENUV_SOB_GetType(SOBDATA* thisSob)
{
    static TCHAR objText[] = TEXT("MENUV");
    return  objText;
}

MENUVFNC_GETTEXT MENUV_GetType;
static  TCHAR* MENUV_GetType(MENUVOBJECT* thisObject)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return NULL;
    return   MENUV_SOB_GetType(SOBOBJ thisObject);
}

// ---------------------------------

MENUVFNC_SETVALUE MENUV_SetShow;
static  myUInt MENUV_SetShow(MENUVOBJECT* thisObject, myUInt value)
{
    // this will need customising if the widget is complex
    // i.e. is really a member of a set of  widgets that can not all appear at the same time
    // e.g. a radio button     or an  overlay set

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return SOB.SetShow(SOBOBJ thisObject, value);
}

// ---------------------------------

MENUVFNC_GETVALUE MENUV_GetUserData;
static myUInt MENUV_GetUserData(MENUVOBJECT* thisObject)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return NULLV;
    return SOB.GetUserData(SOBOBJ thisObject);
}

// ---------------------------------

#if MENUV_HAS_STRETCH

    MENUVFNC_SETVALUE MENUV_SetStretch;
static  myUInt MENUV_SetStretch(MENUVOBJECT* thisObject, myUInt value)
{

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    SOB.SetStretch(SOBOBJ thisObject, value);

    return SOB.SetShow(SOBOBJ thisObject, value);
}
#endif
// ---------------------------------

#if MENUV_HAS_LAST

    MENUVFNC_LASTOBJECT MENUV_LastWidget;
static  MENUVOBJECT* MENUV_LastWidget()
{
    return MENUVLastCreated;
}
#endif

// ---------------------------------

MENUVFNC_SETVALUE MENUV_SetUserData;
static myUInt MENUV_SetUserData(MENUVOBJECT* thisObject, myUInt value)
{
    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;
    return SOB.SetUserData(SOBOBJ thisObject, value);
}

// ----------------------------------------------------------------
// the "destructor" function
// ----------------------------------------------------------------
SOBFNC_SHAKE MENUV_SOB_Delete;
static myUInt MENUV_SOB_Delete(SOBDATA* thisSob)
{
    // we need this function so that when a CONTAINER is deleted
    // it can trigger a delete of any widget related things of it's child
    // SOB OBJECTs here:-

    if (fnInvalidSobGeneric(thisSob))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("MENUV.Delete given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    if (thisSob->type NEQU MENUVId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("MENUV.Delete given a non MENUV object\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }
    //  swprintf_s(txtBuffer, szTxtBuffer, _TEXT("MENUV.Delete \n"));  ReportError(txtBuffer);

    DeleteMenu(thisSob->ppSobData->hMenu, thisSob->sIndex, MF_BYCOMMAND);

    DrawMenuBar((HWND)appHWND);

    sobObj_delete(thisSob);
    return SOB_OK;
}

MENUVFNC_SHAKE MENUV_Delete;
static  myUInt MENUV_Delete(MENUVOBJECT* thisObject)
{
    // this will need customising if the widget is complex
    // i.e. is really a set of widgets
    // e.g. an  overlay set

    if (fnInvalidSobGeneric(SOBOBJ thisObject))return SOB_NOK;

    return   MENUV_SOB_Delete(SOBOBJ thisObject);
}

// ----------------------------------------------------------------
//  inheritance from the underlying SOB
// ----------------------------------------------------------------

// if an inherited function is to be customised/overwritten
// then do it HERE:

MENUVFNC_GETTEXT  MENUV_GETTITLE;
static TCHAR* MENUV_GETTITLE(MENUVOBJECT* sData)
{
    return (TCHAR*)_TEXT("MENUV");
}

MENUVFNC_LASTOBJECT  MENUV_Spacer;
static MENUVOBJECT* MENUV_Spacer()
{
    union
    {
        SOBDATA* thisSob;
        MENUVOBJECT* thisObject;
    } promote = {0};

    // ---------------------------------
    // A  Screen OBject ( SOB )  is really a structure
    //    the GENERIC structure
    //    and a ptr to the VTABLE
    // ---------------------------------
    // An OBJECT instance is really a structure
    //    the GENERIC structure
    //    and a  ptr to the OBJECT's private functions
    // ---------------------------------
    // So a   SOB   and an    OBJECT     have identical structures
    // WHY:
    // This way we can create a SOB as a sort of generic OBJECT.
    // Some functions, predominantly concerned with
    //    screen size management
    //    screen position management
    //   are OBJECT independent.
    //
    // Then we can have an OBJECT specific "view" were each OBJECT type
    // can have its own strongly typed structure.
    // ---------------------------------

    if (!MENUVId)
    {
        // ---------------------------------
        #if DeveloperMode

            union
            {
                myUInt* pEntry;
                void* pVoid;
            } reRef = {0};

            reRef.pVoid = &MENUV;
            myUInt* pEntry = reRef.pEntry;

            int entryCount = sizeof(MENUVMETHODTABLE) / sizeof(void*);
            int i;
            for (i = 0; i < entryCount; i++)
            {
                if ((!*pEntry)   LOR(*pEntry BAND 0x07))
                {
                    swprintf_s(txtBuffer, szTxtBuffer, _TEXT("MENUV methods table, entry: %02i,  not fully/correctly populated. 0x%llx\n"), i, *pEntry);
                    ReportError(txtBuffer);
                }
                pEntry++;
            }

        #endif

        // ---------------------------------

        MENUVId = int_next_maintype();

        // to allow a widget to change the default handling of a SOB
        //  1) create a COPY of the default SOB METHODS TABLE in the widget's code scope
        //  2) overwrite the functional pointer, in that COPY, to the address of an
        //     appropriate custom function.
        //     We do that when the first instance of this widget class is instantiated.
        //  3) and then the pointer, in the SOB data, to the default  SOB METHODS TABLE
        //     with a pointer to our COPY
        //     We do that, after step 2, when the first instance of this widget class is instantiated.

        // ---------------------------------
        //    SPACE for the COPY of the SOB METHODS TABLE  was made at the TOP of this source file
        //    SOBMETHODSTABLE  MENUVSOBMETHODSTABLE;
        // ---------------------------------
        // populate the COPY with the default SOB METHID TABLE

        fnInheritSOBmethods(&MENUVSOBMETHODSTABLE);

        // ---------------------------------
        // now populate those entries, in the COPY of the  SOB METHODS TABLE , that are
        // to be widget specific

        MENUVSOBMETHODSTABLE.GetType = &MENUV_SOB_GetType;

        MENUVSOBMETHODSTABLE.Delete = &MENUV_SOB_Delete;
    }

    // ---------------------------------

    // the current PARENT MUST BE a    MENUV

    SOBID pSobId = sob_get_pSobIndex();
    MAINTYPE type = sob_get_type(pSobId);
    CONTYPE conType = sob_get_subtype(pSobId);

    if (conType NEQU MENUHId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"MENU_V Parent id %i not a MENU H, but a %ws.\n", pSobId, sob_container_to_text(conType));

        ReportError(txtBuffer);
        return NULL;
    }

    HMENU  pMenu = sob_get_HMENU(pSobId);

    promote.thisSob = sob_new_new(MENUVId, SOB_CONTAINER_NO, 0, 0);

    // ---------------------------------

    int menu_style = 0;  // for future use

    if (!AppendMenu(pMenu, MF_SEPARATOR, (UINT)promote.thisSob->sIndex, NULL))
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"Could not APPEND a MENU V\n");
        ReportError(txtBuffer);
        return NULL;
    }

    // ---------------------------------
    // replace the default SOB METHOD TABLE with our MENUV's custom table

    promote.thisSob->sVtbl = &MENUVSOBMETHODSTABLE;

    promote.thisSob->txtRGB = -1; // use system default
    promote.thisSob->bgRGB = -1; // use system default

    #if MENUV_HAS_SUBCLASS
        // ---------------------------------
        // if you need to subclass the widget (i.e. intercept its internal message handling)
        // then insert the interception function here.

        wpOrigMENUVProc = (WNDPROC)SetWindowLongPtr(promote.thisObject->hWnd, GWLP_WNDPROC, (LONG_PTR)MENUV_SUBCLASS_proc);
    #endif

    #if MENUV_HAS_OWNERDRAWN
        // ---------------------------------
        // if this widget is ownderdraw then link in the handler here

        promote.thisSob->ownerDrawnHandler = MENUV_OD_HANDLER;
    #endif

    #if MENUV_HAS_EVENTHANDLER
        // ---------------------------------
        // All OBJECTS are created with a prime/main  EVENTHANDLER
        // For this particular OBJECT the prime/main  EVENTHANDLER
        // is a left mouse click

        // Normally the  "user"  sets a, probably different, EVENTHANDLER for
        // each instantiated widget.

        // If you want you can set a default EVENTHANDLER which the
        // "user" can overwrite for a specific OBJECT
        promote.thisSob->eventHandler = (EVENTHANDLER)MENUV_object_clicked;
    #endif

    MENUVLastCreated = promote.thisObject;
    return   promote.thisObject;

}

// ----------------------------------------------------------------
// the "constructor" function
// ----------------------------------------------------------------

MENUVFNC_GETOBJECT MENUV_Create;
static MENUVOBJECT* MENUV_Create(MENUVCREATEPARAMETERS)
{
    union
    {
        SOBDATA* thisSob;
        MENUVOBJECT* thisObject;
    } promote = {0};

    // ---------------------------------
    // A  Screen OBject ( SOB )  is really a structure
    //    the GENERIC structure
    //    and a ptr to the VTABLE
    // ---------------------------------
    // An OBJECT instance is really a structure
    //    the GENERIC structure
    //    and a  ptr to the OBJECT's private functions
    // ---------------------------------
    // So a   SOB   and an    OBJECT     have identical structures
    // WHY:
    // This way we can create a SOB as a sort of generic OBJECT.
    // Some functions, predominantly concerned with
    //    screen size management
    //    screen position management
    //   are OBJECT independent.
    //
    // Then we can have an OBJECT specific "view" were each OBJECT type
    // can have its own strongly typed structure.
    // ---------------------------------

    if (!MENUVId)
    {
        // ---------------------------------
        #if DeveloperMode

            union
            {
                myUInt* pEntry;
                void* pVoid;
            } reRef = {0};

            reRef.pVoid = &MENUV;
            myUInt* pEntry = reRef.pEntry;

            int entryCount = sizeof(MENUVMETHODTABLE) / sizeof(void*);
            int i;
            for (i = 0; i < entryCount; i++)
            {
                if ((!*pEntry)   LOR(*pEntry BAND 0x07))
                {
                    swprintf_s(txtBuffer, szTxtBuffer, _TEXT("MENUV methods table, entry: %02i,  not fully/correctly populated. 0x%llx\n"), i, *pEntry);
                    ReportError(txtBuffer);
                }
                pEntry++;
            }

        #endif

        // ---------------------------------

        MENUVId = int_next_maintype();

        // to allow a widget to change the default handling of a SOB
        //  1) create a COPY of the default SOB METHODS TABLE in the widget's code scope
        //  2) overwrite the functional pointer, in that COPY, to the address of an
        //     appropriate custom function.
        //     We do that when the first instance of this widget class is instantiated.
        //  3) and then the pointer, in the SOB data, to the default  SOB METHODS TABLE
        //     with a pointer to our COPY
        //     We do that, after step 2, when the first instance of this widget class is instantiated.

        // ---------------------------------
        //    SPACE for the COPY of the SOB METHODS TABLE  was made at the TOP of this source file
        //    SOBMETHODSTABLE  MENUVSOBMETHODSTABLE;
        // ---------------------------------
        // populate the COPY with the default SOB METHID TABLE

        fnInheritSOBmethods(&MENUVSOBMETHODSTABLE);

        // ---------------------------------
        // now populate those entries, in the COPY of the  SOB METHODS TABLE , that are
        // to be widget specific

        MENUVSOBMETHODSTABLE.GetType = &MENUV_SOB_GetType;

        MENUVSOBMETHODSTABLE.Delete = &MENUV_SOB_Delete;
    }

    // ---------------------------------

    // the current PARENT MUST BE a    MENUV

    SOBID pSobId = sob_get_pSobIndex();
    MAINTYPE type = sob_get_type(pSobId);
    CONTYPE conType = sob_get_subtype(pSobId);

    if (conType NEQU MENUHId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"MENU_V Parent id %i not a MENU H, but a %ws.\n", pSobId, sob_container_to_text(conType));

        ReportError(txtBuffer);
        return NULL;
    }

    HMENU  pMenu = sob_get_HMENU(pSobId);

    promote.thisSob = sob_new_new(MENUVId, SOB_CONTAINER_NO, 0, 0);

    // ---------------------------------

    int menu_style = 0;  // for future use

    if (!AppendMenu(pMenu, MF_STRING BOR menu_style, (UINT)promote.thisSob->sIndex, txt))
    {
        swprintf_s(txtBuffer, szTxtBuffer, L"Could not APPEND a MENU V\n");
        ReportError(txtBuffer);
        return NULL;
    }

    // ---------------------------------
    // replace the default SOB METHOD TABLE with our MENUV's custom table

    promote.thisSob->sVtbl = &MENUVSOBMETHODSTABLE;

    promote.thisSob->fncCustomDelete = &MENUV_SOB_Delete;
    promote.thisSob->fncCustomGetTypeText = &MENUV_SOB_GetType;

    promote.thisSob->txtRGB = -1; // use system default
    promote.thisSob->bgRGB = -1; // use system default

    #if MENUV_HAS_SUBCLASS
        // ---------------------------------
        // if you need to subclass the widget (i.e. intercept its internal message handling)
        // then insert the interception function here.

        wpOrigMENUVProc = (WNDPROC)SetWindowLongPtr(promote.thisObject->hWnd, GWLP_WNDPROC, (LONG_PTR)MENUV_SUBCLASS_proc);
    #endif

    #if MENUV_HAS_OWNERDRAWN
        // ---------------------------------
        // if this widget is ownderdraw then link in the handler here

        promote.thisSob->ownerDrawnHandler = MENUV_OD_HANDLER;
    #endif

    #if MENUV_HAS_EVENTHANDLER
        // ---------------------------------
        // All OBJECTS are created with a prime/main  EVENTHANDLER
        // For this particular OBJECT the prime/main  EVENTHANDLER
        // is a left mouse click

        // Normally the  "user"  sets a, probably different, EVENTHANDLER for
        // each instantiated widget.

        // If you want you can set a default EVENTHANDLER which the
        // "user" can overwrite for a specific OBJECT
        promote.thisSob->eventHandler = (EVENTHANDLER)MENUV_object_clicked;
    #endif

    MENUVLastCreated = promote.thisObject;
    return   promote.thisObject;
}

// ----------------------------------------------------------------
// the external view
// ----------------------------------------------------------------

// BE WARNED: must be in the same sequence as the struct defn in the  header file
// if you create custom functions / methods for this widget then they
// MUST be in the struct defn and initialised into this struct instance.

MENUVMETHODTABLE  MENUV =
{
    MENUV_Create
    , MENUV_Delete

    #if MENUV_HAS_RGB
        , MENUV_SetRGB
    #endif

    #if MENUV_HAS_EMPTY
        , MENUV_Empty
    #endif

    #if MENUV_HAS_STATE
        , MENUV_GetState
        , MENUV_SetState
        , MENUV_ToggleState

    #endif

    #if MENUV_HAS_EVENTHANDLER
        , MENUV_SetEventHandler
    #endif

    #if  MENUV_HAS_GREY
        , MENUV_GetGrey
        , MENUV_SetGrey
    #endif

    #if  MENUV_HAS_SELECTION
        , MENUV_GetSelectionIndex
        , MENUV_SetSelectionIndex

        , MENUV_AddItem

        , MENUV_GetItemCount

        , MENUV_GetItemText
        , MENUV_SetItemText

        , MENUV_DeleteIndex

    #endif

    #if MENUV_HAS_TITLE
        , MENUV_GetTitle
        , MENUV_SetTitle
    #endif

    #if MENUV_HAS_STRETCH
        , MENUV_SetStretch
    #endif

    #if MENUV_HAS_LAST
        , MENUV_LastWidget
    #endif

    , MENUV_UnStateAll

    , MENUV_GetUserData
    , MENUV_SetUserData

    ,  MENUV_Spacer
    , MENUV_GetChecked

};

#endif
