1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/menu.cpp 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // For compilers that support precompilation, includes "wx.h". 
  11 #include "wx/wxprec.h" 
  19     #include "wx/bitmap.h" 
  26 #include "wx/gtk/private.h" 
  28 #include <gdk/gdkkeysyms.h> 
  30 // FIXME: is this right? somehow I don't think so (VZ) 
  32 #define gtk_accel_group_attach(g, o) gtk_window_add_accel_group((o), (g)) 
  33 //#define gtk_accel_group_detach(g, o) gtk_window_remove_accel_group((o), (g)) 
  34 //#define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m) 
  36 #define ACCEL_OBJECT        GtkWindow 
  37 #define ACCEL_OBJECTS(a)    (a)->acceleratables 
  38 #define ACCEL_OBJ_CAST(obj) ((GtkWindow*) obj) 
  40 // we use normal item but with a special id for the menu title 
  41 static const int wxGTK_TITLE_ID 
= -3; 
  43 //----------------------------------------------------------------------------- 
  45 //----------------------------------------------------------------------------- 
  48 static wxString 
GetGtkHotKey( const wxMenuItem
& item 
); 
  51 //----------------------------------------------------------------------------- 
  53 //----------------------------------------------------------------------------- 
  55 static wxString 
wxReplaceUnderscore( const wxString
& title 
) 
  59     // GTK 1.2 wants to have "_" instead of "&" for accelerators 
  62     while (*pc 
!= wxT('\0')) 
  64         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
  66             // "&" is doubled to indicate "&" instead of accelerator 
  70         else if (*pc 
== wxT('&')) 
  76             if ( *pc 
== wxT('_') ) 
  78                 // underscores must be doubled to prevent them from being 
  79                 // interpreted as accelerator character prefix by GTK 
  88     // wxPrintf( wxT("before %s after %s\n"), title.c_str(), str.c_str() ); 
  93 //----------------------------------------------------------------------------- 
  94 // activate message from GTK 
  95 //----------------------------------------------------------------------------- 
  97 static void DoCommonMenuCallbackCode(wxMenu 
*menu
, wxMenuEvent
& event
) 
 100         wxapp_install_idle_handler(); 
 102     event
.SetEventObject( menu 
); 
 104     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 105     if (handler 
&& handler
->ProcessEvent(event
)) 
 108     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 110         win
->GetEventHandler()->ProcessEvent( event 
); 
 115 static void gtk_menu_open_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 117     wxMenuEvent 
event(wxEVT_MENU_OPEN
, -1, menu
); 
 119     DoCommonMenuCallbackCode(menu
, event
); 
 122 static void gtk_menu_close_callback( GtkWidget 
*widget
, wxMenuBar 
*menubar 
) 
 124     if ( !menubar
->GetMenuCount() ) 
 126         // if menubar is empty we can't call GetMenu(0) below 
 130     wxMenuEvent 
event( wxEVT_MENU_CLOSE
, -1, NULL 
); 
 132     DoCommonMenuCallbackCode(menubar
->GetMenu(0), event
); 
 137 //----------------------------------------------------------------------------- 
 139 //----------------------------------------------------------------------------- 
 141 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
) 
 143 void wxMenuBar::Init(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long style
) 
 145     // the parent window is known after wxFrame::SetMenu() 
 146     m_needParent 
= false; 
 148     m_invokingWindow 
= (wxWindow
*) NULL
; 
 150     if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize 
) || 
 151         !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") )) 
 153         wxFAIL_MSG( wxT("wxMenuBar creation failed") ); 
 157     m_menubar 
= gtk_menu_bar_new(); 
 159     if (style 
& wxMB_DOCKABLE
) 
 161         m_widget 
= gtk_handle_box_new(); 
 162         gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) ); 
 163         gtk_widget_show( GTK_WIDGET(m_menubar
) ); 
 167         m_widget 
= GTK_WIDGET(m_menubar
); 
 174     for (size_t i 
= 0; i 
< n
; ++i 
) 
 175         Append(menus
[i
], titles
[i
]); 
 177     // VZ: for some reason connecting to menus "deactivate" doesn't work (we 
 178     //     don't get it when the menu is dismissed by clicking outside the 
 179     //     toolbar) so we connect to the global one, even if it means that we 
 180     //     can't pass the menu which was closed in wxMenuEvent object 
 181     g_signal_connect (m_menubar
, "deactivate", 
 182                       G_CALLBACK (gtk_menu_close_callback
), this); 
 186 wxMenuBar::wxMenuBar(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long style
) 
 188     Init(n
, menus
, titles
, style
); 
 191 wxMenuBar::wxMenuBar(long style
) 
 193     Init(0, NULL
, NULL
, style
); 
 196 wxMenuBar::wxMenuBar() 
 198     Init(0, NULL
, NULL
, 0); 
 201 wxMenuBar::~wxMenuBar() 
 205 static void wxMenubarUnsetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 207     menu
->SetInvokingWindow( (wxWindow
*) NULL 
); 
 209     wxWindow 
