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 //----------------------------------------------------------------------------- 
  52 static wxString 
GetGtkHotKey( const wxMenuItem
& item 
); 
  55 //----------------------------------------------------------------------------- 
  57 //----------------------------------------------------------------------------- 
  59 static wxString 
wxReplaceUnderscore( const wxString
& title 
) 
  63     // GTK 1.2 wants to have "_" instead of "&" for accelerators 
  66     while (*pc 
!= wxT('\0')) 
  68         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
  70             // "&" is doubled to indicate "&" instead of accelerator 
  74         else if (*pc 
== wxT('&')) 
  80             if ( *pc 
== wxT('_') ) 
  82                 // underscores must be doubled to prevent them from being 
  83                 // interpreted as accelerator character prefix by GTK 
  92     // wxPrintf( wxT("before %s after %s\n"), title.c_str(), str.c_str() ); 
  97 //----------------------------------------------------------------------------- 
  98 // activate message from GTK 
  99 //----------------------------------------------------------------------------- 
 101 static void DoCommonMenuCallbackCode(wxMenu 
*menu
, wxMenuEvent
& event
) 
 104         wxapp_install_idle_handler(); 
 106     event
.SetEventObject( menu 
); 
 108     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 109     if (handler 
&& handler
->ProcessEvent(event
)) 
 112     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 114         win
->GetEventHandler()->ProcessEvent( event 
); 
 119 static void gtk_menu_open_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 121     wxMenuEvent 
event(wxEVT_MENU_OPEN
, -1, menu
); 
 123     DoCommonMenuCallbackCode(menu
, event
); 
 126 static void gtk_menu_close_callback( GtkWidget 
*widget
, wxMenuBar 
*menubar 
) 
 128     if ( !menubar
->GetMenuCount() ) 
 130         // if menubar is empty we can't call GetMenu(0) below 
 134     wxMenuEvent 
event( wxEVT_MENU_CLOSE
, -1, NULL 
); 
 136     DoCommonMenuCallbackCode(menubar
->GetMenu(0), event
); 
 141 //----------------------------------------------------------------------------- 
 143 //----------------------------------------------------------------------------- 
 145 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
) 
 147 void wxMenuBar::Init(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long style
) 
 149     // the parent window is known after wxFrame::SetMenu() 
 150     m_needParent 
= FALSE
; 
 152     m_invokingWindow 
= (wxWindow
*) NULL
; 
 154     if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize 
) || 
 155         !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") )) 
 157         wxFAIL_MSG( wxT("wxMenuBar creation failed") ); 
 161     m_menubar 
= gtk_menu_bar_new(); 
 163     if (style 
& wxMB_DOCKABLE
) 
 165         m_widget 
= gtk_handle_box_new(); 
 166         gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) ); 
 167         gtk_widget_show( GTK_WIDGET(m_menubar
) ); 
 171         m_widget 
= GTK_WIDGET(m_menubar
); 
 178     for (size_t i 
= 0; i 
< n
; ++i 
) 
 179         Append(menus
[i
], titles
[i
]); 
 181     // VZ: for some reason connecting to menus "deactivate" doesn't work (we 
 182     //     don't get it when the menu is dismissed by clicking outside the 
 183     //     toolbar) so we connect to the global one, even if it means that we 
 184     //     can't pass the menu which was closed in wxMenuEvent object 
 185     g_signal_connect (m_menubar
, "deactivate", 
 186                       G_CALLBACK (gtk_menu_close_callback
), this); 
 190 wxMenuBar::wxMenuBar(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long style
) 
 192     Init(n
, menus
, titles
, style
); 
 195 wxMenuBar::wxMenuBar(long style
) 
 197     Init(0, NULL
, NULL
, style
); 
 200 wxMenuBar::wxMenuBar() 
 202     Init(0, NULL
, NULL
, 0); 
 205 wxMenuBar::~wxMenuBar() 
 209 static void wxMenubarUnsetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 211     menu
->SetInvokingWindow( (wxWindow
*) NULL 
); 
 213     wxWindow 
*top_frame 
= win
; 
 214     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 215         top_frame 
= top_frame
->GetParent(); 
 217     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 220         wxMenuItem 
*menuitem 
= node
->GetData(); 
 221         if (menuitem
->IsSubMenu()) 
 222             wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 223         node 
= node
->GetNext(); 
 227 static void wxMenubarSetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 229     menu
->SetInvokingWindow( win 
); 
 231     wxWindow 
*top_frame 
= win
; 
 232     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 233         top_frame 
= top_frame
->GetParent(); 
 235     // support for native hot keys 
 236     ACCEL_OBJECT 
*obj 
= ACCEL_OBJ_CAST(top_frame
->m_widget
); 
 237     if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj 
) ) 
 238         gtk_accel_group_attach( menu
->m_accel
, obj 
); 
 240     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 243         wxMenuItem 
*menuitem 
= node
->GetData(); 
 244         if (menuitem
->IsSubMenu()) 
 245             wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 246         node 
