1 ///////////////////////////////////////////////////////////////////////////// 
   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" 
  17 #include "wx/bitmap.h" 
  23 #include "wx/gtk/private.h" 
  25 #include <gdk/gdkkeysyms.h> 
  27 // FIXME: is this right? somehow I don't think so (VZ) 
  29     #include <glib-object.h> 
  31     #define gtk_accel_group_attach(g, o) gtk_window_add_accel_group((o), (g)) 
  32     #define gtk_accel_group_detach(g, o) gtk_window_remove_accel_group((o), (g)) 
  33     #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m) 
  35     #define ACCEL_OBJECT        GtkWindow 
  36     #define ACCEL_OBJECTS(a)    (a)->acceleratables 
  37     #define ACCEL_OBJ_CAST(obj) ((GtkWindow*) obj) 
  39     #define ACCEL_OBJECT        GtkObject 
  40     #define ACCEL_OBJECTS(a)    (a)->attach_objects 
  41     #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj) 
  44 // we use normal item but with a special id for the menu title 
  45 static const int wxGTK_TITLE_ID 
= -3; 
  47 //----------------------------------------------------------------------------- 
  49 //----------------------------------------------------------------------------- 
  51 extern void wxapp_install_idle_handler(); 
  55 static wxString 
GetGtkHotKey( const wxMenuItem
& item 
); 
  58 //----------------------------------------------------------------------------- 
  60 //----------------------------------------------------------------------------- 
  62 static wxString 
wxReplaceUnderscore( const wxString
& title 
) 
  66     // GTK 1.2 wants to have "_" instead of "&" for accelerators 
  69     while (*pc 
!= wxT('\0')) 
  71         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
  73             // "&" is doubled to indicate "&" instead of accelerator 
  77         else if (*pc 
== wxT('&')) 
  83             if ( *pc 
== wxT('_') ) 
  85                 // underscores must be doubled to prevent them from being 
  86                 // interpreted as accelerator character prefix by GTK 
  95     // wxPrintf( wxT("before %s after %s\n"), title.c_str(), str.c_str() ); 
 100 //----------------------------------------------------------------------------- 
 101 // activate message from GTK 
 102 //----------------------------------------------------------------------------- 
 104 static void DoCommonMenuCallbackCode(wxMenu 
*menu
, wxMenuEvent
& event
) 
 107         wxapp_install_idle_handler(); 
 109     event
.SetEventObject( menu 
); 
 111     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 112     if (handler 
&& handler
->ProcessEvent(event
)) 
 115     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 117         win
->GetEventHandler()->ProcessEvent( event 
); 
 122 static void gtk_menu_open_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 124     wxMenuEvent 
event(wxEVT_MENU_OPEN
, -1, menu
); 
 126     DoCommonMenuCallbackCode(menu
, event
); 
 129 static void gtk_menu_close_callback( GtkWidget 
*widget
, wxMenuBar 
*menubar 
) 
 131     if ( !menubar
->GetMenuCount() ) 
 133         // if menubar is empty we can't call GetMenu(0) below 
 137     wxMenuEvent 
event( wxEVT_MENU_CLOSE
, -1, NULL 
); 
 139     DoCommonMenuCallbackCode(menubar
->GetMenu(0), event
); 
 144 //----------------------------------------------------------------------------- 
 146 //----------------------------------------------------------------------------- 
 148 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
) 
 150 void wxMenuBar::Init(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long style
) 
 152     // the parent window is known after wxFrame::SetMenu() 
 153     m_needParent 
= FALSE
; 
 155     m_invokingWindow 
= (wxWindow
*) NULL
; 
 157     if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize 
) || 
 158         !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") )) 
 160         wxFAIL_MSG( wxT("wxMenuBar creation failed") ); 
 164     m_menubar 
= gtk_menu_bar_new(); 
 166     if (style 
& wxMB_DOCKABLE
) 
 168         m_widget 
= gtk_handle_box_new(); 
 169         gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) ); 
 170         gtk_widget_show( GTK_WIDGET(m_menubar
) ); 
 174         m_widget 
= GTK_WIDGET(m_menubar
); 
 181     for (size_t i 
= 0; i 
< n
; ++i 
) 
 182         Append(menus
[i
], titles
[i
]); 
 184     // VZ: for some reason connecting to menus "deactivate" doesn't work (we 
 185     //     don't get it when the menu is dismissed by clicking outside the 
 186     //     toolbar) so we connect to the global one, even if it means that we 
 187     //     can't pass the menu which was closed in wxMenuEvent object 
 188     g_signal_connect (m_menubar
, "deactivate", 
 189                       G_CALLBACK (gtk_menu_close_callback
), this); 
 193 wxMenuBar::wxMenuBar(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long style
) 
 195     Init(n
, menus
, titles
, style
); 
 198 wxMenuBar::wxMenuBar(long style
) 
 200     Init(0, NULL
, NULL
, style
); 
 203 wxMenuBar::wxMenuBar() 
 205     Init(0, NULL
, NULL
, 0); 
 208 wxMenuBar::~wxMenuBar() 
 212 static void wxMenubarUnsetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 214     menu