*top_frame 
= win
; 
 210     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 211         top_frame 
= top_frame
->GetParent(); 
 213     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 216         wxMenuItem 
*menuitem 
= node
->GetData(); 
 217         if (menuitem
->IsSubMenu()) 
 218             wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 219         node 
= node
->GetNext(); 
 223 static void wxMenubarSetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 225     menu
->SetInvokingWindow( win 
); 
 227     wxWindow 
*top_frame 
= win
; 
 228     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 229         top_frame 
= top_frame
->GetParent(); 
 231     // support for native hot keys 
 232     ACCEL_OBJECT 
*obj 
= ACCEL_OBJ_CAST(top_frame
->m_widget
); 
 233     if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj 
) ) 
 234         gtk_accel_group_attach( menu
->m_accel
, obj 
); 
 236     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 239         wxMenuItem 
*menuitem 
= node
->GetData(); 
 240         if (menuitem
->IsSubMenu()) 
 241             wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 242         node 
= node
->GetNext(); 
 246 void wxMenuBar::SetInvokingWindow( wxWindow 
*win 
) 
 248     m_invokingWindow 
= win
; 
 249     wxWindow 
*top_frame 
= win
; 
 250     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 251         top_frame 
= top_frame
->GetParent(); 
 253     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 256         wxMenu 
*menu 
= node
->GetData(); 
 257         wxMenubarSetInvokingWindow( menu
, win 
); 
 258         node 
= node
->GetNext(); 
 262 void wxMenuBar::UnsetInvokingWindow( wxWindow 
*win 
) 
 264     m_invokingWindow 
= (wxWindow
*) NULL
; 
 265     wxWindow 
*top_frame 
= win
; 
 266     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 267         top_frame 
= top_frame
->GetParent(); 
 269     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 272         wxMenu 
*menu 
= node
->GetData(); 
 273         wxMenubarUnsetInvokingWindow( menu
, win 
); 
 274         node 
= node
->GetNext(); 
 278 bool wxMenuBar::Append( wxMenu 
*menu
, const wxString 
&title 
) 
 280     if ( !wxMenuBarBase::Append( menu
, title 
) ) 
 283     return GtkAppend(menu
, title
); 
 286 bool wxMenuBar::GtkAppend(wxMenu 
*menu
, const wxString
& title
, int pos
) 
 288     wxString 
str( wxReplaceUnderscore( title 
) ); 
 290     // This doesn't have much effect right now. 
 291     menu
->SetTitle( str 
); 
 293     // The "m_owner" is the "menu item" 
 294     menu
->m_owner 
= gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str 
) ); 
 296     gtk_widget_show( menu
->m_owner 
); 
 298     gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu 
); 
 301         gtk_menu_shell_append( GTK_MENU_SHELL(m_menubar
), menu
->m_owner 
); 
 303         gtk_menu_shell_insert( GTK_MENU_SHELL(m_menubar
), menu
->m_owner
, pos 
); 
 305     g_signal_connect (menu
->m_owner
, "activate", 
 306                       G_CALLBACK (gtk_menu_open_callback
), 
 309     // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables 
 310     // addings menu later on. 
 311     if (m_invokingWindow
) 
 313         wxMenubarSetInvokingWindow( menu
, m_invokingWindow 
); 
 315             // OPTIMISE ME:  we should probably cache this, or pass it 
 316             //               directly, but for now this is a minimal 
 317             //               change to validate the new dynamic sizing. 
 318             //               see (and refactor :) similar code in Remove 
 321         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 324             frame
->UpdateMenuBarSize(); 
 330 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 332     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
 337     if ( !GtkAppend(menu
, title
, (int)pos
) ) 
 343 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 345     // remove the old item and insert a new one 
 346     wxMenu 
*menuOld 
= Remove(pos
); 
 347     if ( menuOld 
&& !Insert(pos
, menu
, title
) ) 
 349         return (wxMenu
*) NULL
; 
 352     // either Insert() succeeded or Remove() failed and menuOld is NULL 
 356 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
 358     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
 360         return (wxMenu
*) NULL
; 
 362     gtk_menu_item_remove_submenu( GTK_MENU_ITEM(menu
->m_owner
) ); 
 363     gtk_container_remove(GTK_CONTAINER(m_menubar
), menu
->m_owner
); 
 365     gtk_widget_destroy( menu
->m_owner 
); 
 366     menu
->m_owner 
= NULL
; 
 368     if (m_invokingWindow
) 
 370         // OPTIMISE ME:  see comment in GtkAppend 
 371         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 374             frame
->UpdateMenuBarSize(); 
 380 static int FindMenuItemRecursive( const wxMenu 
*menu
, const wxString 
&menuString
, const wxString 
&itemString 
) 
 382     if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
)) 
 384         int res 
= menu
->FindItem( itemString 
); 
 385         if (res 
!= wxNOT_FOUND
) 
 389     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 392         wxMenuItem 