= node
->GetNext(); 
 250 void wxMenuBar::SetInvokingWindow( wxWindow 
*win 
) 
 252     m_invokingWindow 
= win
; 
 253     wxWindow 
*top_frame 
= win
; 
 254     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 255         top_frame 
= top_frame
->GetParent(); 
 257     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 260         wxMenu 
*menu 
= node
->GetData(); 
 261         wxMenubarSetInvokingWindow( menu
, win 
); 
 262         node 
= node
->GetNext(); 
 266 void wxMenuBar::UnsetInvokingWindow( wxWindow 
*win 
) 
 268     m_invokingWindow 
= (wxWindow
*) NULL
; 
 269     wxWindow 
*top_frame 
= win
; 
 270     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 271         top_frame 
= top_frame
->GetParent(); 
 273     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 276         wxMenu 
*menu 
= node
->GetData(); 
 277         wxMenubarUnsetInvokingWindow( menu
, win 
); 
 278         node 
= node
->GetNext(); 
 282 bool wxMenuBar::Append( wxMenu 
*menu
, const wxString 
&title 
) 
 284     if ( !wxMenuBarBase::Append( menu
, title 
) ) 
 287     return GtkAppend(menu
, title
); 
 290 bool wxMenuBar::GtkAppend(wxMenu 
*menu
, const wxString
& title
, int pos
) 
 292     wxString 
str( wxReplaceUnderscore( title 
) ); 
 294     // This doesn't have much effect right now. 
 295     menu
->SetTitle( str 
); 
 297     // The "m_owner" is the "menu item" 
 298     menu
->m_owner 
= gtk_menu_item_new_with_mnemonic( wxGTK_CONV( str 
) ); 
 300     gtk_widget_show( menu
->m_owner 
); 
 302     gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu 
); 
 305         gtk_menu_shell_append( GTK_MENU_SHELL(m_menubar
), menu
->m_owner 
); 
 307         gtk_menu_shell_insert( GTK_MENU_SHELL(m_menubar
), menu
->m_owner
, pos 
); 
 309     g_signal_connect (menu
->m_owner
, "activate", 
 310                       G_CALLBACK (gtk_menu_open_callback
), 
 313     // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables 
 314     // addings menu later on. 
 315     if (m_invokingWindow
) 
 317         wxMenubarSetInvokingWindow( menu
, m_invokingWindow 
); 
 319             // OPTIMISE ME:  we should probably cache this, or pass it 
 320             //               directly, but for now this is a minimal 
 321             //               change to validate the new dynamic sizing. 
 322             //               see (and refactor :) similar code in Remove 
 325         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 328             frame
->UpdateMenuBarSize(); 
 334 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 336     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
 341     if ( !GtkAppend(menu
, title
, (int)pos
) ) 
 347 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 349     // remove the old item and insert a new one 
 350     wxMenu 
*menuOld 
= Remove(pos
); 
 351     if ( menuOld 
&& !Insert(pos
, menu
, title
) ) 
 353         return (wxMenu
*) NULL
; 
 356     // either Insert() succeeded or Remove() failed and menuOld is NULL 
 360 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
 362     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
 364         return (wxMenu
*) NULL
; 
 366     gtk_menu_item_remove_submenu( GTK_MENU_ITEM(menu
->m_owner
) ); 
 367     gtk_container_remove(GTK_CONTAINER(m_menubar
), menu
->m_owner
); 
 369     gtk_widget_destroy( menu
->m_owner 
); 
 370     menu
->m_owner 
= NULL
; 
 372     if (m_invokingWindow
) 
 374         // OPTIMISE ME:  see comment in GtkAppend 
 375         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 378             frame
->UpdateMenuBarSize(); 
 384 static int FindMenuItemRecursive( const wxMenu 
*menu
, const wxString 
&menuString
, const wxString 
&itemString 
) 
 386     if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
)) 
 388         int res 
= menu
->FindItem( itemString 
); 
 389         if (res 
!= wxNOT_FOUND
) 
 393     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 396         wxMenuItem 
*item 
= node
->GetData(); 
 397         if (item
->IsSubMenu()) 
 398             return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
); 
 400         node 
= node
->GetNext(); 
 406 int wxMenuBar::FindMenuItem( const wxString 
&menuString
, const wxString 
&itemString 
) const 
 408     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 411         wxMenu 
*menu 
= node
->GetData(); 
 412         int res 
= FindMenuItemRecursive( menu
, menuString
, itemString
); 
 415         node 
= node
->GetNext(); 
 421 // Find a wxMenuItem using its id. Recurses down into sub-menus 
 422 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
) 
 424     wxMenuItem
* result 
= menu
->FindChildItem(id
); 
 426     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
 427     while ( node 
&& result 
== NULL 
) 
 429         wxMenuItem 
*item 
= node
->GetData(); 
 430         if (item
->IsSubMenu()) 
 432             result 
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id 
); 
 434         node 