->SetInvokingWindow( (wxWindow
*) NULL 
); 
 216     wxWindow 
*top_frame 
= win
; 
 217     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 218         top_frame 
= top_frame
->GetParent(); 
 220     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 223         wxMenuItem 
*menuitem 
= node
->GetData(); 
 224         if (menuitem
->IsSubMenu()) 
 225             wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 226         node 
= node
->GetNext(); 
 230 static void wxMenubarSetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 232     menu
->SetInvokingWindow( win 
); 
 234     wxWindow 
*top_frame 
= win
; 
 235     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 236         top_frame 
= top_frame
->GetParent(); 
 238     // support for native hot keys 
 239     ACCEL_OBJECT 
*obj 
= ACCEL_OBJ_CAST(top_frame
->m_widget
); 
 240     if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj 
) ) 
 241         gtk_accel_group_attach( menu
->m_accel
, obj 
); 
 243     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 246         wxMenuItem 
*menuitem 
= node
->GetData(); 
 247         if (menuitem
->IsSubMenu()) 
 248             wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 249         node 
= node
->GetNext(); 
 253 void wxMenuBar::SetInvokingWindow( wxWindow 
*win 
) 
 255     m_invokingWindow 
= win
; 
 256     wxWindow 
*top_frame 
= win
; 
 257     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 258         top_frame 
= top_frame
->GetParent(); 
 260     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 263         wxMenu 
*menu 
= node
->GetData(); 
 264         wxMenubarSetInvokingWindow( menu
, win 
); 
 265         node 
= node
->GetNext(); 
 269 void wxMenuBar::UnsetInvokingWindow( wxWindow 
*win 
) 
 271     m_invokingWindow 
= (wxWindow
*) NULL
; 
 272     wxWindow 
*top_frame 
= win
; 
 273     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 274         top_frame 
= top_frame
->GetParent(); 
 276     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 279         wxMenu 
*menu 
= node
->GetData(); 
 280         wxMenubarUnsetInvokingWindow( menu
, win 
); 
 281         node 
= node
->GetNext(); 
 285 bool wxMenuBar::Append( wxMenu 
*menu
, const wxString 
&title 
) 
 287     if ( !wxMenuBarBase::Append( menu
, title 
) ) 
 290     return GtkAppend(menu
, title
); 
 293 bool wxMenuBar::GtkAppend(wxMenu 
*menu
, const wxString
& title
, int pos
) 
 295     wxString 
str( wxReplaceUnderscore( title 
) ); 
 297     // This doesn't have much effect right now. 
 298     menu
->SetTitle( str 
); 
 300     // The "m_owner" is the "menu item" 
 301     menu
->m_owner 
= gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str 
) ); 
 303     gtk_widget_show( menu
->m_owner 
); 
 305     gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu 
); 
 308         gtk_menu_shell_append( GTK_MENU_SHELL(m_menubar
), menu
->m_owner 
); 
 310         gtk_menu_shell_insert( GTK_MENU_SHELL(m_menubar
), menu
->m_owner
, pos 
); 
 312     g_signal_connect (menu
->m_owner
, "activate", 
 313                       G_CALLBACK (gtk_menu_open_callback
), 
 316     // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables 
 317     // addings menu later on. 
 318     if (m_invokingWindow
) 
 320         wxMenubarSetInvokingWindow( menu
, m_invokingWindow 
); 
 322             // OPTIMISE ME:  we should probably cache this, or pass it 
 323             //               directly, but for now this is a minimal 
 324             //               change to validate the new dynamic sizing. 
 325             //               see (and refactor :) similar code in Remove 
 328         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 331             frame
->UpdateMenuBarSize(); 
 337 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 339     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
 344     if ( !GtkAppend(menu
, title
, (int)pos
) ) 
 350 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 352     // remove the old item and insert a new one 
 353     wxMenu 
*menuOld 
= Remove(pos
); 
 354     if ( menuOld 
&& !Insert(pos
, menu
, title
) ) 
 356         return (wxMenu
*) NULL
; 
 359     // either Insert() succeeded or Remove() failed and menuOld is NULL 
 363 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
 365     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
 367         return (wxMenu
*) NULL
; 
 369     gtk_menu_item_remove_submenu( GTK_MENU_ITEM(menu
->m_owner
) ); 
 370     gtk_container_remove(GTK_CONTAINER(m_menubar
), menu
->m_owner
); 
 372     gtk_widget_destroy( menu
->m_owner 
); 
 373     menu
->m_owner 
= NULL
; 
 375     if (m_invokingWindow
) 
 377         // OPTIMISE ME:  see comment in GtkAppend 
 378         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 381             frame
->UpdateMenuBarSize(); 
 387 static int FindMenuItemRecursive( const wxMenu 
*menu
, const wxString 
&menuString
, const wxString 
&itemString 
) 
 389     if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
)) 
 391         int res 
