wxMenu
Official Classes | Archive • Containers • Controls • Data Structures • Database • Date & Time • Debug • Device Contexts • Dialogs • Document & Views • Drag & Drop • Events • Filesystem • Frames • Graphics • Grid Cell • Help • HTML • Logging • Miscellaneous • Networking • Printing • Sizers • Streams • Threading • Windows |
A menu is a popup (or pull down) list of items, one of which may be selected before the menu goes away (clicking elsewhere dismisses the menu).
Menus may be used to construct either menu bars or popup menus.
A menu item has an integer ID associated with it which can be used to identify the selection, or to change the menu item in some way. A menu item with a special identifier wxID_SEPARATOR is a separator item and doesn't have an associated command but just makes a separator line appear in the menu.
Note: Please note that wxID_ABOUT and wxID_EXIT are predefined by wxWidgets and have a special meaning since entries using these IDs will be taken out of the normal menus under MacOS X and will be inserted into the system menu (following the appropriate MacOS X interface guideline).
Menu items may be either normal items, check items or radio items. Normal items don't have any special properties while the check items have a boolean flag associated to them and they show a checkmark in the menu when the flag is set. wxWidgets automatically toggles the flag value when the item is clicked and its value may be retrieved using either wxMenu::IsChecked method of wxMenu or wxMenuBar itself or by using wxEvent::IsChecked when you get the menu notification for the item in question.
The radio items are similar to the check items except that all the other items in the same radio group are unchecked when a radio item is checked. The radio group is formed by a contiguous range of radio items, i.e. it starts at the first item of this kind and ends with the first item of a different kind (or the end of the menu). Notice that because the radio groups are defined in terms of the item positions inserting or removing the items in the menu containing the radio items risks to not work correctly.
Or right-click menus: you can create a popup menu (a menu that isn't attached to any menubar) using the 'PopupMenu' member function of a wxWindow.
This is demonstrated in the 'taskbar' sample, together with how to attach it to a system tray icon.
In wxWidgets 2.6.x the menu, richedit, listctrl and treectrl samples each demonstrate use of the wxWindow::PopupMenu function.
In this example the client data from a wxListCtrl's right-clicked item is passed to the menu. It's monolithic, it doesn't need any sort of previous setup (with the exception that the list right-click handler should be connected to the proper event)
#define ID_SOMETHING 2001
#define ID_SOMETHING_ELSE 2002
void MyFrame::OnPopupClick(wxCommandEvent &evt)
{
void *data=static_cast<wxMenu *>(evt.GetEventObject())->GetClientData();
switch(evt.GetId()) {
case ID_SOMETHING:
break;
case ID_SOMETHING_ELSE:
break;
}
}
void MyFrame::OnListRightClick(wxListEvent &evt)
{
void *data = reinterpret_cast<void *>(evt.GetItem().GetData());
wxMenu mnu;
mnu.SetClientData( data );
mnu.Append(ID_SOMETHING, "Do something");
mnu.Append(ID_SOMETHING_ELSE, "Do something else");
mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyFrame::OnPopupClick), NULL, this);
PopupMenu(&mnu);
}
If you are using wxXRC for resources, and you then remove some items from the menu (say for example you compiled those features out of the app, and you want the menu items for those features gone too), then you can be left with menu separators at the bottom of a menu, at the top of a menu, or multiple separators in a row. Here is a function that will search through a menubar and remove these cruft separators. Just send it a pointer to the menubar that you want to be cleaned:
void strip_excess_separators_from_menubar( wxMenuBar* target_menubar )
{
// Index of the menu in the menubar
size_t current_menu_index;
wxMenu* current_menu;
// The current menuitem's node in the list of menuitems.
// [See 'wxNode' in wxWidgets class reference for a list of possible methods.]
wxMenuItemList::Node* current_menuitem_node;
// The current menuitem, as extracted from the node.
wxMenuItem* current_menuitem;
for ( current_menu_index = 0; current_menu_index < target_menubar->GetMenuCount(); current_menu_index++ ) {
// Get the current menu.
current_menu = target_menubar->GetMenu( current_menu_index );
// Loop through the menuitemnodes on this menu, from bottom to top. Going bottom
// to top as the menuitem index numbers will shift to smaller numbers as menuitems are deleted.
current_menuitem_node = current_menu->GetMenuItems().GetLast();
while ( current_menuitem_node ) {
current_menuitem = current_menuitem_node->GetData();
// If a separator...
if ( current_menuitem->IsSeparator() ) {
// ..and it is....
// ...the last menuitem in the menu....
if ( ! current_menuitem_node->GetNext()
// ...or the first menuitem in the menu...
|| ! current_menuitem_node->GetPrevious()
// ...or the menuitem prior to it in the menu is also a separator...
|| current_menuitem_node->GetPrevious()->GetData()->IsSeparator() ) {
// ...then delete it.
current_menu->Delete( current_menuitem );
}
}
current_menuitem_node = current_menuitem_node->GetPrevious();
}
}
}
The statement "current_menuitem_node = current_menu->GetMenuItems().GetLast();" causes a crash after a menu separator is deleted because GetPrevious() gets an invalid pointer from the deleted menu item. Allocating a local wxMenuItemList for the node pointer will solve the problem, eg.,
wxMenuItemList menuItemsList = current_menu->GetMenuItems(); current_menuitem_node = menuItemsList.GetLast();
Adding Menu Accelerators
Not sure about other platforms, but this works in MS-Windows and Linux-GTK. To add an accelerator for a menu item, just put a tab in the name and add the accelerator name. For example:
wxMenu* EditMenu = new wxMenu; EditMenu->Append(wxID_COPY, wxT("&Copy\tCtrl+C"));
That's it! You don't need to set up a separate accelerator table, as wxMenu does it for you. Easy, huh?
Underlining accelerators on Windows
On Windows, the accelerator keys are only underlined if you open the menu with ALT. You can disable this behaviour by right-clicking on the Desktop, go to the page where you can change the Theme, click Effects, it's the last checkbox.
Suppose we want to create two sets of radio groups in the same menu, with a separator in between. Like this:
o Normal Bold ----------- Normal o Italic Oblique
This is not possible, since the two groups are merged into one (bug/feature?).
To actually create two groups, one should add an additional line when the separator is added:
menu->AppendRadioItem( ID_NORMAL_WEIGHT, wxT("Normal") );
menu->AppendRadioItem( ID_BOLD_WEIGHT, wxT("Bold") );
;
menu->AppendSeparator();
// The following line is important
menu->Remove( menu->Append( -1, wxEmptyString ) );
;
menu->AppendRadioItem( ID_NORMAL_SLANT, wxT("Normal") );
menu->AppendRadioItem( ID_ITALIC_SLANT, wxT("Italic") );
menu->AppendRadioItem( ID_OBLIQUE_SLANT, wxT("Oblique") );