= node
->GetNext(); 
 440 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu 
**menuForItem 
) const 
 442     wxMenuItem
* result 
= 0; 
 443     wxMenuList::compatibility_iterator node 
= m_menus
.GetFirst(); 
 444     while (node 
&& result 
== 0) 
 446         wxMenu 
*menu 
= node
->GetData(); 
 447         result 
= FindMenuItemByIdRecursive( menu
, id 
); 
 448         node 
= node
->GetNext(); 
 453         *menuForItem 
= result 
? result
->GetMenu() : (wxMenu 
*)NULL
; 
 459 void wxMenuBar::EnableTop( size_t pos
, bool flag 
) 
 461     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 463     wxCHECK_RET( node
, wxT("menu not found") ); 
 465     wxMenu
* menu 
= node
->GetData(); 
 468         gtk_widget_set_sensitive( menu
->m_owner
, flag 
); 
 471 wxString 
wxMenuBar::GetLabelTop( size_t pos 
) const 
 473     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 475     wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") ); 
 477     wxMenu
* menu 
= node
->GetData(); 
 480     wxString 
text( menu
->GetTitle() ); 
 481     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 483         if ( *pc 
== wxT('_') ) 
 485             // '_' is the escape character for GTK+ 
 489         // don't remove ampersands '&' since if we have them in the menu title 
 490         // it means that they were doubled to indicate "&" instead of accelerator 
 498 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label 
) 
 500     wxMenuList::compatibility_iterator node 
= m_menus
.Item( pos 
); 
 502     wxCHECK_RET( node
, wxT("menu not found") ); 
 504     wxMenu
* menu 
= node
->GetData(); 
 506     const wxString 
str( wxReplaceUnderscore( label 
) ); 
 508     menu
->SetTitle( str 
); 
 511         gtk_label_set_text_with_mnemonic( GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
), wxGTK_CONV(str
) ); 
 514 //----------------------------------------------------------------------------- 
 516 //----------------------------------------------------------------------------- 
 519 static void gtk_menu_clicked_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 522         wxapp_install_idle_handler(); 
 524     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 526     /* should find it for normal (not popup) menu */ 
 527     wxASSERT_MSG( (id 
!= -1) || (menu
->GetInvokingWindow() != NULL
), 
 528                   _T("menu item not found in gtk_menu_clicked_callback") ); 
 530     if (!menu
->IsEnabled(id
)) 
 533     wxMenuItem
* item 
= menu
->FindChildItem( id 
); 
 534     wxCHECK_RET( item
, wxT("error in menu item callback") ); 
 536     if ( item
->GetId() == wxGTK_TITLE_ID 
) 
 538         // ignore events from the menu title 
 542     if (item
->IsCheckable()) 
 544         bool isReallyChecked 
= item
->IsChecked(), 
 545             isInternallyChecked 
= item
->wxMenuItemBase::IsChecked(); 
 547         // ensure that the internal state is always consistent with what is 
 548         // shown on the screen 
 549         item
->wxMenuItemBase::Check(isReallyChecked
); 
 551         // we must not report the events for the radio button going up nor the 
 552         // events resulting from the calls to wxMenuItem::Check() 
 553         if ( (item
->GetKind() == wxITEM_RADIO 
&& !isReallyChecked
) || 
 554              (isInternallyChecked 
== isReallyChecked
) ) 
 561     // Is this menu on a menubar?  (possibly nested) 
 562     wxFrame
* frame 
= NULL
; 
 563     if(menu
->IsAttached()) 
 564         frame 
= menu
->GetMenuBar()->GetFrame(); 
 566     // FIXME: why do we have to call wxFrame::GetEventHandler() directly here? 
 567     //        normally wxMenu::SendEvent() should be enough, if it doesn't work 
 568     //        in wxGTK then we have a bug in wxMenu::GetInvokingWindow() which 
 569     //        should be fixed instead of working around it here... 
 572         // If it is attached then let the frame send the event. 
 573         // Don't call frame->ProcessCommand(id) because it toggles 
 574         // checkable items and we've already done that above. 
 575         wxCommandEvent 
commandEvent(wxEVT_COMMAND_MENU_SELECTED
, id
); 
 576         commandEvent
.SetEventObject(frame
); 
 577         if (item
->IsCheckable()) 
 578             commandEvent
.SetInt(item
->IsChecked()); 
 579         commandEvent
.SetEventObject(menu
); 
 581         frame
->GetEventHandler()->ProcessEvent(commandEvent
); 
 585         // otherwise let the menu have it 
 586         menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1); 
 591 //----------------------------------------------------------------------------- 
 593 //----------------------------------------------------------------------------- 
 596 static void gtk_menu_hilight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 598     if (g_isIdle
) wxapp_install_idle_handler(); 
 600     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 602     wxASSERT( id 
!= -1 ); // should find it! 
 604     if (!menu
->IsEnabled(id
)) 
 607     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, id 
); 
 608     event