= menu
->FindItem( itemString 
); 
 392         if (res 
!= wxNOT_FOUND
) 
 396     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 399         wxMenuItem 
*item 
= node
->GetData(); 
 400         if (item
->IsSubMenu()) 
 401             return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
); 
 403         node 
= node
->GetNext(); 
 409 int wxMenuBar::FindMenuItem( const wxString 
&menuString
, const wxString 
&itemString 
) const 
 411     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 414         wxMenu 
*menu 
= node
->GetData(); 
 415         int res 
= FindMenuItemRecursive( menu
, menuString
, itemString
); 
 418         node 
= node
->GetNext(); 
 424 // Find a wxMenuItem using its id. Recurses down into sub-menus 
 425 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
) 
 427     wxMenuItem
* result 
= menu
->FindChildItem(id
); 
 429     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 430     while ( node 
&& result 
== NULL 
) 
 432         wxMenuItem 
*item 
= node
->GetData(); 
 433         if (item
->IsSubMenu()) 
 435             result 
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id 
); 
 437         node 
= node
->GetNext(); 
 443 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu 
**menuForItem 
) const 
 445     wxMenuItem
* result 
= 0; 
 446     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 447     while (node 
&& result 
== 0) 
 449         wxMenu 
*menu 
= node
->GetData(); 
 450         result 
= FindMenuItemByIdRecursive( menu
, id 
); 
 451         node 
= node
->GetNext(); 
 456         *menuForItem 
= result 
? result
->GetMenu() : (wxMenu 
*)NULL
; 
 462 void wxMenuBar::EnableTop( size_t pos
, bool flag 
) 
 464     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 466     wxCHECK_RET( node
, wxT("menu not found") ); 
 468     wxMenu
* menu 
= node
->GetData(); 
 471         gtk_widget_set_sensitive( menu
->m_owner
, flag 
); 
 474 wxString 
wxMenuBar::GetLabelTop( size_t pos 
) const 
 476     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 478     wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") ); 
 480     wxMenu
* menu 
= node
->GetData(); 
 483     wxString 
text( menu
->GetTitle() ); 
 484     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 486         if ( *pc 
== wxT('_') ) 
 488             // '_' is the escape character for GTK+ 
 492         // don't remove ampersands '&' since if we have them in the menu title 
 493         // it means that they were doubled to indicate "&" instead of accelerator 
 501 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label 
) 
 503     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 505     wxCHECK_RET( node
, wxT("menu not found") ); 
 507     wxMenu
* menu 
= node
->GetData(); 
 509     const wxString 
str( wxReplaceUnderscore( label 
) ); 
 511     menu
->SetTitle( str 
); 
 515         GtkLabel 
*glabel 
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child 
); 
 518         gtk_label_set( glabel
, wxGTK_CONV( str 
) ); 
 520         /* reparse key accel */ 
 521         (void)gtk_label_parse_uline (GTK_LABEL(glabel
), wxGTK_CONV( str 
) ); 
 522         gtk_accel_label_refetch( GTK_ACCEL_LABEL(glabel
) ); 
 527 //----------------------------------------------------------------------------- 
 529 //----------------------------------------------------------------------------- 
 532 static void gtk_menu_clicked_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 535         wxapp_install_idle_handler(); 
 537     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 539     /* should find it for normal (not popup) menu */ 
 540     wxASSERT_MSG( (id 
!= -1) || (menu
->GetInvokingWindow() != NULL
), 
 541                   _T("menu item not found in gtk_menu_clicked_callback") ); 
 543     if (!menu
->IsEnabled(id
)) 
 546     wxMenuItem
* item 
= menu
->FindChildItem( id 
); 
 547     wxCHECK_RET( item
, wxT("error in menu item callback") ); 
 549     if ( item
->GetId() == wxGTK_TITLE_ID 
) 
 551         // ignore events from the menu title 
 555     if (item
->IsCheckable()) 
 557         bool isReallyChecked 
= item
->IsChecked(), 
 558             isInternallyChecked 
= item
->wxMenuItemBase::IsChecked(); 
 560         // ensure that the internal state is always consistent with what is 
 561         // shown on the screen 
 562         item
->wxMenuItemBase::Check(isReallyChecked
); 
 564         // we must not report the events for the radio button going up nor the 
 565         // events resulting from the calls to wxMenuItem::Check() 
 566         if ( (item
->GetKind() == wxITEM_RADIO 
&& !isReallyChecked
) || 
 567              (isInternallyChecked 
== isReallyChecked
) ) 
 574     // Is this menu on a menubar?  (possibly nested) 
 575     wxFrame
* frame 
= NULL
; 
 576     if(menu
->IsAttached()) 
 577         frame 
= menu
->GetMenuBar()->GetFrame(); 
 579     // FIXME: why do we have to call wxFrame::GetEventHandler() directly here? 
 580     //        normally wxMenu::SendEvent() should be enough, if it doesn't work 
 581     //        in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which 
 582     //        should be fixed instead of working around it here... 
 585         // If it is attached then let the frame send the event. 
 586         // Don't call frame->ProcessCommand(id) because it toggles 
 587         // checkable items and we've already done that above. 
 588         wxCommandEvent 
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
); 
 589         commandEvent