*item 
= node
->GetData(); 
 393         if (item
->IsSubMenu()) 
 394             return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
); 
 396         node 
= node
->GetNext(); 
 402 int wxMenuBar::FindMenuItem( const wxString 
&menuString
, const wxString 
&itemString 
) const 
 404     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 407         wxMenu 
*menu 
= node
->GetData(); 
 408         int res 
= FindMenuItemRecursive( menu
, menuString
, itemString
); 
 411         node 
= node
->GetNext(); 
 417 // Find a wxMenuItem using its id. Recurses down into sub-menus 
 418 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
) 
 420     wxMenuItem
* result 
= menu
->FindChildItem(id
); 
 422     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 423     while ( node 
&& result 
== NULL 
) 
 425         wxMenuItem 
*item 
= node
->GetData(); 
 426         if (item
->IsSubMenu()) 
 428             result 
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id 
); 
 430         node 
= node
->GetNext(); 
 436 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu 
**menuForItem 
) const 
 438     wxMenuItem
* result 
= 0; 
 439     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 440     while (node 
&& result 
== 0) 
 442         wxMenu 
*menu 
= node
->GetData(); 
 443         result 
= FindMenuItemByIdRecursive( menu
, id 
); 
 444         node 
= node
->GetNext(); 
 449         *menuForItem 
= result 
? result
->GetMenu() : (wxMenu 
*)NULL
; 
 455 void wxMenuBar::EnableTop( size_t pos
, bool flag 
) 
 457     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 459     wxCHECK_RET( node
, wxT("menu not found") ); 
 461     wxMenu
* menu 
= node
->GetData(); 
 464         gtk_widget_set_sensitive( menu
->m_owner
, flag 
); 
 467 wxString 
wxMenuBar::GetLabelTop( size_t pos 
) const 
 469     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 471     wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") ); 
 473     wxMenu
* menu 
= node
->GetData(); 
 476     wxString 
text( menu
->GetTitle() ); 
 477     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 479         if ( *pc 
== wxT('_') ) 
 481             // '_' is the escape character for GTK+ 
 485         // don't remove ampersands '&' since if we have them in the menu title 
 486         // it means that they were doubled to indicate "&" instead of accelerator 
 494 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label 
) 
 496     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 498     wxCHECK_RET( node
, wxT("menu not found") ); 
 500     wxMenu
* menu 
= node
->GetData(); 
 502     const wxString 
str( wxReplaceUnderscore( label 
) ); 
 504     menu
->SetTitle( str 
); 
 507         gtk_label_set_text_with_mnemonic( GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
), wxGTK_CONV(str
) ); 
 510 //----------------------------------------------------------------------------- 
 512 //----------------------------------------------------------------------------- 
 515 static void gtk_menu_clicked_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 518         wxapp_install_idle_handler(); 
 520     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 522     /* should find it for normal (not popup) menu */ 
 523     wxASSERT_MSG( (id 
!= -1) || (menu
->GetInvokingWindow() != NULL
), 
 524                   _T("menu item not found in gtk_menu_clicked_callback") ); 
 526     if (!menu
->IsEnabled(id
)) 
 529     wxMenuItem
* item 
= menu
->FindChildItem( id 
); 
 530     wxCHECK_RET( item
, wxT("error in menu item callback") ); 
 532     if ( item
->GetId() == wxGTK_TITLE_ID 
) 
 534         // ignore events from the menu title 
 538     if (item
->IsCheckable()) 
 540         bool isReallyChecked 
= item
->IsChecked(), 
 541             isInternallyChecked 
= item
->wxMenuItemBase::IsChecked(); 
 543         // ensure that the internal state is always consistent with what is 
 544         // shown on the screen 
 545         item
->wxMenuItemBase::Check(isReallyChecked
); 
 547         // we must not report the events for the radio button going up nor the 
 548         // events resulting from the calls to wxMenuItem::Check() 
 549         if ( (item
->GetKind() == wxITEM_RADIO 
&& !isReallyChecked
) || 
 550              (isInternallyChecked 
== isReallyChecked
) ) 
 557     // Is this menu on a menubar?  (possibly nested) 
 558     wxFrame
* frame 
= NULL
; 
 559     if(menu
->IsAttached()) 
 560         frame 
= menu
->GetMenuBar()->GetFrame(); 
 562     // FIXME: why do we have to call wxFrame::GetEventHandler() directly here? 
 563     //        normally wxMenu::SendEvent() should be enough, if it doesn't work 
 564     //        in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which 
 565     //        should be fixed instead of working around it here... 
 568         // If it is attached then let the frame send the event. 
 569         // Don't call frame->ProcessCommand(id) because it toggles 
 570         // checkable items and we've already done that above. 
 571         wxCommandEvent 
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
); 
 572         commandEvent
.SetEventObject(frame
); 
 573         if (item
->IsCheckable()) 
 574             commandEvent
.SetInt(item
->IsChecked()); 
 575         commandEvent
.SetEventObject(menu
); 
 577         frame