.SetEventObject( menu 
); 
 610     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 611     if (handler 
&& handler
->ProcessEvent(event
)) 
 614     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 615     if (win
) win
->GetEventHandler()->ProcessEvent( event 
); 
 619 //----------------------------------------------------------------------------- 
 621 //----------------------------------------------------------------------------- 
 624 static void gtk_menu_nolight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 626     if (g_isIdle
) wxapp_install_idle_handler(); 
 628     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 630     wxASSERT( id 
!= -1 ); // should find it! 
 632     if (!menu
->IsEnabled(id
)) 
 635     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, -1 ); 
 636     event
.SetEventObject( menu 
); 
 638     wxEvtHandler
* handler 
= menu
->GetEventHandler(); 
 639     if (handler 
&& handler
->ProcessEvent(event
)) 
 642     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 644         win
->GetEventHandler()->ProcessEvent( event 
); 
 648 //----------------------------------------------------------------------------- 
 650 //----------------------------------------------------------------------------- 
 652 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 654 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
 656                                 const wxString
& name
, 
 657                                 const wxString
& help
, 
 661     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
 664 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 666                        const wxString
& text
, 
 667                        const wxString
& help
, 
 670           : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
) 
 675 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 677                        const wxString
& text
, 
 678                        const wxString
& help
, 
 681           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 682                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 687 void wxMenuItem::Init(const wxString
& text
) 
 689     m_labelWidget 
= (GtkWidget 
*) NULL
; 
 690     m_menuItem 
= (GtkWidget 
*) NULL
; 
 695 wxMenuItem::~wxMenuItem() 
 697    // don't delete menu items, the menus take care of that 
 700 // return the menu item text without any menu accels 
 702 wxString 
wxMenuItemBase::GetLabelFromText(const wxString
& text
) 
 706     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 708         if ( *pc 
== wxT('\t')) 
 711         if ( *pc 
== wxT('_') ) 
 713             // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx" 
 719         if ( *pc 
== wxT('\\')  ) 
 721             // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx" 
 727         if ( (*pc 
== wxT('&')) && (*(pc
+1) != wxT('&')) ) 
 730             // "&" is doubled to indicate "&" instead of accelerator 
 737     // wxPrintf( wxT("GetLabelFromText(): text %s label %s\n"), text.c_str(), label.c_str() ); 
 742 void wxMenuItem::SetText( const wxString
& str 
) 
 744     // Some optimization to avoid flicker 
 745     wxString oldLabel 
= m_text
; 
 746     oldLabel 
= wxStripMenuCodes(oldLabel
); 
 747     oldLabel
.Replace(wxT("_"), wxT("")); 
 748     wxString label1 
= wxStripMenuCodes(str
); 
 749     wxString oldhotkey 
= GetHotKey();    // Store the old hotkey in Ctrl-foo format 
 750     wxCharBuffer oldbuf 
= wxGTK_CONV( GetGtkHotKey(*this) );  // and as <control>foo 
 754     if (oldLabel 
== label1 
&& 
 755              oldhotkey 
== GetHotKey())    // Make sure we can change a hotkey even if the label is unaltered 
 762             label 
= (GtkLabel
*) m_labelWidget
; 
 764             label 
= GTK_LABEL( GTK_BIN(m_menuItem
)->child 
); 
 766         gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(m_text
) ); 
 770     GdkModifierType accel_mods