.SetEventObject(frame
); 
 590         if (item
->IsCheckable()) 
 591             commandEvent
.SetInt(item
->IsChecked()); 
 592         commandEvent
.SetEventObject(menu
); 
 594         frame
->GetEventHandler()->ProcessEvent(commandEvent
); 
 598         // otherwise let the menu have it 
 599         menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1); 
 604 //----------------------------------------------------------------------------- 
 606 //----------------------------------------------------------------------------- 
 609 static void gtk_menu_hilight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 611     if (g_isIdle
) wxapp_install_idle_handler(); 
 613     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 615     wxASSERT( id 
!= -1 ); // should find it! 
 617     if (!menu
->IsEnabled(id
)) 
 620     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, id 
); 
 621     event
.SetEventObject( menu 
); 
 623     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 624     if (handler 
&& handler
->ProcessEvent(event
)) 
 627     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 628     if (win
) win
->GetEventHandler()->ProcessEvent( event 
); 
 632 //----------------------------------------------------------------------------- 
 634 //----------------------------------------------------------------------------- 
 637 static void gtk_menu_nolight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 639     if (g_isIdle
) wxapp_install_idle_handler(); 
 641     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 643     wxASSERT( id 
!= -1 ); // should find it! 
 645     if (!menu
->IsEnabled(id
)) 
 648     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, -1 ); 
 649     event
.SetEventObject( menu 
); 
 651     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 652     if (handler 
&& handler
->ProcessEvent(event
)) 
 655     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 657         win
->GetEventHandler()->ProcessEvent( event 
); 
 661 //----------------------------------------------------------------------------- 
 663 //----------------------------------------------------------------------------- 
 665 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 667 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
 669                                 const wxString
& name
, 
 670                                 const wxString
& help
, 
 674     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
 677 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 679                        const wxString
& text
, 
 680                        const wxString
& help
, 
 683           : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
) 
 688 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 690                        const wxString
& text
, 
 691                        const wxString
& help
, 
 694           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 695                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 700 void wxMenuItem::Init(const wxString
& text
) 
 702     m_labelWidget 
= (GtkWidget 
*) NULL
; 
 703     m_menuItem 
= (GtkWidget 
*) NULL
; 
 708 wxMenuItem::~wxMenuItem() 
 710    // don't delete menu items, the menus take care of that 
 713 // return the menu item text without any menu accels 
 715 wxString 
wxMenuItemBase::GetLabelFromText(const wxString
& text
) 
 719     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 721         if ( *pc 
== wxT('\t')) 
 724         if ( *pc 
== wxT('_') ) 
 726             // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx" 
 732         if ( *pc 
== wxT('\\')  ) 
 734             // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx" 
 740         if ( (*pc 
== wxT('&')) && (*(pc
+1) != wxT('&')) ) 
 743             // "&" is doubled to indicate "&" instead of accelerator 
 750     // wxPrintf( wxT("GetLabelFromText(): text %s label %s\n"), text.c_str(), label.c_str() ); 
 755 void wxMenuItem::SetText( const wxString
& str 
) 
 757     // Some optimization to avoid flicker 
 758     wxString oldLabel 
= m_text
; 
 759     oldLabel 
= wxStripMenuCodes(oldLabel
); 
 760     oldLabel
.Replace(wxT("_"), wxT("")); 
 761     wxString label1 
= wxStripMenuCodes(str
); 
 762     wxString oldhotkey 
= GetHotKey();    // Store the old hotkey in Ctrl-foo format 
 763     wxCharBuffer oldbuf 
= wxGTK_CONV( GetGtkHotKey(*this) );  // and as <control>foo 
 767     if (oldLabel 
== label1 
&& 
 768              oldhotkey 
== GetHotKey())    // Make sure we can change a hotkey even if the label is unaltered 
 775             label 
= (GtkLabel
*) m_labelWidget
; 
 777             label 
= GTK_LABEL( GTK_BIN(m_menuItem
)->child 
); 
 779         gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(m_text
) ); 
 783     GdkModifierType accel_mods
