Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS General Discussion » CExtPopupMenuWnd Item focus problem. Collapse All
Subject Author Date
Chris Mason Nov 17, 2009 - 5:46 AM

Hi,


We are currently experiencing a problem with our menus that until now has gone unnoticed. When using the keyboard to select a menu (for example ALT + F) the first item is not being selected. Or more specifically it is being selected initially when OnTrackPopup is called but within a fraction of a second the item is no longer selected. However, CExtPopupWndMenu::_ItemFocusCancel is not being called. Is the menu being redrawn somewhere else that we are not noticing?


I appreciate that this is quite an ambiguous question but any help would be greatly appreciated.


 


 

Chris Mason Nov 20, 2009 - 1:51 AM

Hmmm. Have just done that and it appears that both functions are only called once when tracking the menu. Strange, guess it’s just something we’ll have to live with. Cheers.

Chris Mason Nov 19, 2009 - 2:28 AM

Sorry, I should have mentioned that we are running an older version of Prof-uis, 2.60 I think. I appreciate that this is probably not a bug as it is something that you would have picked up on long ago, you notice quite quickly that the menus don’t work as you’re used to.


We are wondering whether it has something to do with the way we create our menus? The code for this is from an older version of our product which we have ported directly into our latest version. The major difference is that we are using prof-uis.


Essentially what we do is build the menus from a text file, like xml but in a more archaic fashion. For example our file menu looks like this:



Se&lect Project...	  #Ctrl+L \ @DfwFileProjectLogin
&Project Information...		 \ @DfwFileProjectInfo
							    \
&New						    \ >
	    &Worksheet	  #Ctrl+N \ @DfwFileNewWorksheet
	    &Script				 \ @DfwFileNewScript
	    &Macro				  \ @DfwFileNewMacro
        &Project                \ @DfwFileNewProject
							    \ <
&Open...			    #Ctrl+O \ @DfwFileOpen
&Run						    \ >
	    &Script...	  #Ctrl+R \ @DfwFileRunScript
	    &Macro...	   #Ctrl+M \ @DfwFileRunMacro
							    \ <
							    \
&Save Worksheet		 #Ctrl+S \ @DfwFileSaveWorksheet
Save Worksheet &As...		   \ @DfwFileSaveWorksheetAs
                \
&Acquire Data				   \ >acquire.opt
&Generate Data				  \ >generate.opt
&Import Data				    \ >import.opt
Import Script				   \ @DfwFileImportScriptAsModule
							    \
Print &Worksheet...	 #Ctrl+W \ @DfwFilePrintWorksheet
P&rint Setup...				 \ @DfwFilePrintSetup
							    \
Recent Files            \ >
    Recent Files        \ @DfwFileList
                \ <
Recent Worksheets        \ >
    Recent worksheets    \ @DfwWorksheetList
                \ <
							    \
E&xit DATS For Windows		  \ @DfwFileExit

From this file we build our menus using the following code:



BOOL CUtilMenu::AddMenu(CMenu &cMenu, CAcceleratorTable
&cAcceleratorTable, CMenuCallbacks *p_cMenuCallbacks, LPCTSTR
lpszFileName, FILE *fp /*=NULL*/)