->GetEventHandler()->ProcessEvent(commandEvent
); 
 581         // otherwise let the menu have it 
 582         menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1); 
 587 //----------------------------------------------------------------------------- 
 589 //----------------------------------------------------------------------------- 
 592 static void gtk_menu_hilight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 594     if (g_isIdle
) wxapp_install_idle_handler(); 
 596     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 598     wxASSERT( id 
!= -1 ); // should find it! 
 600     if (!menu
->IsEnabled(id
)) 
 603     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, id 
); 
 604     event
.SetEventObject( menu 
); 
 606     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 607     if (handler 
&& handler
->ProcessEvent(event
)) 
 610     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 611     if (win
) win
->GetEventHandler()->ProcessEvent( event 
); 
 615 //----------------------------------------------------------------------------- 
 617 //----------------------------------------------------------------------------- 
 620 static void gtk_menu_nolight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 622     if (g_isIdle
) wxapp_install_idle_handler(); 
 624     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 626     wxASSERT( id 
!= -1 ); // should find it! 
 628     if (!menu
->IsEnabled(id
)) 
 631     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, -1 ); 
 632     event
.SetEventObject( menu 
); 
 634     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 635     if (handler 
&& handler
->ProcessEvent(event
)) 
 638     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 640         win
->GetEventHandler()->ProcessEvent( event 
); 
 644 //----------------------------------------------------------------------------- 
 646 //----------------------------------------------------------------------------- 
 648 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 650 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
 652                                 const wxString
& name
, 
 653                                 const wxString
& help
, 
 657     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
 660 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 662                        const wxString
& text
, 
 663                        const wxString
& help
, 
 666           : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
) 
 671 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 673                        const wxString
& text
, 
 674                        const wxString
& help
, 
 677           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 678                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 683 void wxMenuItem::Init(const wxString
& text
) 
 685     m_labelWidget 
= (GtkWidget 
*) NULL
; 
 686     m_menuItem 
= (GtkWidget 
*) NULL
; 
 691 wxMenuItem::~wxMenuItem() 
 693    // don't delete menu items, the menus take care of that 
 696 // return the menu item text without any menu accels 
 698 wxString 
wxMenuItemBase::GetLabelFromText(const wxString
& text
) 
 702     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 704         if ( *pc 
== wxT('\t')) 
 707         if ( *pc 
== wxT('_') ) 
 709             // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx" 
 715         if ( *pc 
== wxT('\\')  ) 
 717             // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx" 
 723         if ( (*pc 
== wxT('&')) && (*(pc
+1) != wxT('&')) ) 
 726             // "&" is doubled to indicate "&" instead of accelerator 
 733     // wxPrintf( wxT("GetLabelFromText(): text %s label %s\n"), text.c_str(), label.c_str() ); 
 738 void wxMenuItem::SetText( const wxString
& str 
) 
 740     // Some optimization to avoid flicker 
 741     wxString oldLabel 
= m_text
; 
 742     oldLabel 
= wxStripMenuCodes(oldLabel
); 
 743     oldLabel
.Replace(wxT("_"), wxT("")); 
 744     wxString label1 
= wxStripMenuCodes(str
); 
 745     wxString oldhotkey 
= GetHotKey();    // Store the old hotkey in Ctrl-foo format 
 746     wxCharBuffer oldbuf 
= wxGTK_CONV_SYS( GetGtkHotKey(*this) );  // and as <control>foo 
 750     if (oldLabel 
== label1 
&& 
 751              oldhotkey 
== GetHotKey())    // Make sure we can change a hotkey even if the label is unaltered 
 758             label 
= (GtkLabel
*) m_labelWidget
; 
 760             label 
= GTK_LABEL( GTK_BIN(m_menuItem
)->child 
); 
 762         gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV_SYS(m_text
) ); 
 766     GdkModifierType accel_mods