; 
 771     gtk_accelerator_parse( (const char*) oldbuf
, &accel_key
, &accel_mods
); 
 774         gtk_widget_remove_accelerator( GTK_WIDGET(m_menuItem
), 
 775                                        m_parentMenu
->m_accel
, 
 780     wxCharBuffer buf 
= wxGTK_CONV( GetGtkHotKey(*this) ); 
 781     gtk_accelerator_parse( (const char*) buf
, &accel_key
, &accel_mods
); 
 784         gtk_widget_add_accelerator( GTK_WIDGET(m_menuItem
), 
 786                                     m_parentMenu
->m_accel
, 
 793 // it's valid for this function to be called even if m_menuItem == NULL 
 794 void wxMenuItem::DoSetText( const wxString
& str 
) 
 796     // '\t' is the deliminator indicating a hot key 
 798     const wxChar 
*pc 
= str
; 
 799     while ( (*pc 
!= wxT('\0')) && (*pc 
!= wxT('\t')) ) 
 801         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
 803             // "&" is doubled to indicate "&" instead of accelerator 
 807         else if (*pc 
== wxT('&')) 
 811         else if ( *pc 
== wxT('_') )    // escape underscores 
 830     // wxPrintf( wxT("DoSetText(): str %s m_text %s hotkey %s\n"), str.c_str(), m_text.c_str(), m_hotKey.c_str() ); 
 835 wxAcceleratorEntry 
*wxMenuItem::GetAccel() const 
 840         return (wxAcceleratorEntry 
*)NULL
; 
 843     // as wxGetAccelFromString() looks for TAB, insert a dummy one here 
 845     label 
<< wxT('\t') << GetHotKey(); 
 847     return wxGetAccelFromString(label
); 
 850 #endif // wxUSE_ACCEL 
 852 void wxMenuItem::Check( bool check 
) 
 854     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 856     if (check 
== m_isChecked
) 
 859     wxMenuItemBase::Check( check 
); 
 865             gtk_check_menu_item_set_active( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check 
); 
 869             wxFAIL_MSG( _T("can't check this item") ); 
 873 void wxMenuItem::Enable( bool enable 
) 
 875     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 877     gtk_widget_set_sensitive( m_menuItem
, enable 
); 
 878     wxMenuItemBase::Enable( enable 
); 
 881 bool wxMenuItem::IsChecked() const 
 883     wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") ); 
 885     wxCHECK_MSG( IsCheckable(), FALSE
, 
 886                  wxT("can't get state of uncheckable item!") ); 
 888     return ((GtkCheckMenuItem
*)m_menuItem
)->active 
!= 0; 
 891 //----------------------------------------------------------------------------- 
 893 //----------------------------------------------------------------------------- 
 895 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
) 
 899     m_accel 
= gtk_accel_group_new(); 
 900     m_menu 
= gtk_menu_new(); 
 901     // NB: keep reference to the menu so that it is not destroyed behind 
 902     //     our back by GTK+ e.g. when it is removed from menubar: 
 903     gtk_widget_ref(m_menu
); 
 905     m_owner 
= (GtkWidget
*) NULL
; 
 907     // Tearoffs are entries, just like separators. So if we want this 
 908     // menu to be a tear-off one, we just append a tearoff entry 
 910     if ( m_style 
& wxMENU_TEAROFF 
) 
 912         GtkWidget 
*tearoff 
= gtk_tearoff_menu_item_new(); 
 914         gtk_menu_shell_append(GTK_MENU_SHELL(m_menu
), tearoff
); 
 919     // append the title as the very first entry if we have it 
 920     if ( !m_title
.empty() ) 
 922         Append(wxGTK_TITLE_ID
, m_title
); 
 929    WX_CLEAR_LIST(wxMenuItemList
, m_items
); 
 931    if ( GTK_IS_WIDGET( m_menu 
)) 
 934        gtk_widget_unref( m_menu 
); 
 935        // if the menu is inserted in another menu at this time, there was 
 936        // one more reference to it: 
 938            gtk_widget_destroy( m_menu 
); 
 942 bool wxMenu::GtkAppend(wxMenuItem 
*mitem
, int pos
) 
 948     if ( mitem
->IsSeparator() ) 
 950         menuItem 
= gtk_separator_menu_item_new(); 
 952     else if (mitem
->GetBitmap().Ok()) 
 954         text 
= mitem
->GetText(); 
 955         const wxBitmap 
*bitmap 
= &mitem
->GetBitmap(); 
 957         menuItem 
= gtk_image_menu_item_new_with_mnemonic( wxGTK_CONV( text 
) ); 
 960         if (bitmap
->HasPixbuf()) 
 962             image 
= gtk_image_new_from_pixbuf(bitmap
->GetPixbuf()); 
 966             GdkPixmap 
*gdk_pixmap 
= bitmap
->GetPixmap(); 
 967             GdkBitmap 
*gdk_bitmap 
= bitmap
->GetMask() ? 
 968                                         bitmap
->GetMask()->GetBitmap() : 
 970             image 
= gtk_image_new_from_pixmap( gdk_pixmap
, gdk_bitmap 
); 
 973         gtk_widget_show(image
); 
 975         gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(menuItem
), image 
); 
 979     else // a normal item 
 981         // text has "_" instead of "&" after mitem->SetText() so don't use it 
 982         text 
=  mitem
->GetText() ; 
 984         switch ( mitem
->GetKind() ) 
 988                 menuItem 
= gtk_check_menu_item_new_with_mnemonic( wxGTK_CONV( text 
) ); 
 995                 GSList 
*group 
= NULL
; 
 996                 if ( m_prevRadio 
== NULL 
) 
 998                     // start of a new radio group 
 999                     m_prevRadio 