{

    CStringEx cPathName(CRegistry::GetDatsDirectory() + "lib\\menus\\");

    CStringEx cMenuText("");

    ACCEL Accelerator;

    CStringEx cFunctionName("");

    int        iID;



    TRACE("Loading <%s>\n", lpszFileName );

    // Delete the existing menu (including all sub-menus) first

    DeleteChildMenus(&cMenu, p_cMenuCallbacks);

    // Open the file (may already be open)



    if( !StartOptions(lpszFileName, &fp) )

    {
        AfxMessageBox("Failed to load menu file " + CString(lpszFileName));
        return FALSE;
    }



    while( GetNextOption(cMenuText, &Accelerator, cFunctionName, fp) )
    {
        // Check for a Menu separator
        if(cMenuText == "" && cFunctionName == "")
        {
            cMenu.AppendMenu(MF_SEPARATOR, 0, "");
            continue;
        }

        // Check the 1st character :

        switch(cFunctionName[0])
        {
            // New submenu?          

            case ’>’:
            {
                // Create a new popup menu

                CMenu cNewMenu;
                cNewMenu.CreatePopupMenu();

                CStringEx cFileName = cFunctionName.Right(cFunctionName.GetLength() - 1);

                // Is the new menu in another file ?

                if( !cFileName.IsBlankLine() )
                {

                    if( !CDatsUtil::FileExists(cPathName + cFileName))
                    {

                        TRACE("Warning <%s> could not be found\n",cFileName);

                        continue;             	  

                    }

                    CUtilMenu::AddMenu(cNewMenu, cAcceleratorTable, p_cMenuCallbacks, cFileName);
                }                

                // Otherwise continue reading the menu from the same file

                else
                {
                    CUtilMenu::AddMenu(cNewMenu, cAcceleratorTable, p_cMenuCallbacks, cFileName, fp);

                }                

                cMenu.AppendMenu(MF_POPUP, (unsigned int)cNewMenu.m_hMenu, cMenuText.GetBuffer(0));

                HMENU hMenu = cNewMenu.Detach();

                break;
		    }

            // End of a submenu?            

            case ’<’:
            {
                return TRUE;

		    }

#ifdef _DATSMINI

		    // Internal Script callback?	    

		    case ’@’:
            {

	             cFunctionName = cFunctionName.Right(cFunctionName.GetLength()-1);

                iID = AfxGetCallbackMap()->GetFunctionID(cFunctionName);

                cMenu.AppendMenu(MF_STRING, iID, cMenuText);

                // Add an accelerator to the table if required and if one doesn’t already exist

                if( Accelerator.key != 0 )
                {
                    Accelerator.cmd = iID;
                    cAcceleratorTable.Add(Accelerator);

                }
                break;
            }

#else
		    // A script?		    

		    case ’!’:
            {
                iID = p_cMenuCallbacks->Add(cFunctionName, true);

                cMenu.AppendMenu(MF_STRING, iID, cMenuText.GetBuffer(0));

                // Add an accelerator to the table if required and if one doesn’t already exist

                if( Accelerator.key != 0 )

                {
                    Accelerator.cmd = iID;
                    cAcceleratorTable.Add(Accelerator);
                }

                break;
            }

		    // Internal Script callback?		   

		    case ’@’:
            {
	             cFunctionName = cFunctionName.Right(cFunctionName.GetLength()-1);

                iID = AfxGetCallbackMap()->GetFunctionID(cFunctionName);

                cMenu.AppendMenu(MF_STRING, iID, cMenuText);

                // Add an accelerator to the table if required and if one doesn’t already exist

                if( Accelerator.key != 0 )
                {
                    Accelerator.cmd = iID;
                    cAcceleratorTable.Add(Accelerator);

                }
                break;
            }
            // Must be an analysis                            

            default:
            {
                BOOL bValid = AfxGetModuleDatabase()->IsFunctionValid(cFunctionName);

                iID = p_cMenuCallbacks->Add(cFunctionName, bValid);

                cMenu.AppendMenu(MF_STRING, iID, cMenuText.GetBuffer(0));

                // Add an accelerator to the table if required and if one doesn’t already exist

                if( Accelerator.key != 0 )
                {
                    Accelerator.cmd = iID;
                    cAcceleratorTable.Add(Accelerator);
                }

                break;
            }

#endif

        }

    }



    return TRUE;

}


Okay I don’t seem to have the handle of this editor, but all the above boxes are one function. The GetNextOption() function simply returns 3 ExStrings (our own class) which we build the menus from.




We replace the menus in the main frame as follows:




bool CMainFrame::ReplaceMenus(CMenus* pMenus /*=NULL*/)
{
    // Use the internal set of menus if none specified

    if( pMenus == NULL )
    {
        pMenus = &m_cMenus;
    }

    // Replace the mainframe menu and add new commands to command manager

    CMenu *pNewMenu = pMenus->GetAt(0);
    CMenu *pFrameMenu = GetMenu();
    SetMenu(pNewMenu);

    if(pFrameMenu)
    {
        pFrameMenu->DestroyMenu();
        pFrameMenu = NULL;
    }

    g_CmdManager->UpdateFromMenu(AfxGetApp()->m_pszProfileName, pNewMenu->m_hMenu, true, true, true);

    // Replace document specific menus with new ones

    CDocManager* pDocManager = AfxGetApp ()->m_pDocManager;

    if (pDocManager != NULL)
    {
        // Walk all templates in the application:

        int iMenuIndex = 0;

        for (POSITION pos = pDocManager->GetFirstDocTemplatePosition (); pos != NULL;)
        {
            CMultiDocTemplate* pTemplate = 
                (CMultiDocTemplate*) pDocManager->GetNextDocTemplate (pos);
            ASSERT_VALID (pTemplate);
            ASSERT_KINDOF (CDocTemplate, pTemplate);

            // We are interested in CMultiDocTemplate objects with
            // the shared menu only....

            if( pTemplate->IsKindOf (RUNTIME_CLASS (CMultiDocTemplate)) )
            {
                // Load menubar from supplied menus list and associate it with the
                // template shared menu:

                if( pTemplate->m_hMenuShared != NULL )
                {
                    // Replace menu and add new commands to command manager

                    CMenu *pNewMenu = pMenus->GetAt(iMenuIndex+1);
                    pTemplate->m_hMenuShared = pNewMenu->m_hMenu;
                    pNewMenu->Detach();

                    g_CmdManager->UpdateFromMenu(AfxGetApp()->m_pszProfileName, pTemplate->m_hMenuShared, true, true, true);
                }

                iMenuIndex++;

                // Load an additional menubar from supplied menus list and associate 
                // it with the template in-place server menu if it has one:

                if( pTemplate->m_hMenuInPlaceServer != NULL )
                {
                    // Replace menu and add new commands to command manager

                    CMenu *pNewMenu = pMenus->GetAt(iMenuIndex+1);
                    pTemplate->m_hMenuInPlaceServer = pNewMenu->m_hMenu;
                    pNewMenu->Detach();

                    g_CmdManager->UpdateFromMenu(AfxGetApp()->m_pszProfileName, pTemplate->m_hMenuInPlaceServer, true, true, true);
                }

                // Load an additional menubar from supplied menus list and associate 
                // it with the template embedded server menu if it has one:

                iMenuIndex++;

                if( pTemplate->m_hMenuEmbedding != NULL )
                {
                    // Replace menu and add new commands to command manager

                    CMenu *pNewMenu = pMenus->GetAt(iMenuIndex+1);
                    pTemplate->m_hMenuEmbedding = pNewMenu->m_hMenu;
                    pNewMenu->Detach();

                    g_CmdManager->UpdateFromMenu(AfxGetApp()->m_pszProfileName, pTemplate->m_hMenuEmbedding, true, true, true);
                }

                iMenuIndex++;
            }
        }
    }

    // Cleanup

    for( int i=0; i<pMenus->GetSize(); i++)
    {
        CMenu *pMenu = pMenus->GetAt(i);

        if( pMenu != NULL )
        {
            // Detach from the before deletion (HMENU is now owned by the mainframe or template)

            pMenu->Detach();
            delete pMenu;
        }
    }

    pMenus->RemoveAll();
    
    return true;
}

Finally we do a OnExtMenuPrepare where we update the MRU etc.



