WxMenu
From WxWiki
Contents |
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 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, (wxObjectEventFunction)&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") );