Search code examples
visual-c++mfctoolbarpopupmenumfc-feature-pack

OnInitMenuPopup does not init correctly when called from a toolbar button that was a POPUP menu in the App main menu bar


To make a long history short, imagine my main menu is a CMFCMenuBar which menu is defined by:

IDR_MAINFRAME MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "New",               ID_FILE_NEW
        MENUITEM "Open",              ID_FILE_OPEN
        MENUITEM "Save",              ID_FILE_SAVE
        POPUP "Save As"
        BEGIN
             MENUITEM "Format XXX",                 ID_FILE_SAVEAS_XXX
             MENUITEM SEPARATOR
             MENUITEM "Format YYY",                 ID_FILE_SAVEAS_YYY
             MENUITEM SEPARATOR
             MENUITEM "Format ZZZ",                 ID_FILE_SAVEAS_ZZZ
             MENUITEM "Format WWW",                 ID_FILE_SAVEAS_WWW
        END
        MENUITEM "Close",             ID_FILE_CLOSE
    END
    POPUP "&Object"
    BEGIN
        POPUP "Create object"
        BEGIN
            MENUITEM "Create object...",            ID_CREATE_OBJECT
            MENUITEM "Create object as...",         ID_CREATE_OBJECTAS
        END
        POPUP "Save object"
        BEGIN
            MENUITEM "Save object...",              ID_SAVE_AS_XXX
            MENUITEM "Save object copy",            ID_SAVE_OBJECT_COPY
        END
    END
    MENUITEM "Delete",                      ID_DELETE_OBJECT
    END
END

As POPUP menus have no ID (all them have a value of -1), to know on what menu I am doing the initialization, I followed the approach on https://stackoverflow.com/a/3910405/383779 and implemented functions like

bool CMainFrame::IsFileMenu(CMenu* pPopupMenu) const
{
    if(!pPopupMenu);    
       return false;
    return (pPopupMenu->GetMenuItemCount() > 0) && (pPopupMenu->GetMenuItemID(0) == ID_FILE_NEW);
}

bool CMainFrame::IsObjectMenu(CMenu* pPopupMenu) const
{
    if(!pPopupMenu);    
       return false;
    return (pPopupMenu->GetMenuItemCount() > 0) && (pPopupMenu->GetMenuItemID(2) == ID_DELETE_OBJECT);
}

The Menu initialization is like:

void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) 
{
    if(!license.supports("SaveAsFile"))
    {
         if(IsFileMenu())
         {
              //code to find ID_FILE_SAVEAS_XXX parent menu ("Save As"), then recursively delete all its descendants and itself
         }

         if(IsObjectMenu())
         {
            for(int i = 0; i < pPopupMenu->GetMenuItemCount();i++)
            {
                MENUITEMINFO MenuItemInfo;
                memset(&MenuItemInfo, 0, sizeof(MENUITEMINFO));
                MenuItemInfo.cbSize = sizeof (MENUITEMINFO);    // must fill up this field
                MenuItemInfo.fMask = MIIM_SUBMENU;  

                if (!pPopupMenu->GetMenuItemInfo(i, &MenuItemInfo, TRUE))
                    continue;

                CMenu* SubMenu = pPopupMenu->GetSubMenu(i);

                if (SubMenu != NULL)
                {
                    memset(&MenuItemInfo, 0, sizeof(MENUITEMINFO));
                    MenuItemInfo.cbSize = sizeof (MENUITEMINFO);
                    MenuItemInfo.fMask = MIIM_ID | MIIM_TYPE;

                    for(int j=0; j<SubMenu->GetMenuItemCount() ;j++ )
                    {
                        SubMenu->GetMenuItemInfo(j, &MenuItemInfo, TRUE);

                        if (MenuItemInfo.wID == ID_CREATE_OBJECTAS)
                        {
                            for (int i=0; i<m_custom_objects.GetSize(); i++)
                                pPopup->AppendMenu(MF_STRING | MF_ENABLED, WM_MENU_CUSTOM_OBJECTS_BEGIN + i, (LPCTSTR) m_custom_objects[i].GetName() );

                            found= true;
                            break;
                        }
                    }

                   if(found)
                       break;
                }
            }    
        }
    }

    __super::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

Notice the ID_FILE_SAVEAS_XXX MENUITEM identifer is repeated! As I don't'want to delete the "Save Object" POPUP when the the license supporting "SaveAsFile" is absent, so it is an absolute need to identify what menu is now being processed.

Now the user has created a toolbar by using the MFC Customize dialog, then he dragged the "Create Object" POPUP to the new toolbar. When he clicks this new button of the toolbar, he sees the "Create object" and "Create object as" option, but not the custom objects have not been appended because to the IsObjectMenu() condition was not satisfied. The behaviour was completely different when he came from the main menu; the appending has been done!

How can I make the code in a way that the Appending of custom objects is done when the user clicks the button of the toolbar?


Solution

  • I am not proud of my own code, but I solved it by separating the code of "Object" from "Create Object" menu.

    bool CMainFrame::IsCreateObjectMenu(CMenu* pPopupMenu) const
    {
        if(!pPopupMenu);    
           return false;
        return (pPopupMenu->GetMenuItemCount() > 0) && (pPopupMenu->GetMenuItemID(0) == ID_CREATE_OBJECT);
    }
    
    
    void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) 
    {
        if(!license.supports("SaveAsFile"))
        {
             if(IsFileMenu())
             {
                  //code to find ID_FILE_SAVEAS_XXX parent menu ("Save As"), then recursively delete all its descendants and itself
             }
    
             if(IsObjectMenu())
             {
                  // Do things
             }
    
             if(IsCreateObjectMenu())
             {
                 for (int i=0; i<m_custom_objects.GetSize(); i++)
                     pPopupMenu->AppendMenu(MF_STRING | MF_ENABLED, WM_MENU_CUSTOM_OBJECTS_BEGIN + i, (LPCTSTR) m_custom_objects[i].GetName());
             }
        }
    
        __super::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
    }