[RESOLVED] Trouble with MDI GetSubMenu
I have a MDI program that is setup to support multiple languages. This app is configured to allow the operator to swap the language on-the-fly and all the menus/dialogs/etc will automatically switch to the newly selected language.
There are multiple types of document / view classes, and each class has its own menu. Also, I have a function that will return the translation (for the selected language) that is keyed off of the operator specified language.
Everything works fine when I have only 1 document open, but if there are 2 documents, then the program crashes (because GetSubMenu returns a NULL) when I switch to the 2nd document.
Each CScrollView type that I have uses a function such as OnActivateView bellow...
Code:
void CDocType1View::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
if (bActivate)
MainFramePtr->ShowType1Tools(); // changes the toolbar just bellow the menu
CScrollView::OnActivateView(bActivate, pActivateView, pDeactiveView);
if (bActivate && (nPrevLang != larray.nLangIdx))
RebuildMenu();
}
nPrevLang is a private int variable for CDocType1View
larray is a structure containing all my translations, a function "L" that return the translated text, and nLangIdx which keeps track of the selected language
The problem is in RebuildMenu(), when I first assign submenu2 (in the "Edit" section)...
Code:
void CDocType1View::RebuildMenu()
{
if (nPrevLang == larray.nLangIdx)
return;
nPrevLang = larray.nLangIdx;
CMenu *pMenu = AfxGetApp()->m_pMainWnd->GetMenu(),
*submenu = NULL,
*submenu2 = NULL,
*submenu3 = NULL,
*submenu4 = NULL;
CString szTemp;
// File
submenu = pMenu->GetSubMenu(0);
szTemp = larray.L(_T("&File"));
pMenu->ModifyMenu(0, MF_BYPOSITION, MF_STRING, szTemp);
szTemp = larray.L(_T("&Open...\tCtrl O"));
submenu->ModifyMenu(ID_FILE_OPEN, MF_BYCOMMAND, ID_FILE_OPEN, szTemp);
szTemp = larray.L(_T("&Print...\tCtrl P"));
submenu->ModifyMenu(ID_FILE_PRINT, MF_BYCOMMAND, ID_FILE_PRINT, szTemp);
szTemp = larray.L(_T("Print Pre&view"));
submenu->ModifyMenu(ID_FILE_PRINT_PREVIEW, MF_BYCOMMAND, ID_FILE_PRINT_PREVIEW, szTemp);
szTemp = larray.L(_T("P&rint Setup..."));
submenu->ModifyMenu(ID_FILE_PRINT_SETUP, MF_BYCOMMAND, ID_FILE_PRINT_SETUP, szTemp);
szTemp = larray.L(_T("E&xit"));
submenu->ModifyMenu(ID_APP_EXIT, MF_BYCOMMAND, ID_APP_EXIT, szTemp);
// Edit
submenu = pMenu->GetSubMenu(1);
szTemp = larray.L(_T("&Edit"));
pMenu->ModifyMenu(1, MF_BYPOSITION, MF_STRING, szTemp);
szTemp = larray.L(_T("Change Co&mments\tQ"));
submenu->ModifyMenu(ID_EDIT_CHANGECOMMENTS, MF_BYCOMMAND, ID_EDIT_CHANGECOMMENTS, szTemp);
submenu2 = submenu->GetSubMenu(1);
// this is where I'm failing on the 2nd document initialization, GetSubMenu return NULL
szTemp = larray.L(_T("Grouping"));
submenu->ModifyMenu(1, MF_BYPOSITION, MF_STRING, szTemp);
szTemp = larray.L(_T("Group 1"));
submenu2->ModifyMenu(ID_EDIT_GROUP1, MF_BYCOMMAND, ID_EDIT_GROUP1, szTemp);
szTemp = larray.L(_T("Group 2"));
submenu2->ModifyMenu(ID_EDIT_GROUP2, MF_BYCOMMAND, ID_EDIT_GROUP2, szTemp);
submenu2 = submenu->GetSubMenu(2);
szTemp = larray.L(_T("Typing"));
submenu->ModifyMenu(2, MF_BYPOSITION, MF_STRING, szTemp);
szTemp = larray.L(_T("Type 1"));
submenu2->ModifyMenu(ID_EDIT_TYPE1, MF_BYCOMMAND, ID_EDIT_TYPE1, szTemp);
szTemp = larray.L(_T("Type 2"));
submenu2->ModifyMenu(ID_EDIT_TYPE2, MF_BYCOMMAND, ID_EDIT_TYPE2, szTemp);
// ... I have clipped the rest of the code between the "Edit->Typing" through "Help", but it is very similar in style and substance. There are more cascades of menus that require me to use 4 submenu pointers.
// Help
submenu = pMenu->GetSubMenu(5);
szTemp = larray.L(_T("&Help"));
pMenu->ModifyMenu(5, MF_BYPOSITION, MF_STRING, szTemp);
szTemp = larray.L(_T("&Help Topics"));
submenu->ModifyMenu(ID_HELP_FINDER, MF_BYCOMMAND, ID_HELP_FINDER, szTemp);
szTemp = larray.L(_T("&About Display..."));
submenu->ModifyMenu(ID_APP_ABOUT, MF_BYCOMMAND, ID_APP_ABOUT, szTemp);
}
Can anyone tell me why this is failing when I switch to my 2nd document / view?
Re: Trouble with MDI GetSubMenu
I'd suggest you to redesign...
It's a very bad idea to hard code the texts (not only for menus!). You should just load the menu for appropriate language and destroy the existent one after you changed the language.
Re: Trouble with MDI GetSubMenu
The translations are not hard coded. There is a database that the operator is able to modify so that they can have their own "custom" translations if need be. Because of that, I can't really create separate resources for different languages... there are far too many possibilities and I would have to re-deploy any time someone wanted to tweak their wording.
Re: Trouble with MDI GetSubMenu
If it were me then, I'd probably set up the CCmdUI update mechanism, call GetText() when it's about to display, look up the value in the database then use SetText() to display the command in the correct language.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
vandy_mike
Each CScrollView type that I have uses a function such as OnActivateView bellow...
Code:
void CDocType1View::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
if (bActivate)
MainFramePtr->ShowType1Tools(); // changes the toolbar just bellow the menu
CScrollView::OnActivateView(bActivate, pActivateView, pDeactiveView);
if (bActivate && (nPrevLang != larray.nLangIdx))
RebuildMenu();
}
Why don't you check the view to be activated? (it sounds like you are trying to RebuildMenu for the deactivated view too!)
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
GCDEF
If it were me then, I'd probably set up the CCmdUI update mechanism, call GetText() when it's about to display, look up the value in the database then use SetText() to display the command in the correct language.
Wouldn't that just update the text once you mouse over? I want the text to change as soon as the operator changes from English -> Spanish (etc.), not when they actually go through the menu system.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
vandy_mike
Wouldn't that just update the text once you mouse over? I want the text to change as soon as the operator changes from English -> Spanish (etc.), not when they actually go through the menu system.
It would change the text whenever the menu item was about to be displayed. It's not necessary to change the text on items they can't see. All you'd need to do is have the correct database or translation table in place and the OnUpdate handler would ensure the correct text was displayed whenever necessary.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
VictorN
Why don't you check the view to be activated? (it sounds like you are trying to RebuildMenu for the deactivated view too!)
Isn't this what I'm doing in "OnActivateView" right before the call to "RebuildMenu"? I would think that the condition of bActivate == TRUE would take care of this.
Quote:
Originally Posted by
vandy_mike
Code:
void CDocType1View::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
if (bActivate)
MainFramePtr->ShowType1Tools(); // changes the toolbar just bellow the menu
CScrollView::OnActivateView(bActivate, pActivateView, pDeactiveView);
if (bActivate && (nPrevLang != larray.nLangIdx))
RebuildMenu();
}
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
vandy_mike
Isn't this what I'm doing in "OnActivateView" right before the call to "RebuildMenu"? I would think that the condition of bActivate == TRUE would take care of this.
No.
You should also check the (pActivateView == this) condition.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
VictorN
No.
You should also check the (pActivateView == this) condition.
This had no impact at all. It looks like my check to bActivate was probably sufficient... but I'm still getting a crash.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
GCDEF
It would change the text whenever the menu item was about to be displayed. It's not necessary to change the text on items they can't see. All you'd need to do is have the correct database or translation table in place and the OnUpdate handler would ensure the correct text was displayed whenever necessary.
so then you would suggest changing ALL of the functions such as...
Code:
void CDocType1View::OnUpdateEditGroup1(CCmdUI *pCmdUI)
{
CMenu *pMenu = AfxGetApp()->m_pMainWnd->GetMenu();
pCmdUI->Enable(bAllowGrouping);
pMenu->CheckMenuItem(ID_EDIT_GROUP1, ((bAllowGrouping && bIsGroup1) ? MF_CHECKED : MF_UNCHECKED));
}
to...
Code:
void CDocType1View::OnUpdateEditGroup1(CCmdUI *pCmdUI)
{
CString szCurrentText;
CMenu *pMenu = AfxGetApp()->m_pMainWnd->GetMenu();
pCmdUI->Enable(bAllowGrouping);
pMenu->GetMenuString(ID_EDIT_GROUP1, szCurrentText, 255, MF_BYCOMMAND);
szCurrentText = larray.L(szCurrentText);
// either
pCmdUI->SetText(szCurrentText);
// or
pMenu->ModifyMenu(ID_EDIT_GROUP1, MF_BYCOMMAND, ID_EDIT_GROUP1, szCurrentText);
pMenu->CheckMenuItem(ID_EDIT_GROUP1, ((bAllowGrouping && bIsGroup1) ? MF_CHECKED : MF_UNCHECKED));
}
I think this would only update when the operator went to that section of the menu. So the menu would be a strange combination of English / Spanish (etc.) until the operator had moused-over each item.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
vandy_mike
so then you would suggest changing
ALL of the functions such as...
Code:
void CDocType1View::OnUpdateEditGroup1(CCmdUI *pCmdUI)
{
CMenu *pMenu = AfxGetApp()->m_pMainWnd->GetMenu();
pCmdUI->Enable(bAllowGrouping);
pMenu->CheckMenuItem(ID_EDIT_GROUP1, ((bAllowGrouping && bIsGroup1) ? MF_CHECKED : MF_UNCHECKED));
}
to...
Code:
void CDocType1View::OnUpdateEditGroup1(CCmdUI *pCmdUI)
{
CString szCurrentText;
CMenu *pMenu = AfxGetApp()->m_pMainWnd->GetMenu();
pCmdUI->Enable(bAllowGrouping);
pMenu->GetMenuString(ID_EDIT_GROUP1, szCurrentText, 255, MF_BYCOMMAND);
szCurrentText = larray.L(szCurrentText);
// either
pCmdUI->SetText(szCurrentText);
// or
pMenu->ModifyMenu(ID_EDIT_GROUP1, MF_BYCOMMAND, ID_EDIT_GROUP1, szCurrentText);
pMenu->CheckMenuItem(ID_EDIT_GROUP1, ((bAllowGrouping && bIsGroup1) ? MF_CHECKED : MF_UNCHECKED));
}
I think this would only update when the operator went to that section of the menu. So the menu would be a strange combination of English / Spanish (etc.) until the operator had moused-over each item.
Not really. You seem to be missing that the OnUpdate handler is called before the menu is displayed so you could change the text to whatever you want before the user sees it.
I would just add a line like
Code:
pCmdUI->SetText(TranslateText(pCmdUI->GetText());
where TranslateText would be a function that does the lookup and translation in your app.
Re: Trouble with MDI GetSubMenu
The only problem with CCmdUI update mechanism is it won't update the menu bar items.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
GCDEF
Not really. You seem to be missing that the OnUpdate handler is called before the menu is displayed so you could change the text to whatever you want before the user sees it.
I would just add a line like
Code:
pCmdUI->SetText(TranslateText(pCmdUI->GetText());
where TranslateText would be a function that does the lookup and translation in your app.
First...
pCmdUI->GetText() does not exist... so I would still have to do something like GetMenuString.
Second...
That doesn't help me for "pop-up" menus, only the items in the pop-up.
The root problem is that I can't access my "nested" pop-up menus.
For instance, if there is a menu pop-up item called "Operations" as a subset of "Edit", and there is a menu pop-up item called "Math" inside operations... and Math has 2 menu items called "Add" (ID_OPER_MATH_ADD) and "Subtract" (ID_OPER_MATH_SUB)...
Edit -> Operations -> Math -> Add
Edit -> Operations -> Math -> Subtract
I can translate these using ModifyMenu from my originally posted code, such as...
Code:
szTemp = larray.L(_T("Add"));
pMenu->ModifyMenu(ID_OPER_MATH_ADD, MF_BYCOMMAND, ID_OPER_MATH_ADD, szTemp);
//pMenu is OK whenever using MF_BYCOMMAND
And I can translate Edit such as...
Code:
szTemp = larray.L(_T("&Edit"));
pMenu->ModifyMenu(1, MF_BYPOSITION, MF_STRING, szTemp);
//using pMenu so no problem
And I can translate Operations such as...
Code:
submenu = pMenu->GetSubMenu(1);// Edit submenu
szTemp = larray.L(_T("Operations"));
submenu->ModifyMenu(0, MF_BYPOSITION, MF_STRING, szTemp);
//succeeds at getting the submenu of pMenu
But I fail (2nd document/view) when I try to translate Math...
Code:
submenu2 = submenu ->GetSubMenu(0);// Operations submenu
szTemp = larray.L(_T("Math"));
submenu2->ModifyMenu(0, MF_BYPOSITION, MF_STRING, szTemp);
//fails to get submenu2 from the pointer to submenu
As I said, this all works correctly for the 1st document/view, but when I change to the 2nd window that I have open... my application crashes.
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
vandy_mike
...
As I said, this all works correctly for the 1st document/view, but when I change to the 2nd window that I have open... my application crashes.
Perhaps, because the 2nd document uses another menu? :cool:
Re: Trouble with MDI GetSubMenu
Quote:
Originally Posted by
VictorN
Perhaps, because the 2nd document uses another menu? :cool:
Since I'm getting a pointer for the main window at the beginning...
Quote:
Code:
CMenu *pMenu = AfxGetApp()->m_pMainWnd->GetMenu()
I would imagine that this means I'm re-using the same menu. Either way, I don't see why I can sometimes access the submenu of a submenu... but other times I cannot.
Re: Trouble with MDI GetSubMenu
The mainframe menu is usually reloaded by change the document.
Re: Trouble with MDI GetSubMenu
For what it's worth... to anyone else who runs into this problem...
This issue is when I try to access the submenus using MF_BYPOSITION. The cause of this problem is that when any MDI child window was created, I do NOT have the icon to the left of the "File" menu. However, one I open a 2nd child window and navigate between the 2... now both documents have the icon to the left of the "File" menu.
This icon is what allows me to select things like minimize, maximize and move.
By putting in a simple line to check to see if submenu #0 has a string value or is an icon, I know if I need to treat the "File" menu as submenu #0 or #1.
Easy fix, hard to find.
Code:
int nOffset = 0;
CString szTemp;
GetMenuString(0, szTemp, MF_BYPOSITION);
if (szTemp.IsEmpty())
nOffset++;
CMenu* pSubMenu = GetSubMenu(0 + nOffset); // this should ALWAYS get me to the "File" menu
Re: [RESOLVED] Trouble with MDI GetSubMenu
This way is not good.
1. you should check whether the child window is maximized (then the first menu item is an "icon") or not.
2. You should not hard code the menu item strings, i.e. you should not expect that the first menu would be "File", the second - "Edit" and so on. The better way - to find the menu that corresponds to "File" using some menu item IDs that are always included in that menu (like ID_FILE_OPEN, ID_FILE_CLOSE and so on). A good example is the MFC CMDIFrameWnd::GetWindowMenuPopup method.
Re: [RESOLVED] Trouble with MDI GetSubMenu
I do see the merit in what you are suggesting, but in my case - the menu arrangement is static. I don't need to worry about the "File" menu being 1st or 3rd. My only requirement is that I'm doing a real-time substitution of text in the menu dependent on the operator specific language. So using code that returns the location of the "File" menu as 1 doesn't really help me any more than knowing that it is in location 0 + 1 if the item in location 0 has a string length of 0.
And in terms of sub-menus, this solution would not work if the only items in a sub-menu are also sub-menus. Granted... once you know the index location of "File", the sub-menu of sub-menu issue is irrelevant because you know have the index of that original sub-menu from doing the lookup function.
Anyway... for my purposes, this option is more than what I need... but thank you for your suggestions.