; 
 767     gtk_accelerator_parse( (const char*) oldbuf
, &accel_key
, &accel_mods
); 
 770         gtk_widget_remove_accelerator( GTK_WIDGET(m_menuItem
), 
 771                                        m_parentMenu
->m_accel
, 
 776     wxCharBuffer buf 
= wxGTK_CONV_SYS( GetGtkHotKey(*this) ); 
 777     gtk_accelerator_parse( (const char*) buf
, &accel_key
, &accel_mods
); 
 780         gtk_widget_add_accelerator( GTK_WIDGET(m_menuItem
), 
 782                                     m_parentMenu
->m_accel
, 
 789 // it's valid for this function to be called even if m_menuItem == NULL 
 790 void wxMenuItem::DoSetText( const wxString
& str 
) 
 792     // '\t' is the deliminator indicating a hot key 
 794     const wxChar 
*pc 
= str
; 
 795     while ( (*pc 
!= wxT('\0')) && (*pc 
!= wxT('\t')) ) 
 797         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
 799             // "&" is doubled to indicate "&" instead of accelerator 
 803         else if (*pc 
== wxT('&')) 
 807         else if ( *pc 
== wxT('_') )    // escape underscores 
 826     // wxPrintf( wxT("DoSetText(): str %s m_text %s hotkey %s\n"), str.c_str(), m_text.c_str(), m_hotKey.c_str() ); 
 831 wxAcceleratorEntry 
*wxMenuItem::GetAccel() const 
 836         return (wxAcceleratorEntry 
*)NULL
; 
 839     // as wxGetAccelFromString() looks for TAB, insert a dummy one here 
 841     label 
<< wxT('\t') << GetHotKey(); 
 843     return wxGetAccelFromString(label
); 
 846 #endif // wxUSE_ACCEL 
 848 void wxMenuItem::Check( bool check 
) 
 850     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 852     if (check 
== m_isChecked
) 
 855     wxMenuItemBase::Check( check 
); 
 861             gtk_check_menu_item_set_active( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check 
); 
 865             wxFAIL_MSG( _T("can't check this item") ); 
 869 void wxMenuItem::Enable( bool enable 
) 
 871     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 873     gtk_widget_set_sensitive( m_menuItem
, enable 
); 
 874     wxMenuItemBase::Enable( enable 
); 
 877 bool wxMenuItem::IsChecked() const 
 879     wxCHECK_MSG( m_menuItem
, false, wxT("invalid menu item") ); 
 881     wxCHECK_MSG( IsCheckable(), false, 
 882                  wxT("can't get state of uncheckable item!") ); 
 884     return ((GtkCheckMenuItem
*)m_menuItem
)->active 
!= 0; 
 887 //----------------------------------------------------------------------------- 
 889 //----------------------------------------------------------------------------- 
 891 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
) 
 895     m_accel 
= gtk_accel_group_new(); 
 896     m_menu 
= gtk_menu_new(); 
 897     // NB: keep reference to the menu so that it is not destroyed behind 
 898     //     our back by GTK+ e.g. when it is removed from menubar: 
 899     gtk_widget_ref(m_menu
); 
 901     m_owner 
= (GtkWidget
*) NULL
; 
 903     // Tearoffs are entries, just like separators. So if we want this 
 904     // menu to be a tear-off one, we just append a tearoff entry 
 906     if ( m_style 
& wxMENU_TEAROFF 
) 
 908         GtkWidget 
*tearoff 
= gtk_tearoff_menu_item_new(); 
 910         gtk_menu_shell_append(GTK_MENU_SHELL(m_menu
), tearoff
); 
 915     // append the title as the very first entry if we have it 
 916     if ( !m_title
.empty() ) 
 918         Append(wxGTK_TITLE_ID
, m_title
); 
 925    WX_CLEAR_LIST(wxMenuItemList
, m_items
); 
 927    if ( GTK_IS_WIDGET( m_menu 
)) 
 930        gtk_widget_unref( m_menu 
); 
 931        // if the menu is inserted in another menu at this time, there was 
 932        // one more reference to it: 
 934            gtk_widget_destroy( m_menu 
); 
 938 bool wxMenu::GtkAppend(wxMenuItem 
*mitem
, int pos
) 
 944     if ( mitem
->IsSeparator() ) 
 946         menuItem 
= gtk_separator_menu_item_new(); 
 948     else if (mitem
->GetBitmap().Ok()) 
 950         text 
= mitem
->GetText(); 
 951         const wxBitmap 
*bitmap 
= &mitem
->GetBitmap(); 
 953         menuItem 
= gtk_image_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text 
) ); 
 956         if (bitmap
->HasPixbuf()) 
 958             image 
= gtk_image_new_from_pixbuf(bitmap
->GetPixbuf()); 
 962             GdkPixmap 
*gdk_pixmap 
= bitmap
->GetPixmap(); 
 963             GdkBitmap 
*gdk_bitmap 
= bitmap
->GetMask() ? 
 964                                         bitmap
->GetMask()->GetBitmap() : 
 966             image 
= gtk_image_new_from_pixmap( gdk_pixmap
, gdk_bitmap 
); 
 969         gtk_widget_show(image
); 
 971         gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(menuItem
), image 
); 
 975     else // a normal item 
 977         // text has "_" instead of "&" after mitem->SetText() so don't use it 
 978         text 
=  mitem
->GetText() ; 
 980         switch ( mitem
->GetKind() ) 
 984                 menuItem 
= gtk_check_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text 
) ); 
 991                 GSList 
*group 
= NULL
; 
 992                 if ( m_prevRadio 
== NULL 
) 
 994                     // start of a new radio group 
 995                     m_prevRadio 
= menuItem 
= 
 996                         gtk_radio_menu_item_new_with_mnemonic( group
, wxGTK_CONV_SYS( text 
) ); 
 998                 else // continue the radio group 