; 
 784     gtk_accelerator_parse( (const char*) oldbuf
, &accel_key
, &accel_mods
); 
 787         gtk_widget_remove_accelerator( GTK_WIDGET(m_menuItem
), 
 788                                        m_parentMenu
->m_accel
, 
 793     wxCharBuffer buf 
= wxGTK_CONV( GetGtkHotKey(*this) ); 
 794     gtk_accelerator_parse( (const char*) buf
, &accel_key
, &accel_mods
); 
 797         gtk_widget_add_accelerator( GTK_WIDGET(m_menuItem
), 
 799                                     m_parentMenu
->m_accel
, 
 806 // it's valid for this function to be called even if m_menuItem == NULL 
 807 void wxMenuItem::DoSetText( const wxString
& str 
) 
 809     // '\t' is the deliminator indicating a hot key 
 811     const wxChar 
*pc 
= str
; 
 812     while ( (*pc 
!= wxT('\0')) && (*pc 
!= wxT('\t')) ) 
 814         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
 816             // "&" is doubled to indicate "&" instead of accelerator 
 820         else if (*pc 
== wxT('&')) 
 824         else if ( *pc 
== wxT('_') )    // escape underscores 
 843     // wxPrintf( wxT("DoSetText(): str %s m_text %s hotkey %s\n"), str.c_str(), m_text.c_str(), m_hotKey.c_str() ); 
 848 wxAcceleratorEntry 
*wxMenuItem::GetAccel() const 
 853         return (wxAcceleratorEntry 
*)NULL
; 
 856     // as wxGetAccelFromString() looks for TAB, insert a dummy one here 
 858     label 
<< wxT('\t') << GetHotKey(); 
 860     return wxGetAccelFromString(label
); 
 863 #endif // wxUSE_ACCEL 
 865 void wxMenuItem::Check( bool check 
) 
 867     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 869     if (check 
== m_isChecked
) 
 872     wxMenuItemBase::Check( check 
); 
 878             gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check 
); 
 882             wxFAIL_MSG( _T("can't check this item") ); 
 886 void wxMenuItem::Enable( bool enable 
) 
 888     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 890     gtk_widget_set_sensitive( m_menuItem
, enable 
); 
 891     wxMenuItemBase::Enable( enable 
); 
 894 bool wxMenuItem::IsChecked() const 
 896     wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") ); 
 898     wxCHECK_MSG( IsCheckable(), FALSE
, 
 899                  wxT("can't get state of uncheckable item!") ); 
 901     return ((GtkCheckMenuItem
*)m_menuItem
)->active 
!= 0; 
 904 //----------------------------------------------------------------------------- 
 906 //----------------------------------------------------------------------------- 
 908 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
) 
 912     m_accel 
= gtk_accel_group_new(); 
 913     m_menu 
= gtk_menu_new(); 
 914     // NB: keep reference to the menu so that it is not destroyed behind 
 915     //     our back by GTK+ e.g. when it is removed from menubar: 
 916     gtk_widget_ref(m_menu
); 
 918     m_owner 
= (GtkWidget
*) NULL
; 
 920     // Tearoffs are entries, just like separators. So if we want this 
 921     // menu to be a tear-off one, we just append a tearoff entry 
 923     if ( m_style 
& wxMENU_TEAROFF 
) 
 925         GtkWidget 
*tearoff 
= gtk_tearoff_menu_item_new(); 
 927         gtk_menu_append(GTK_MENU(m_menu
), tearoff
); 
 932     // append the title as the very first entry if we have it 
 933     if ( !m_title
.empty() ) 
 935         Append(wxGTK_TITLE_ID
, m_title
); 
 942    WX_CLEAR_LIST(wxMenuItemList
, m_items
); 
 944    if ( GTK_IS_WIDGET( m_menu 
)) 
 947        gtk_widget_unref( m_menu 
); 
 948        // if the menu is inserted in another menu at this time, there was 
 949        // one more reference to it: 
 951            gtk_widget_destroy( m_menu 
); 
 955 bool wxMenu::GtkAppend(wxMenuItem 
*mitem
, int pos
) 
 961     if ( mitem
->IsSeparator() ) 
 963         menuItem 
= gtk_separator_menu_item_new(); 
 965     else if (mitem
->GetBitmap().Ok()) 
 967         text 
= mitem
->GetText(); 
 968         const wxBitmap 
*bitmap 
= &mitem
->GetBitmap(); 
 970         menuItem 
= gtk_image_menu_item_new_with_mnemonic( wxGTK_CONV( text 
) ); 
 973         if (bitmap
->HasPixbuf()) 
 975             image 
= gtk_image_new_from_pixbuf(bitmap
->GetPixbuf()); 
 979             GdkPixmap 
*gdk_pixmap 
= bitmap
->GetPixmap(); 
 980             GdkBitmap 
*gdk_bitmap 
= bitmap
->GetMask() ? 
 981                                         bitmap
->GetMask()->GetBitmap() : 
 983             image 
= gtk_image_new_from_pixmap( gdk_pixmap
, gdk_bitmap 
); 
 986         gtk_widget_show(image
); 
 988         gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(menuItem
), image 
); 
 992     else // a normal item 
 994         // text has "_" instead of "&" after mitem->SetText() so don't use it 
 995         text 
