WxMenu

From WxWiki

Revision as of 23:31, 14 November 2009 by 186.136.155.200 (Talk)
Jump to: navigation, search

Contents

PopUp-menus

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.

Example of parameter passing to a popup menu

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);
}

Removing unneeded menu separators

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.

Radio Items in a menu

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") );
Personal tools