1000                     group 
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (m_prevRadio
)); 
1001                     m_prevRadio 
= menuItem 
= 
1002                         gtk_radio_menu_item_new_with_mnemonic( group
, wxGTK_CONV_SYS( text 
) ); 
1008                 wxFAIL_MSG( _T("unexpected menu item kind") ); 
1013                 menuItem 
= gtk_menu_item_new_with_mnemonic( wxGTK_CONV_SYS( text 
) ); 
1022     GdkModifierType accel_mods
; 
1023     wxCharBuffer buf 
= wxGTK_CONV_SYS( GetGtkHotKey(*mitem
) ); 
1025     // wxPrintf( wxT("item: %s hotkey %s\n"), mitem->GetText().c_str(), GetGtkHotKey(*mitem).c_str() ); 
1026     gtk_accelerator_parse( (const char*) buf
, &accel_key
, &accel_mods
); 
1029         gtk_widget_add_accelerator (GTK_WIDGET(menuItem
), 
1038         gtk_menu_shell_append(GTK_MENU_SHELL(m_menu
), menuItem
); 
1040         gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu
), menuItem
, pos
); 
1042     gtk_widget_show( menuItem 
); 
1044     if ( !mitem
->IsSeparator() ) 
1046         wxASSERT_MSG( menuItem
, wxT("invalid menuitem") ); 
1048         g_signal_connect (menuItem
, "select", 
1049                           G_CALLBACK (gtk_menu_hilight_callback
), this); 
1050         g_signal_connect (menuItem
, "deselect", 
1051                           G_CALLBACK (gtk_menu_nolight_callback
), this); 
1053         if ( mitem
->IsSubMenu() && mitem
->GetKind() != wxITEM_RADIO 
&& mitem
->GetKind() != wxITEM_CHECK 
) 
1055             gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu 
); 
1057             gtk_widget_show( mitem
->GetSubMenu()->m_menu 
); 
1059             // if adding a submenu to a menu already existing in the menu bar, we 
1060             // must set invoking window to allow processing events from this 
1062             if ( m_invokingWindow 
) 
1063                 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
); 
1067             g_signal_connect (menuItem
, "activate", 
1068                               G_CALLBACK (gtk_menu_clicked_callback
), 
1073     mitem
->SetMenuItem(menuItem
); 
1077         // This doesn't even exist! 
1078         // gtk_widget_lock_accelerators(mitem->GetMenuItem()); 
1084 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*mitem
) 
1086     if (!GtkAppend(mitem
)) 
1089     return wxMenuBase::DoAppend(mitem
); 
1092 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
1094     if ( !wxMenuBase::DoInsert(pos
, item
) ) 
1098     if ( !GtkAppend(item
, (int)pos
) ) 
1104 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
1106     if ( !wxMenuBase::DoRemove(item
) ) 
1107         return (wxMenuItem 
*)NULL
; 
1109     // TODO: this code doesn't delete the item factory item and this seems 
1110     //       impossible as of GTK 1.2.6. 
1111     gtk_widget_destroy( item
->GetMenuItem() ); 
1116 int wxMenu::FindMenuIdByMenuItem( GtkWidget 
*menuItem 
) const 
1118     wxMenuItemList::compatibility_iterator node 
= m_items
.GetFirst(); 
1121         wxMenuItem 
*item 
= node
->GetData(); 
1122         if (item
->GetMenuItem() == menuItem
) 
1123             return item
->GetId(); 
1124         node 
= node
->GetNext(); 
1130 // ---------------------------------------------------------------------------- 
1132 // ---------------------------------------------------------------------------- 
1136 static wxString 
GetGtkHotKey( const wxMenuItem
& item 
) 
1140     wxAcceleratorEntry 
*accel 
= item
.GetAccel(); 
1143         int flags 
= accel
->GetFlags(); 
1144         if ( flags 
& wxACCEL_ALT 
) 
1145             hotkey 
+= wxT("<alt>"); 
1146         if ( flags 
& wxACCEL_CTRL 
) 
1147             hotkey 
+= wxT("<control>"); 
1148         if ( flags 
& wxACCEL_SHIFT 
) 
1149             hotkey 
+= wxT("<shift>"); 
1151         int code 
= accel
->GetKeyCode(); 
1178                 hotkey 
+= wxString::Format(wxT("F%d"), code 
- WXK_F1 
+ 1); 
1181                 // TODO: we should use gdk_keyval_name() (a.k.a. 
1182                 //       XKeysymToString) here as well as hardcoding the keysym 
1183                 //       names this might be not portable 
1185                 hotkey 
<< wxT("Insert" ); 
1188                 hotkey 
<< wxT("Delete" ); 
1191                 hotkey 
<< wxT("Up" ); 
1194                 hotkey 
<< wxT("Down" ); 
1197                 hotkey 
<< wxT("PgUp" ); 
1200                 hotkey 
<< wxT("PgDn" ); 
1203                 hotkey 
<< wxT("Left" ); 
1206                 hotkey 
<< wxT("Right" ); 
1209                 hotkey 
<< wxT("Home" ); 
1212                 hotkey 
<< wxT("End" ); 
1215                 hotkey 
<< wxT("Return" ); 
1218                 hotkey 
<< wxT("BackSpace" ); 
1221                 hotkey 
<< wxT("Tab" ); 
1224                 hotkey 
<< wxT("Esc" ); 
1227                 hotkey 
<< wxT("space" ); 
1230                 hotkey 
<< wxT("Multiply" ); 
1233                 hotkey 
<< wxT("Add" ); 
1236                 hotkey 
<< wxT("Separator" ); 
1239                 hotkey 
<< wxT("Subtract" ); 
1242                 hotkey 
<< wxT("Decimal" ); 
1245                 hotkey 
<< wxT("Divide" ); 
1248                 hotkey 
<< wxT("Cancel" ); 
1251                 hotkey 
<< wxT("Clear" ); 
1254                 hotkey 
<< wxT("Menu" ); 
1257                 hotkey 
<< wxT("Pause" ); 
1260                 hotkey 
<< wxT("Capital" ); 
1263                 hotkey 
<< wxT("Select" ); 
1266                 hotkey 
<< wxT("Print" ); 
1269                 hotkey 
<< wxT("Execute" ); 
1272                 hotkey 
<< wxT("Snapshot" ); 
1275                 hotkey 
<< wxT("Help" ); 
1278                 hotkey 
<< wxT("Num_Lock" ); 
1281                 hotkey 
<< wxT("Scroll_Lock" ); 
1283             case WXK_NUMPAD_INSERT
: 
1284                 hotkey 
<< wxT("KP_Insert" ); 
1286             case WXK_NUMPAD_DELETE
: 
1287                 hotkey 
<< wxT("KP_Delete" ); 
1289              case WXK_NUMPAD_SPACE
: 
1290                 hotkey 
<< wxT("KP_Space" ); 
1292             case WXK_NUMPAD_TAB
: 
1293                 hotkey 
<< wxT("KP_Tab" ); 
1295             case WXK_NUMPAD_ENTER
: 
1296                 hotkey 
<< wxT("KP_Enter" ); 
1298             case WXK_NUMPAD_F1
: case WXK_NUMPAD_F2
: case WXK_NUMPAD_F3
: 
1300                 hotkey 
+= wxString::Format(wxT("KP_F%d"), code 
- WXK_NUMPAD_F1 
+ 1); 
1302             case WXK_NUMPAD_HOME
: 
1303                 hotkey 
<< wxT("KP_Home" ); 
1305             case WXK_NUMPAD_LEFT
: 
1306                 hotkey 
<< wxT("KP_Left" ); 
1309                 hotkey 
<< wxT("KP_Up" ); 
1311             case WXK_NUMPAD_RIGHT
: 
1312                 hotkey 
<< wxT("KP_Right" ); 
1314             case WXK_NUMPAD_DOWN
: 
1315                 hotkey 
<< wxT("KP_Down" ); 
1317             case WXK_NUMPAD_PAGEUP
: 
1318                 hotkey 
<< wxT("KP_PgUp" ); 
1320             case WXK_NUMPAD_PAGEDOWN
: 
1321                 hotkey 
<< wxT("KP_PgDn" ); 
1323             case WXK_NUMPAD_END
: 
1324                 hotkey 
<< wxT("KP_End" ); 
1326             case WXK_NUMPAD_BEGIN
: 
1327                 hotkey 
<< wxT("KP_Begin" ); 
1329             case WXK_NUMPAD_EQUAL
: 
1330                 hotkey 
<< wxT("KP_Equal" ); 
1332             case WXK_NUMPAD_MULTIPLY
: 
1333                 hotkey 
<< wxT("KP_Multiply" ); 
1335             case WXK_NUMPAD_ADD
: 
1336                 hotkey 
<< wxT("KP_Add" ); 
1338             case WXK_NUMPAD_SEPARATOR
: 
1339                 hotkey 
<< wxT("KP_Separator" ); 
1341             case WXK_NUMPAD_SUBTRACT
: 
1342                 hotkey 
<< wxT("KP_Subtract" ); 
1344             case WXK_NUMPAD_DECIMAL
: 
1345                 hotkey 
<< wxT("KP_Decimal" ); 
1347             case WXK_NUMPAD_DIVIDE
: 
1348                 hotkey 
<< wxT("KP_Divide" ); 
1350            case WXK_NUMPAD0
: case WXK_NUMPAD1
: case WXK_NUMPAD2
: 
1351            case WXK_NUMPAD3
: case WXK_NUMPAD4
: case WXK_NUMPAD5
: 
1352            case WXK_NUMPAD6
: case WXK_NUMPAD7
: case WXK_NUMPAD8
: case WXK_NUMPAD9
: 
1353                 hotkey 
+= wxString::Format(wxT("KP_%d"), code 
- WXK_NUMPAD0
); 
1355             case WXK_WINDOWS_LEFT
: 
1356                 hotkey 
<< wxT("Super_L" ); 
1358             case WXK_WINDOWS_RIGHT
: 
1359                 hotkey 
<< wxT("Super_R" ); 
1361             case WXK_WINDOWS_MENU
: 
1362                 hotkey 
<< wxT("Menu" ); 
1365                 hotkey 
<< wxT("Command" ); 
1367           /* These probably wouldn't work as there is no SpecialX in gdk/keynames.txt 
1368             case WXK_SPECIAL1: case WXK_SPECIAL2: case WXK_SPECIAL3: case WXK_SPECIAL4: 
1369             case WXK_SPECIAL5: case WXK_SPECIAL6: case WXK_SPECIAL7: case WXK_SPECIAL8: 
1370             case WXK_SPECIAL9:  case WXK_SPECIAL10:  case WXK_SPECIAL11: case WXK_SPECIAL12: 
1371             case WXK_SPECIAL13: case WXK_SPECIAL14: case WXK_SPECIAL15: case WXK_SPECIAL16: 
1372             case WXK_SPECIAL17: case WXK_SPECIAL18: case WXK_SPECIAL19:  case WXK_SPECIAL20: 
1373                 hotkey += wxString::Format(wxT("Special%d"), code - WXK_SPECIAL1 + 1); 
1376                 // if there are any other keys wxGetAccelFromString() may 
1377                 // return, we should process them here 
1382                     wxString name 
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) ); 
1390                 wxFAIL_MSG( wxT("unknown keyboard accel") ); 
1399 #endif // wxUSE_ACCEL 
1401 // ---------------------------------------------------------------------------- 
1402 // Pop-up menu stuff 
1403 // ---------------------------------------------------------------------------- 
1405 #if wxUSE_MENUS_NATIVE 
1407 extern "C" WXDLLIMPEXP_CORE
 
1408 void gtk_pop_hide_callback( GtkWidget 
*WXUNUSED(widget
), bool* is_waiting  
) 
1410     *is_waiting 
= false; 
1413 WXDLLIMPEXP_CORE 
void SetInvokingWindow( wxMenu 
*menu
, wxWindow
* win 
) 
1415     menu
->SetInvokingWindow( win 
); 
1417     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
1420         wxMenuItem 
*menuitem 
= node
->GetData(); 
1421         if (menuitem
->IsSubMenu()) 
1423             SetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
1426         node 
= node
->GetNext(); 
1430 extern "C" WXDLLIMPEXP_CORE
 
1431 void wxPopupMenuPositionCallback( GtkMenu 
*menu
, 
1433                                   gboolean 
* WXUNUSED(whatever
), 
1434                                   gpointer user_data 
) 
1436     // ensure that the menu appears entirely on screen 
1438     gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
); 
1440     wxSize sizeScreen 
= wxGetDisplaySize(); 
1441     wxPoint 
*pos 
= (wxPoint
*)user_data
; 
1443     gint xmax 
= sizeScreen
.x 
- req
.width
, 
1444          ymax 
= sizeScreen
.y 
- req
.height
; 
1446     *x 
= pos
->x 
< xmax 
? pos
->x 
: xmax
; 
1447     *y 
= pos
->y 
< ymax 
? pos
->y 
: ymax
; 
1450 bool wxWindowGTK::DoPopupMenu( wxMenu 
*menu
, int x
, int y 
) 
1452     wxCHECK_MSG( m_widget 
!= NULL
, false, wxT("invalid window") ); 
1454     wxCHECK_MSG( menu 
!= NULL
, false, wxT("invalid popup-menu") ); 
1456     // NOTE: if you change this code, you need to update 
1457     //       the same code in taskbar.cpp as well. This 
1458     //       is ugly code duplication, I know. 
1460     SetInvokingWindow( menu
, this ); 
1464     bool is_waiting 
= true; 
1466     gulong handler 
= g_signal_connect (menu
->m_menu
, "hide", 
1467                                        G_CALLBACK (gtk_pop_hide_callback
), 
1472     GtkMenuPositionFunc posfunc
; 
1473     if ( x 
== -1 && y 
== -1 ) 
1475         // use GTK's default positioning algorithm 
1481         pos 
= ClientToScreen(wxPoint(x
, y
)); 
1483         posfunc 
= wxPopupMenuPositionCallback
; 
1486     wxMenuEvent 
eventOpen(wxEVT_MENU_OPEN
, -1, menu
); 
1487     DoCommonMenuCallbackCode(menu
, eventOpen
); 
1490                   GTK_MENU(menu
->m_menu
), 
1491                   (GtkWidget 
*) NULL
,           // parent menu shell 
1492                   (GtkWidget 
*) NULL
,           // parent menu item 
1493                   posfunc
,                      // function to position it 
1494                   userdata
,                     // client data 
1495                   0,                            // button used to activate it 
1496                   gtk_get_current_event_time() 
1501         gtk_main_iteration(); 
1504     g_signal_handler_disconnect (menu
->m_menu
, handler
); 
1506     wxMenuEvent 
eventClose(wxEVT_MENU_CLOSE
, -1, menu
); 
1507     DoCommonMenuCallbackCode(menu
, eventClose
); 
1512 #endif // wxUSE_MENUS_NATIVE