=  mitem
->GetText() ; 
 997         switch ( mitem
->GetKind() ) 
1001                 menuItem 
= gtk_check_menu_item_new_with_mnemonic( wxGTK_CONV( text 
) ); 
1008                 GSList 
*group 
= NULL
; 
1009                 if ( m_prevRadio 
== NULL 
) 
1011                     // start of a new radio group 
1012                     m_prevRadio 
= menuItem 
= gtk_radio_menu_item_new_with_mnemonic( group
, wxGTK_CONV( text 
) ); 
1014                 else // continue the radio group 
1016                     group 
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (m_prevRadio
)); 
1017                     m_prevRadio 
= menuItem 
= gtk_radio_menu_item_new_with_mnemonic( group
, wxGTK_CONV( text 
) ); 
1023                 wxFAIL_MSG( _T("unexpected menu item kind") ); 
1028                 menuItem 
= gtk_menu_item_new_with_mnemonic( wxGTK_CONV( text 
) ); 
1037     GdkModifierType accel_mods
; 
1038     wxCharBuffer buf 
= wxGTK_CONV( GetGtkHotKey(*mitem
) ); 
1040     // wxPrintf( wxT("item: %s hotkey %s\n"), mitem->GetText().c_str(), GetGtkHotKey(*mitem).c_str() ); 
1041     gtk_accelerator_parse( (const char*) buf
, &accel_key
, &accel_mods
); 
1044         gtk_widget_add_accelerator (GTK_WIDGET(menuItem
), 
1053         gtk_menu_shell_append(GTK_MENU_SHELL(m_menu
), menuItem
); 
1055         gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu
), menuItem
, pos
); 
1057     gtk_widget_show( menuItem 
); 
1059     if ( !mitem
->IsSeparator() ) 
1061         wxASSERT_MSG( menuItem
, wxT("invalid menuitem") ); 
1063         g_signal_connect (menuItem
, "select", 
1064                           G_CALLBACK (gtk_menu_hilight_callback
), this); 
1065         g_signal_connect (menuItem
, "deselect", 
1066                           G_CALLBACK (gtk_menu_nolight_callback
), this); 
1068         if ( mitem
->IsSubMenu() && mitem
->GetKind() != wxITEM_RADIO 
&& mitem
->GetKind() != wxITEM_CHECK 
) 
1070             gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu 
); 
1072             gtk_widget_show( mitem
->GetSubMenu()->m_menu 
); 
1074             // if adding a submenu to a menu already existing in the menu bar, we 
1075             // must set invoking window to allow processing events from this 
1077             if ( m_invokingWindow 
) 
1078                 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
); 
1082             g_signal_connect (menuItem
, "activate", 
1083                               G_CALLBACK (gtk_menu_clicked_callback
), 
1088     mitem
->SetMenuItem(menuItem
); 
1092         // This doesn't even exist! 
1093         // gtk_widget_lock_accelerators(mitem->GetMenuItem()); 
1099 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*mitem
) 
1101     if (!GtkAppend(mitem
)) 
1104     return wxMenuBase::DoAppend(mitem
); 
1107 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
1109     if ( !wxMenuBase::DoInsert(pos
, item
) ) 
1113     if ( !GtkAppend(item
, (int)pos
) ) 
1119 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
1121     if ( !wxMenuBase::DoRemove(item
) ) 
1122         return (wxMenuItem 
*)NULL
; 
1124     // TODO: this code doesn't delete the item factory item and this seems 
1125     //       impossible as of GTK 1.2.6. 
1126     gtk_widget_destroy( item
->GetMenuItem() ); 
1131 int wxMenu::FindMenuIdByMenuItem( GtkWidget 
*menuItem 
) const 
1133     wxMenuItemList::compatibility_iterator node 
= m_items
.GetFirst(); 
1136         wxMenuItem 
*item 
= node
->GetData(); 
1137         if (item
->GetMenuItem() == menuItem
) 
1138             return item
->GetId(); 
1139         node 
= node
->GetNext(); 
1145 // ---------------------------------------------------------------------------- 
1147 // ---------------------------------------------------------------------------- 
1151 static wxString 
GetGtkHotKey( const wxMenuItem
& item 
) 
1155     wxAcceleratorEntry 
*accel 
= item
.GetAccel(); 
1158         int flags 
= accel
->GetFlags(); 
1159         if ( flags 
& wxACCEL_ALT 
) 
1160             hotkey 
+= wxT("<alt>"); 
1161         if ( flags 
& wxACCEL_CTRL 
) 
1162             hotkey 
+= wxT("<control>"); 
1163         if ( flags 
& wxACCEL_SHIFT 
) 
1164             hotkey 
+= wxT("<shift>"); 
1166         int code 
= accel
->GetKeyCode(); 
1193                 hotkey 
+= wxString::Format(wxT("F%d"), code 
- WXK_F1 
+ 1); 
1196                 // TODO: we should use gdk_keyval_name() (a.k.a. 
1197                 //       XKeysymToString) here as well as hardcoding the keysym 
1198                 //       names this might be not portable 
1200                 hotkey 
<< wxT("Insert" ); 
1203                 hotkey 
<< wxT("Delete" ); 
1206                 hotkey 
<< wxT("Up" ); 
1209                 hotkey 
<< wxT("Down" ); 
1213                 hotkey 
<< wxT("Prior" ); 
1217                 hotkey 
<< wxT("Next" ); 
1220                 hotkey 
<< wxT("Left" ); 
1223                 hotkey 
<< wxT("Right" ); 
1226                 hotkey 
<< wxT("Home" ); 
1229                 hotkey 
<< wxT("End" ); 
1232                 hotkey 
<< wxT("Return" ); 
1235                 hotkey 
<< wxT("BackSpace" ); 
1238                 hotkey 
<< wxT("Tab" ); 
1241                 hotkey 
<< wxT("Esc" ); 
1244                 hotkey 
<< wxT("space" ); 
1247                 hotkey 
<< wxT("Multiply" ); 
1250                 hotkey 
<< wxT("Add" ); 
1253                 hotkey 
<< wxT("Separator" ); 
1256                 hotkey 
<< wxT("Subtract" ); 
1259                 hotkey 
<< wxT("Decimal" ); 
1262                 hotkey 
<< wxT("Divide" ); 
1265                 hotkey 
<< wxT("Cancel" ); 
1268                 hotkey 
<< wxT("Clear" ); 
1271                 hotkey 
<< wxT("Menu" ); 
1274                 hotkey 
<< wxT("Pause" ); 
1277                 hotkey 
<< wxT("Capital" ); 
1280                 hotkey 
<< wxT("Select" ); 
1283                 hotkey 
<< wxT("Print" ); 
1286                 hotkey 
<< wxT("Execute" ); 
1289                 hotkey 
<< wxT("Snapshot" ); 
1292                 hotkey 
<< wxT("Help" ); 
1295                 hotkey 
<< wxT("Num_Lock" ); 
1298                 hotkey 
<< wxT("Scroll_Lock" ); 
1300             case WXK_NUMPAD_INSERT
: 
1301                 hotkey 
<< wxT("KP_Insert" ); 
1303             case WXK_NUMPAD_DELETE
: 
1304                 hotkey 
<< wxT("KP_Delete" ); 
1306              case WXK_NUMPAD_SPACE
: 
1307                 hotkey 
<< wxT("KP_Space" ); 
1309             case WXK_NUMPAD_TAB
: 
1310                 hotkey 
<< wxT("KP_Tab" ); 
1312             case WXK_NUMPAD_ENTER
: 
1313                 hotkey 
<< wxT("KP_Enter" ); 
1315             case WXK_NUMPAD_F1
: case WXK_NUMPAD_F2
: case WXK_NUMPAD_F3
: 
1317                 hotkey 
+= wxString::Format(wxT("KP_F%d"), code 
- WXK_NUMPAD_F1 
+ 1); 
1319             case WXK_NUMPAD_HOME
: 
1320                 hotkey 
<< wxT("KP_Home" ); 
1322             case WXK_NUMPAD_LEFT
: 
1323                 hotkey 
<< wxT("KP_Left" ); 
1326                 hotkey 
<< wxT("KP_Up" ); 
1328             case WXK_NUMPAD_RIGHT
: 
1329                 hotkey 
<< wxT("KP_Right" ); 
1331             case WXK_NUMPAD_DOWN
: 
1332                 hotkey 
<< wxT("KP_Down" ); 
1334             case WXK_NUMPAD_PRIOR
: case WXK_NUMPAD_PAGEUP
: 
1335                 hotkey 
<< wxT("KP_Prior" ); 
1337             case WXK_NUMPAD_NEXT
:  case WXK_NUMPAD_PAGEDOWN
: 
1338                 hotkey 
<< wxT("KP_Next" ); 
1340             case WXK_NUMPAD_END
: 
1341                 hotkey 
<< wxT("KP_End" ); 
1343             case WXK_NUMPAD_BEGIN
: 
1344                 hotkey 
<< wxT("KP_Begin" ); 
1346             case WXK_NUMPAD_EQUAL
: 
1347                 hotkey 
<< wxT("KP_Equal" ); 
1349             case WXK_NUMPAD_MULTIPLY
: 
1350                 hotkey 
<< wxT("KP_Multiply" ); 
1352             case WXK_NUMPAD_ADD
: 
1353                 hotkey 
<< wxT("KP_Add" ); 
1355             case WXK_NUMPAD_SEPARATOR
: 
1356                 hotkey 
<< wxT("KP_Separator" ); 
1358             case WXK_NUMPAD_SUBTRACT
: 
1359                 hotkey 
<< wxT("KP_Subtract" ); 
1361             case WXK_NUMPAD_DECIMAL
: 
1362                 hotkey 
<< wxT("KP_Decimal" ); 
1364             case WXK_NUMPAD_DIVIDE
: 
1365                 hotkey 
<< wxT("KP_Divide" ); 
1367            case WXK_NUMPAD0
: case WXK_NUMPAD1
: case WXK_NUMPAD2
: 
1368            case WXK_NUMPAD3
: case WXK_NUMPAD4
: case WXK_NUMPAD5
: 
1369            case WXK_NUMPAD6
: case WXK_NUMPAD7
: case WXK_NUMPAD8
: case WXK_NUMPAD9
: 
1370                 hotkey 
+= wxString::Format(wxT("KP_%d"), code 
- WXK_NUMPAD0
); 
1372             case WXK_WINDOWS_LEFT
: 
1373                 hotkey 
<< wxT("Super_L" ); 
1375             case WXK_WINDOWS_RIGHT
: 
1376                 hotkey 
<< wxT("Super_R" ); 
1378             case WXK_WINDOWS_MENU
: 
1379                 hotkey 
<< wxT("Menu" ); 
1382                 hotkey 
<< wxT("Command" ); 
1384           /* These probably wouldn't work as there is no SpecialX in gdk/keynames.txt 
1385             case WXK_SPECIAL1: case WXK_SPECIAL2: case WXK_SPECIAL3: case WXK_SPECIAL4: 
1386             case WXK_SPECIAL5: case WXK_SPECIAL6: case WXK_SPECIAL7: case WXK_SPECIAL8: 
1387             case WXK_SPECIAL9:  case WXK_SPECIAL10:  case WXK_SPECIAL11: case WXK_SPECIAL12: 
1388             case WXK_SPECIAL13: case WXK_SPECIAL14: case WXK_SPECIAL15: case WXK_SPECIAL16: 
1389             case WXK_SPECIAL17: case WXK_SPECIAL18: case WXK_SPECIAL19:  case WXK_SPECIAL20: 
1390                 hotkey += wxString::Format(wxT("Special%d"), code - WXK_SPECIAL1 + 1); 
1393                 // if there are any other keys wxGetAccelFromString() may 
1394                 // return, we should process them here 
1399                     wxString name 
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) ); 
1407                 wxFAIL_MSG( wxT("unknown keyboard accel") ); 
1416 #endif // wxUSE_ACCEL 
1418 // ---------------------------------------------------------------------------- 
1419 // Pop-up menu stuff 
1420 // ---------------------------------------------------------------------------- 
1422 #if wxUSE_MENUS_NATIVE 
1425 void gtk_pop_hide_callback( GtkWidget 
*WXUNUSED(widget
), bool* is_waiting  
) 
1427     *is_waiting 
= FALSE
; 
1430 void SetInvokingWindow( wxMenu 
*menu
, wxWindow
* win 
) 
1432     menu
->SetInvokingWindow( win 
); 
1434     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
1437         wxMenuItem 
*menuitem 
= node
->GetData(); 
1438         if (menuitem
->IsSubMenu()) 
1440             SetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
1443         node 
= node
->GetNext(); 
1448 void wxPopupMenuPositionCallback( GtkMenu 
*menu
, 
1450                                   gboolean 
* WXUNUSED(whatever
), 
1451                                   gpointer user_data 
) 
1453     // ensure that the menu appears entirely on screen 
1455     gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
); 
1457     wxSize sizeScreen 
= wxGetDisplaySize(); 
1458     wxPoint 
*pos 
= (wxPoint
*)user_data
; 
1460     gint xmax 
= sizeScreen
.x 
- req
.width
, 
1461          ymax 
= sizeScreen
.y 
- req
.height
; 
1463     *x 
= pos
->x 
< xmax 
? pos
->x 
: xmax
; 
1464     *y 
= pos
->y 
< ymax 
? pos
->y 
: ymax
; 
1467 bool wxWindowGTK::DoPopupMenu( wxMenu 
*menu
, int x
, int y 
) 
1469     wxCHECK_MSG( m_widget 
!= NULL
, false, wxT("invalid window") ); 
1471     wxCHECK_MSG( menu 
!= NULL
, false, wxT("invalid popup-menu") ); 
1473     // NOTE: if you change this code, you need to update 
1474     //       the same code in taskbar.cpp as well. This 
1475     //       is ugly code duplication, I know. 
1477     SetInvokingWindow( menu
, this ); 
1481     bool is_waiting 
= true; 
1483     gulong handler 
= g_signal_connect (menu
->m_menu
, "hide", 
1484                                        G_CALLBACK (gtk_pop_hide_callback
), 
1489     GtkMenuPositionFunc posfunc
; 
1490     if ( x 
== -1 && y 
== -1 ) 
1492         // use GTK's default positioning algorithm 
1498         pos 
= ClientToScreen(wxPoint(x
, y
)); 
1500         posfunc 
= wxPopupMenuPositionCallback
; 
1503     wxMenuEvent 
eventOpen(wxEVT_MENU_OPEN
, -1, menu
); 
1504     DoCommonMenuCallbackCode(menu
, eventOpen
); 
1507                   GTK_MENU(menu
->m_menu
), 
1508                   (GtkWidget 
*) NULL
,           // parent menu shell 
1509                   (GtkWidget 
*) NULL
,           // parent menu item 
1510                   posfunc
,                      // function to position it 
1511                   userdata
,                     // client data 
1512                   0,                            // button used to activate it 
1513                   gtk_get_current_event_time() 
1518         gtk_main_iteration(); 
1521     g_signal_handler_disconnect (menu
->m_menu
, handler
); 
1523     wxMenuEvent 
eventClose(wxEVT_MENU_CLOSE
, -1, menu
); 
1524     DoCommonMenuCallbackCode(menu
, eventClose
); 
1529 #endif // wxUSE_MENUS_NATIVE