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