= menuItem 
= gtk_radio_menu_item_new_with_mnemonic( group
, wxGTK_CONV( text 
) ); 
1001                 else // continue the radio group 
1003                     group 
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (m_prevRadio
)); 
1004                     m_prevRadio 
= menuItem 
= gtk_radio_menu_item_new_with_mnemonic( group
, wxGTK_CONV( text 
) ); 
1010                 wxFAIL_MSG( _T("unexpected menu item kind") ); 
1015                 menuItem 
= gtk_menu_item_new_with_mnemonic( wxGTK_CONV( text 
) ); 
1024     GdkModifierType accel_mods
; 
1025     wxCharBuffer buf 
= wxGTK_CONV( GetGtkHotKey(*mitem
) ); 
1027     // wxPrintf( wxT("item: %s hotkey %s\n"), mitem->GetText().c_str(), GetGtkHotKey(*mitem).c_str() ); 
1028     gtk_accelerator_parse( (const char*) buf
, &accel_key
, &accel_mods
); 
1031         gtk_widget_add_accelerator (GTK_WIDGET(menuItem
), 
1040         gtk_menu_shell_append(GTK_MENU_SHELL(m_menu
), menuItem
); 
1042         gtk_menu_shell_insert(GTK_MENU_SHELL(m_menu
), menuItem
, pos
); 
1044     gtk_widget_show( menuItem 
); 
1046     if ( !mitem
->IsSeparator() ) 
1048         wxASSERT_MSG( menuItem
, wxT("invalid menuitem") ); 
1050         g_signal_connect (menuItem
, "select", 
1051                           G_CALLBACK (gtk_menu_hilight_callback
), this); 
1052         g_signal_connect (menuItem
, "deselect", 
1053                           G_CALLBACK (gtk_menu_nolight_callback
), this); 
1055         if ( mitem
->IsSubMenu() && mitem
->GetKind() != wxITEM_RADIO 
&& mitem
->GetKind() != wxITEM_CHECK 
) 
1057             gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu 
); 
1059             gtk_widget_show( mitem
->GetSubMenu()->m_menu 
); 
1061             // if adding a submenu to a menu already existing in the menu bar, we 
1062             // must set invoking window to allow processing events from this 
1064             if ( m_invokingWindow 
) 
1065                 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
); 
1069             g_signal_connect (menuItem
, "activate", 
1070                               G_CALLBACK (gtk_menu_clicked_callback
), 
1075     mitem
->SetMenuItem(menuItem
); 
1079         // This doesn't even exist! 
1080         // gtk_widget_lock_accelerators(mitem->GetMenuItem()); 
1086 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*mitem
) 
1088     if (!GtkAppend(mitem
)) 
1091     return wxMenuBase::DoAppend(mitem
); 
1094 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
1096     if ( !wxMenuBase::DoInsert(pos
, item
) ) 
1100     if ( !GtkAppend(item
, (int)pos
) ) 
1106 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
1108     if ( !wxMenuBase::DoRemove(item
) ) 
1109         return (wxMenuItem 
*)NULL
; 
1111     // TODO: this code doesn't delete the item factory item and this seems 
1112     //       impossible as of GTK 1.2.6. 
1113     gtk_widget_destroy( item
->GetMenuItem() ); 
1118 int wxMenu::FindMenuIdByMenuItem( GtkWidget 
*menuItem 
) const 
1120     wxMenuItemList::compatibility_iterator node 
= m_items
.GetFirst(); 
1123         wxMenuItem 
*item 
= node
->GetData(); 
1124         if (item
->GetMenuItem() == menuItem
) 
1125             return item
->GetId(); 
1126         node 
= node
->GetNext(); 
1132 // ---------------------------------------------------------------------------- 
1134 // ---------------------------------------------------------------------------- 
1138 static wxString 
GetGtkHotKey( const wxMenuItem
& item 
) 
1142     wxAcceleratorEntry 
*accel 
= item
.GetAccel(); 
1145         int flags 
= accel
->GetFlags(); 
1146         if ( flags 
& wxACCEL_ALT 
) 
1147             hotkey 
+= wxT("<alt>"); 
1148         if ( flags 
& wxACCEL_CTRL 
) 
1149             hotkey 
+= wxT("<control>"); 
1150         if ( flags 
& wxACCEL_SHIFT 
) 
1151             hotkey 
+= wxT("<shift>"); 
1153         int code 
= accel
->GetKeyCode(); 
1180                 hotkey 
+= wxString::Format(wxT("F%d"), code 
- WXK_F1 
+ 1); 
1183                 // TODO: we should use gdk_keyval_name() (a.k.a. 
1184                 //       XKeysymToString) here as well as hardcoding the keysym 
1185                 //       names this might be not portable 
1187                 hotkey 
<< wxT("Insert" ); 
1190                 hotkey 
<< wxT("Delete" ); 
1193                 hotkey 
<< wxT("Up" ); 
1196                 hotkey 
<< wxT("Down" ); 
1199                 hotkey 
<< wxT("PgUp" ); 
1202                 hotkey 
<< wxT("PgDn" ); 
1205                 hotkey 
<< wxT("Left" ); 
1208                 hotkey 
<< wxT("Right" ); 
1211                 hotkey 
<< wxT("Home" ); 
1214                 hotkey 
<< wxT("End" ); 
1217                 hotkey 
<< wxT("Return" ); 
1220                 hotkey 
<< wxT("BackSpace" ); 
1223                 hotkey 
<< wxT("Tab" ); 
1226                 hotkey 
<< wxT("Esc" ); 
1229                 hotkey 
<< wxT("space" ); 
1232                 hotkey 
<< wxT("Multiply" ); 
1235                 hotkey 
<< wxT("Add" ); 
1238                 hotkey 
<< wxT("Separator" ); 
1241                 hotkey 
<< wxT("Subtract" ); 
1244                 hotkey 
<< wxT("Decimal" ); 
1247                 hotkey 
<< wxT("Divide" ); 
1250                 hotkey 
<< wxT("Cancel" ); 
1253                 hotkey 
<< wxT("Clear" ); 
1256                 hotkey 
<< wxT("Menu" ); 
1259                 hotkey 
<< wxT("Pause" ); 
1262                 hotkey 
<< wxT("Capital" ); 
1265                 hotkey 
<< wxT("Select" ); 
1268                 hotkey 
<< wxT("Print" ); 
1271                 hotkey 
<< wxT("Execute" ); 
1274                 hotkey 
<< wxT("Snapshot" ); 
1277                 hotkey 
<< wxT("Help" ); 
1280                 hotkey 
<< wxT("Num_Lock" ); 
1283                 hotkey 
<< wxT("Scroll_Lock" ); 
1285             case WXK_NUMPAD_INSERT
: 
1286                 hotkey 
<< wxT("KP_Insert" ); 
1288             case WXK_NUMPAD_DELETE
: 
1289                 hotkey 
<< wxT("KP_Delete" ); 
1291              case WXK_NUMPAD_SPACE
: 
1292                 hotkey 
<< wxT("KP_Space" ); 
1294             case WXK_NUMPAD_TAB
: 
1295                 hotkey 
<< wxT("KP_Tab" ); 
1297             case WXK_NUMPAD_ENTER
: 
1298                 hotkey 
<< wxT("KP_Enter" ); 
1300             case WXK_NUMPAD_F1
: case WXK_NUMPAD_F2
: case WXK_NUMPAD_F3
: 
1302                 hotkey 
+= wxString::Format(wxT("KP_F%d"), code 
- WXK_NUMPAD_F1 
+ 1); 
1304             case WXK_NUMPAD_HOME
: 
1305                 hotkey 
<< wxT("KP_Home" ); 
1307             case WXK_NUMPAD_LEFT
: 
1308                 hotkey 
<< wxT("KP_Left" ); 
1311                 hotkey 
<< wxT("KP_Up" ); 
1313             case WXK_NUMPAD_RIGHT
: 
1314                 hotkey 
<< wxT("KP_Right" ); 
1316             case WXK_NUMPAD_DOWN
: 
1317                 hotkey 
<< wxT("KP_Down" ); 
1319             case WXK_NUMPAD_PAGEUP
: 
1320                 hotkey 
<< wxT("KP_PgUp" ); 
1322             case WXK_NUMPAD_PAGEDOWN
: 
1323                 hotkey 
<< wxT("KP_PgDn" ); 
1325             case WXK_NUMPAD_END
: 
1326                 hotkey 
<< wxT("KP_End" ); 
1328             case WXK_NUMPAD_BEGIN
: 
1329                 hotkey 
<< wxT("KP_Begin" ); 
1331             case WXK_NUMPAD_EQUAL
: 
1332                 hotkey 
<< wxT("KP_Equal" ); 
1334             case WXK_NUMPAD_MULTIPLY
: 
1335                 hotkey 
<< wxT("KP_Multiply" ); 
1337             case WXK_NUMPAD_ADD
: 
1338                 hotkey 
<< wxT("KP_Add" ); 
1340             case WXK_NUMPAD_SEPARATOR
: 
1341                 hotkey 
<< wxT("KP_Separator" ); 
1343             case WXK_NUMPAD_SUBTRACT
: 
1344                 hotkey 
<< wxT("KP_Subtract" ); 
1346             case WXK_NUMPAD_DECIMAL
: 
1347                 hotkey 
<< wxT("KP_Decimal" ); 
1349             case WXK_NUMPAD_DIVIDE
: 
1350                 hotkey 
<< wxT("KP_Divide" ); 
1352            case WXK_NUMPAD0
: case WXK_NUMPAD1
: case WXK_NUMPAD2
: 
1353            case WXK_NUMPAD3
: case WXK_NUMPAD4
: case WXK_NUMPAD5
: 
1354            case WXK_NUMPAD6
: case WXK_NUMPAD7
: case WXK_NUMPAD8
: case WXK_NUMPAD9
: 
1355                 hotkey 
+= wxString::Format(wxT("KP_%d"), code 
- WXK_NUMPAD0
); 
1357             case WXK_WINDOWS_LEFT
: 
1358                 hotkey 
<< wxT("Super_L" ); 
1360             case WXK_WINDOWS_RIGHT
: 
1361                 hotkey 
<< wxT("Super_R" ); 
1363             case WXK_WINDOWS_MENU
: 
1364                 hotkey 
<< wxT("Menu" ); 
1367                 hotkey 
<< wxT("Command" ); 
1369           /* These probably wouldn't work as there is no SpecialX in gdk/keynames.txt 
1370             case WXK_SPECIAL1: case WXK_SPECIAL2: case WXK_SPECIAL3: case WXK_SPECIAL4: 
1371             case WXK_SPECIAL5: case WXK_SPECIAL6: case WXK_SPECIAL7: case WXK_SPECIAL8: 
1372             case WXK_SPECIAL9:  case WXK_SPECIAL10:  case WXK_SPECIAL11: case WXK_SPECIAL12: 
1373             case WXK_SPECIAL13: case WXK_SPECIAL14: case WXK_SPECIAL15: case WXK_SPECIAL16: 
1374             case WXK_SPECIAL17: case WXK_SPECIAL18: case WXK_SPECIAL19:  case WXK_SPECIAL20: 
1375                 hotkey += wxString::Format(wxT("Special%d"), code - WXK_SPECIAL1 + 1); 
1378                 // if there are any other keys wxGetAccelFromString() may 
1379                 // return, we should process them here 
1384                     wxString name 
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) ); 
1392                 wxFAIL_MSG( wxT("unknown keyboard accel") ); 
1401 #endif // wxUSE_ACCEL 
1403 // ---------------------------------------------------------------------------- 
1404 // Pop-up menu stuff 
1405 // ---------------------------------------------------------------------------- 
1407 #if wxUSE_MENUS_NATIVE 
1409 extern "C" WXDLLIMPEXP_CORE
 
1410 void gtk_pop_hide_callback( GtkWidget 
*WXUNUSED(widget
), bool* is_waiting  
) 
1412     *is_waiting 
= FALSE
; 
1415 WXDLLIMPEXP_CORE 
void SetInvokingWindow( wxMenu 
*menu
, wxWindow
* win 
) 
1417     menu
->SetInvokingWindow( win 
); 
1419     wxMenuItemList::compatibility_iterator node 
= menu
->GetMenuItems().GetFirst(); 
1422         wxMenuItem 
*menuitem 
= node
->GetData(); 
1423         if (menuitem
->IsSubMenu()) 
1425             SetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
1428         node 
= node
->GetNext(); 
1432 extern "C" WXDLLIMPEXP_CORE
 
1433 void wxPopupMenuPositionCallback( GtkMenu 
*menu
, 
1435                                   gboolean 
* WXUNUSED(whatever
), 
1436                                   gpointer user_data 
) 
1438     // ensure that the menu appears entirely on screen 
1440     gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
); 
1442     wxSize sizeScreen 
= wxGetDisplaySize(); 
1443     wxPoint 
*pos 
= (wxPoint
*)user_data
; 
1445     gint xmax 
= sizeScreen
.x 
- req
.width
, 
1446          ymax 
= sizeScreen
.y 
- req
.height
; 
1448     *x 
= pos
->x 
< xmax 
? pos
->x 
: xmax
; 
1449     *y 
= pos
->y 
< ymax 
? pos
->y 
: ymax
; 
1452 bool wxWindowGTK::DoPopupMenu( wxMenu 
*menu
, int x
, int y 
) 
1454     wxCHECK_MSG( m_widget 
!= NULL
, false, wxT("invalid window") ); 
1456     wxCHECK_MSG( menu 
!= NULL
, false, wxT("invalid popup-menu") ); 
1458     // NOTE: if you change this code, you need to update 
1459     //       the same code in taskbar.cpp as well. This 
1460     //       is ugly code duplication, I know. 
1462     SetInvokingWindow( menu
, this ); 
1466     bool is_waiting 
= true; 
1468     gulong handler 
= g_signal_connect (menu
->m_menu
, "hide", 
1469                                        G_CALLBACK (gtk_pop_hide_callback
), 
1474     GtkMenuPositionFunc posfunc
; 
1475     if ( x 
== -1 && y 
== -1 ) 
1477         // use GTK's default positioning algorithm 
1483         pos 
= ClientToScreen(wxPoint(x
, y
)); 
1485         posfunc 
= wxPopupMenuPositionCallback
; 
1488     wxMenuEvent 
eventOpen(wxEVT_MENU_OPEN
, -1, menu
); 
1489     DoCommonMenuCallbackCode(menu
, eventOpen
); 
1492                   GTK_MENU(menu
->m_menu
), 
1493                   (GtkWidget 
*) NULL
,           // parent menu shell 
1494                   (GtkWidget 
*) NULL
,           // parent menu item 
1495                   posfunc
,                      // function to position it 
1496                   userdata
,                     // client data 
1497                   0,                            // button used to activate it 
1498                   gtk_get_current_event_time() 
1503         gtk_main_iteration(); 
1506     g_signal_handler_disconnect (menu
->m_menu
, handler
); 
1508     wxMenuEvent 
eventClose(wxEVT_MENU_CLOSE
, -1, menu
); 
1509     DoCommonMenuCallbackCode(menu
, eventClose
); 
1514 #endif // wxUSE_MENUS_NATIVE