LRESULT CMainFrame::OnExtMenuPrepare(WPARAM wParam, LPARAM lParam)
{
    CExtPopupMenuWnd::MsgPrepareMenuData_t *pData =    reinterpret_cast< CExtPopupMenuWnd::MsgPrepareMenuData_t * >(wParam);
     ASSERT( pData != NULL );
 
    CExtPopupMenuWnd * pPopup = pData->m_pPopup;
     ASSERT( pPopup != NULL );

    CExtPopupMenuWnd *pTheMenu = NULL;
    int iReplacePos = -1;

#ifndef _DATSMINI
    // Build the Favorites menu
    // ------------------------------------------

    if( FindMenuItemFromCmdID(pPopup, ID_FAVORITES_LIST, pTheMenu, iReplacePos) ) 
    { 
        // Remove placeholder so that user doesn’t see it

        pTheMenu->ItemRemove(iReplacePos);

        // Insert the current contents of the Favorites.xml file

        BuildFavorites(pTheMenu, iReplacePos);

        pData->m_bMenuChanged = true;
        
        return 1L;
    }

    // Append worksheet MRU list to the File menu
    // ------------------------------------------

    // Look for a worksheet MRU placeholder command in the menu or one of its sub-menus
     
    if( FindMenuItemFromCmdID(pPopup, ID_FILE_WORKSHEET_MRU_FILE1, pTheMenu, iReplacePos) ) 
    { 
       // Remove placeholder so that user doesn’t see it
     
       pTheMenu->ItemRemove(iReplacePos);
     
       // Insert the current contents of the worksheet MRU list
     
        CRecentFileList *pRecentFileList = AfxGetApp()->GetRecentWorksheetList();

        if( pRecentFileList != NULL )
        { 
            TCHAR sCurrDir[_MAX_PATH+1];
            ::memset(sCurrDir,0,sizeof(sCurrDir));
            ::GetCurrentDirectory(_MAX_PATH,sCurrDir);
            int nLenCurDir = (int)_tcslen(sCurrDir);

            for( int nItemIndex=0; nItemIndex<pRecentFileList->GetSize(); nItemIndex++ )
            {
                // Fetch next name from the MRU list

                CString cDisplayName( _T("") );

                if(    !pRecentFileList->GetDisplayName( cDisplayName, nItemIndex, sCurrDir, nLenCurDir, TRUE)    )
                    continue;

                ASSERT( !cDisplayName.IsEmpty() );

                // Add an entry to the command manager if not already present

                UINT nCmdID = ID_FILE_WORKSHEET_MRU_FILE1 + nItemIndex;
                ASSERT( nCmdID <= ID_FILE_WORKSHEET_MRU_FILE16 );

                CExtCmdItem *pCmdItem =g_CmdManager->CmdGetPtr(g_CmdManager->ProfileNameFromWnd( GetSafeHwnd() ), nCmdID);

                if( pCmdItem == NULL )
                    pCmdItem = g_CmdManager->CmdAllocPtr( g_CmdManager->ProfileNameFromWnd( GetSafeHwnd() ), nCmdID );

                ASSERT( pCmdItem != NULL );

                if( pCmdItem == NULL )
                    return 0L;

                // Format menu text

                int nDisplayIndex = nItemIndex+1;
                if( nDisplayIndex < 10 )
                    pCmdItem->m_sMenuText.Format(_T("&%d %s"), nDisplayIndex, cDisplayName);
                else
                    pCmdItem->m_sMenuText.Format(_T("%d&%d %s"), nDisplayIndex/10, nDisplayIndex%10, cDisplayName);
                
                CString cRecentFileFmt1( _T("Recent worksheet %d") );
                CString cRecentFileFmt2( _T("Recent worksheet %d (\"%s\")") );

                pCmdItem->m_sToolbarText.Format(cRecentFileFmt1, nItemIndex + 1);
                pCmdItem->m_sTipTool.Format(cRecentFileFmt2, nItemIndex + 1, cDisplayName);
                pCmdItem->m_sTipStatus = pCmdItem->m_sTipTool;

                if( !pTheMenu->ItemInsert(nCmdID, iReplacePos + nItemIndex) )
                {
                    ASSERT( FALSE );
                    return 0L;
                }
            }
        }
        pData->m_bMenuChanged = true;
    }

    // Add "Windows..." command
    // -------------------------

    int iNewPos = -1;
    
    int iItemPos = pPopup->ItemFindPosForCmdID( __ID_MDIWND_DLGWINDOWS );

    if( iItemPos > 0 )
    {
        // "More Windows..." command found
        pPopup->ItemRemove( iItemPos );
        iNewPos = iItemPos;
        pData->m_bMenuChanged = true;
    }
    else
    {
        int nMaxCmdID = 0;
        for( int nCmdID = __ID_MDIWNDLIST_FIRST; nCmdID <= __ID_MDIWNDLIST_LAST; nCmdID++ ){
            iItemPos = pPopup->ItemFindPosForCmdID( nCmdID );
            if( iItemPos > 0 )
            {
                if( nCmdID > nMaxCmdID )
                {
                    nMaxCmdID = nCmdID;
                    iNewPos = iItemPos;
                }
            }
            pData->m_bMenuChanged = true;
        }
        if( iNewPos > 0 )
        {
            pPopup->ItemInsert(
                (UINT)CExtPopupMenuWnd::TYPE_SEPARATOR,
                ++iNewPos
                );
            iNewPos++;
            pData->m_bMenuChanged = true;
        }
    }
    if( iNewPos > 0 )
    {
        UINT nCmdID = ID_WINDOWS_LIST;
        CExtCmdItem * pCmdItem =
            g_CmdManager->CmdGetPtr(
            g_CmdManager->ProfileNameFromWnd( this->GetSafeHwnd() ),
            nCmdID
            );
        if( pCmdItem == NULL ){
            pCmdItem = 
                g_CmdManager->CmdAllocPtr( 
                g_CmdManager->ProfileNameFromWnd( this->GetSafeHwnd() ), 
                nCmdID 
                );
        }
        ASSERT( pCmdItem != NULL );
        if( pCmdItem != NULL )
        {
            CExtSafeString sMoreWindows(_T("Windows..."));
            CExtSafeString sManageWindows(_T("Manages the currently open windows"));
            pCmdItem->m_sMenuText = sMoreWindows;
            pCmdItem->m_sToolbarText = pCmdItem->m_sMenuText;
            pCmdItem->m_sTipTool = sManageWindows;
            pCmdItem->m_sTipStatus = pCmdItem->m_sTipTool;
            pCmdItem->StateSetBasic( true );
            
            pPopup->ItemInsert(
                nCmdID,
                iNewPos,
                sMoreWindows,
                NULL,
                m_hWnd
                );
        }
        pData->m_bMenuChanged = true;
    }
    
#endif

    return 1L;;
}

These are the main places where the menus are created and updates. Hopefully you can spot some problem that we haven’t seen. If you need more info feel please free to ask.


 


Thanks


 


 

Technical Support Nov 20, 2009 - 2:38 PM

No, you can switch to the latest Prof-UIS and re-check this issue. It should not appear.

Technical Support Nov 19, 2009 - 1:47 PM

This problem is absent in 2.60 too. It does not depend on the dynamic menu initialization. The only case when the first menu item can be non-selected is when your code removes all the menu items. Please try to set breakpoints into the beginning of the CExtPopupMenuWnd::_ItemFocusSet() and CExtPopupMenuWnd::_ItemFocusCancel() methods and you will see whether popup menu tries to set focus to the first displayed menu item or whether it cancels it and why.



Technical Support Nov 18, 2009 - 6:51 AM

We used Prof-UIS 2.87 for testing this issue. We run the MDIDOCVIEW sample application. We did the following two tests:

1) We pressed Alt, then pressed F without releasing Alt, then released both keys.

2) We pressed Alt, then released Alt, the pressed F, then released F.

Result was the same in both tests. The file menu appeared. The first menu item is selected in it.