1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  11 #pragma implementation "menu.h" 
  12 #pragma implementation "menuitem.h" 
  18 #include "wx/bitmap.h" 
  25 #include "wx/gtk/private.h" 
  27 #include <gdk/gdkkeysyms.h> 
  29 // FIXME: is this right? somehow I don't think so (VZ) 
  31     #include <glib-object.h> 
  33     #define gtk_accel_group_attach(g, o) _gtk_accel_group_attach((g), (o)) 
  34     #define gtk_accel_group_detach(g, o) _gtk_accel_group_detach((g), (o)) 
  35     #define gtk_menu_ensure_uline_accel_group(m) gtk_menu_get_accel_group(m) 
  37     #define ACCEL_OBJECT        GObject 
  38     #define ACCEL_OBJECTS(a)    (a)->acceleratables 
  39     #define ACCEL_OBJ_CAST(obj) G_OBJECT(obj) 
  41     #define ACCEL_OBJECT        GtkObject 
  42     #define ACCEL_OBJECTS(a)    (a)->attach_objects 
  43     #define ACCEL_OBJ_CAST(obj) GTK_OBJECT(obj) 
  46 //----------------------------------------------------------------------------- 
  48 //----------------------------------------------------------------------------- 
  50 extern void wxapp_install_idle_handler(); 
  53 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL 
  54     static wxString 
GetHotKey( const wxMenuItem
& item 
); 
  57 //----------------------------------------------------------------------------- 
  58 // substitute for missing GtkPixmapMenuItem 
  59 //----------------------------------------------------------------------------- 
  61 // FIXME: I can't make this compile with GTK+ 2.0, disabling for now (VZ) 
  63     #define USE_MENU_BITMAPS 
  66 #ifdef USE_MENU_BITMAPS 
  68 #define GTK_TYPE_PIXMAP_MENU_ITEM            (gtk_pixmap_menu_item_get_type ()) 
  69 #define GTK_PIXMAP_MENU_ITEM(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItem)) 
  70 #define GTK_PIXMAP_MENU_ITEM_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_PIXMAP_MENU_ITEM, GtkPixmapMenuItemClass)) 
  71 #define GTK_IS_PIXMAP_MENU_ITEM(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_PIXMAP_MENU_ITEM)) 
  72 #define GTK_IS_PIXMAP_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PIXMAP_MENU_ITEM)) 
  73 //#define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_PIXMAP_MENU_ITEM)) 
  74 #define GTK_PIXMAP_MENU_ITEM_GET_CLASS(obj)  (GTK_PIXMAP_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj))) 
  76 #ifndef GTK_MENU_ITEM_GET_CLASS 
  77 #define GTK_MENU_ITEM_GET_CLASS(obj) (GTK_MENU_ITEM_CLASS( GTK_OBJECT_GET_CLASS(obj))) 
  80 typedef struct _GtkPixmapMenuItem       GtkPixmapMenuItem
; 
  81 typedef struct _GtkPixmapMenuItemClass  GtkPixmapMenuItemClass
; 
  83 struct _GtkPixmapMenuItem
 
  85     GtkMenuItem menu_item
; 
  90 struct _GtkPixmapMenuItemClass
 
  92     GtkMenuItemClass parent_class
; 
  94     guint orig_toggle_size
; 
  95     guint have_pixmap_count
; 
  99 GtkType           
gtk_pixmap_menu_item_get_type       (void); 
 100 GtkWidget
* gtk_pixmap_menu_item_new            (void); 
 101 void       gtk_pixmap_menu_item_set_pixmap     (GtkPixmapMenuItem 
*menu_item
, 
 104 #endif // USE_MENU_BITMAPS 
 106 //----------------------------------------------------------------------------- 
 108 //----------------------------------------------------------------------------- 
 110 static wxString 
wxReplaceUnderscore( const wxString
& title 
) 
 114     /* GTK 1.2 wants to have "_" instead of "&" for accelerators */ 
 117     while (*pc 
!= wxT('\0')) 
 119         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
 121             // "&" is doubled to indicate "&" instead of accelerator 
 125         else if (*pc 
== wxT('&')) 
 127 #if GTK_CHECK_VERSION(1, 2, 0) 
 131 #if GTK_CHECK_VERSION(2, 0, 0) 
 132         else if (*pc 
== wxT('/')) 
 136         else if (*pc 
== wxT('\\')) 
 140 #elif GTK_CHECK_VERSION(1, 2, 0) 
 141         else if (*pc 
== wxT('/')) 
 149             if ( *pc 
== wxT('_') ) 
 151                 // underscores must be doubled to prevent them from being 
 152                 // interpreted as accelerator character prefix by GTK 
 164 //----------------------------------------------------------------------------- 
 166 //----------------------------------------------------------------------------- 
 168 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
) 
 170 wxMenuBar::wxMenuBar( long style 
) 
 172     /* the parent window is known after wxFrame::SetMenu() */ 
 173     m_needParent 
= FALSE
; 
 175     m_invokingWindow 
= (wxWindow
*) NULL
; 
 177     if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize 
) || 
 178         !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") )) 
 180         wxFAIL_MSG( wxT("wxMenuBar creation failed") ); 
 184     m_menus
.DeleteContents( TRUE 
); 
 186     /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */ 
 187 #if GTK_CHECK_VERSION(1, 2, 1) 
 188     m_accel 
= gtk_accel_group_new(); 
 189     m_factory 
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel 
); 
 190     m_menubar 
= gtk_item_factory_get_widget( m_factory
, "<main>" ); 
 192     m_menubar 
= gtk_menu_bar_new(); 
 195     if (style 
& wxMB_DOCKABLE
) 
 197         m_widget 
= gtk_handle_box_new(); 
 198         gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) ); 
 199         gtk_widget_show( GTK_WIDGET(m_menubar
) ); 
 203         m_widget 
= GTK_WIDGET(m_menubar
); 
 211 wxMenuBar::wxMenuBar() 
 213     /* the parent window is known after wxFrame::SetMenu() */ 
 214     m_needParent 
= FALSE
; 
 216     m_invokingWindow 
= (wxWindow
*) NULL
; 
 218     if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize 
) || 
 219         !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") )) 
 221         wxFAIL_MSG( wxT("wxMenuBar creation failed") ); 
 225     m_menus
.DeleteContents( TRUE 
); 
 227     /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */ 
 228 #if GTK_CHECK_VERSION(1, 2, 1) 
 229     m_accel 
= gtk_accel_group_new(); 
 230     m_factory 
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel 
); 
 231     m_menubar 
= gtk_item_factory_get_widget( m_factory
, "<main>" ); 
 233     m_menubar 
= gtk_menu_bar_new(); 
 236     m_widget 
= GTK_WIDGET(m_menubar
); 
 243 wxMenuBar::~wxMenuBar() 
 245 //    gtk_object_unref( GTK_OBJECT(m_factory) );  why not ? 
 248 static void wxMenubarUnsetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 250     menu
->SetInvokingWindow( (wxWindow
*) NULL 
); 
 252     wxWindow 
*top_frame 
= win
; 
 253     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 254         top_frame 
= top_frame
->GetParent(); 
 256     /* support for native hot keys */ 
 257     gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) ); 
 259     wxMenuItemList::Node 
*node 
= menu
->GetMenuItems().GetFirst(); 
 262         wxMenuItem 
*menuitem 
= node
->GetData(); 
 263         if (menuitem
->IsSubMenu()) 
 264             wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 265         node 
= node
->GetNext(); 
 269 static void wxMenubarSetInvokingWindow( wxMenu 
*menu
, wxWindow 
*win 
) 
 271     menu
->SetInvokingWindow( win 
); 
 273 #if GTK_CHECK_VERSION(1, 2, 1) 
 274     wxWindow 
*top_frame 
= win
; 
 275     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 276         top_frame 
= top_frame
->GetParent(); 
 278     /* support for native hot keys  */ 
 279     ACCEL_OBJECT 
*obj 
= ACCEL_OBJ_CAST(top_frame
->m_widget
); 
 280     if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj 
) ) 
 281         gtk_accel_group_attach( menu
->m_accel
, obj 
); 
 282 #endif // GTK+ 1.2.1+ 
 284     wxMenuItemList::Node 
*node 
= menu
->GetMenuItems().GetFirst(); 
 287         wxMenuItem 
*menuitem 
= node
->GetData(); 
 288         if (menuitem
->IsSubMenu()) 
 289             wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win 
); 
 290         node 
= node
->GetNext(); 
 294 void wxMenuBar::SetInvokingWindow( wxWindow 
*win 
) 
 296     m_invokingWindow 
= win
; 
 297 #if GTK_CHECK_VERSION(1, 2, 1) 
 298     wxWindow 
*top_frame 
= win
; 
 299     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 300         top_frame 
= top_frame
->GetParent(); 
 302     /* support for native key accelerators indicated by underscroes */ 
 303     ACCEL_OBJECT 
*obj 
= ACCEL_OBJ_CAST(top_frame
->m_widget
); 
 304     if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj 
) ) 
 305         gtk_accel_group_attach( m_accel
, obj 
); 
 306 #endif // GTK+ 1.2.1+ 
 308     wxMenuList::Node 
*node 
= m_menus
.GetFirst(); 
 311         wxMenu 
*menu 
= node
->GetData(); 
 312         wxMenubarSetInvokingWindow( menu
, win 
); 
 313         node 
= node
->GetNext(); 
 317 void wxMenuBar::UnsetInvokingWindow( wxWindow 
*win 
) 
 319     m_invokingWindow 
= (wxWindow
*) NULL
; 
 320 #if GTK_CHECK_VERSION(1, 2, 1) 
 321     wxWindow 
*top_frame 
= win
; 
 322     while (top_frame
->GetParent() && !(top_frame
->IsTopLevel())) 
 323         top_frame 
= top_frame
->GetParent(); 
 325     /* support for native key accelerators indicated by underscroes */ 
 326     gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) ); 
 327 #endif // GTK+ 1.2.1+ 
 329     wxMenuList::Node 
*node 
= m_menus
.GetFirst(); 
 332         wxMenu 
*menu 
= node
->GetData(); 
 333         wxMenubarUnsetInvokingWindow( menu
, win 
); 
 334         node 
= node
->GetNext(); 
 338 bool wxMenuBar::Append( wxMenu 
*menu
, const wxString 
&title 
) 
 340     if ( !wxMenuBarBase::Append( menu
, title 
) ) 
 343     return GtkAppend(menu
, title
); 
 346 bool wxMenuBar::GtkAppend(wxMenu 
*menu
, const wxString
& title
) 
 348     wxString 
str( wxReplaceUnderscore( title 
) ); 
 350     /* this doesn't have much effect right now */ 
 351     menu
->SetTitle( str 
); 
 353     /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */ 
 354 #if GTK_CHECK_VERSION(1, 2, 1) 
 357     buf 
<< wxT('/') << str
.c_str(); 
 359     /* local buffer in multibyte form */ 
 361     strcpy(cbuf
, wxGTK_CONV(buf
) ); 
 363     GtkItemFactoryEntry entry
; 
 364     entry
.path 
= (gchar 
*)cbuf
;  // const_cast 
 365     entry
.accelerator 
= (gchar
*) NULL
; 
 366     entry
.callback 
= (GtkItemFactoryCallback
) NULL
; 
 367     entry
.callback_action 
= 0; 
 368     entry
.item_type 
= (char *)"<Branch>"; 
 370     gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 );  /* what is 2 ? */ 
 371     /* in order to get the pointer to the item we need the item text _without_ underscores */ 
 372     wxString tmp 
= wxT("<main>/"); 
 374     for ( pc 
= str
; *pc 
!= wxT('\0'); pc
++ ) 
 376        // contrary to the common sense, we must throw out _all_ underscores, 
 377        // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we 
 378        // might naively think). IMHO it's a bug in GTK+ (VZ) 
 379        while (*pc 
== wxT('_')) 
 383     menu
->m_owner 
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( tmp 
) ); 
 384     gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu 
); 
 387     menu
->m_owner 
= gtk_menu_item_new_with_label( wxGTK_CONV( str 
) ); 
 388     gtk_widget_show( menu
->m_owner 
); 
 389     gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu 
); 
 391     gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner 
); 
 395     // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables 
 396     // addings menu later on. 
 397     if (m_invokingWindow
) 
 399         wxMenubarSetInvokingWindow( menu
, m_invokingWindow 
); 
 401             // OPTIMISE ME:  we should probably cache this, or pass it 
 402             //               directly, but for now this is a minimal 
 403             //               change to validate the new dynamic sizing. 
 404             //               see (and refactor :) similar code in Remove 
 407         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 410             frame
->UpdateMenuBarSize(); 
 416 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 418     if ( !wxMenuBarBase::Insert(pos
, menu
, title
) ) 
 421     // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as 
 422     // of version 1.2.6), so we first append the item and then change its 
 424     if ( !GtkAppend(menu
, title
) ) 
 427     if (pos
+1 >= m_menus
.GetCount()) 
 430     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL(m_factory
->widget
); 
 431     gpointer data 
= g_list_last(menu_shell
->children
)->data
; 
 432     menu_shell
->children 
= g_list_remove(menu_shell
->children
, data
); 
 433     menu_shell
->children 
= g_list_insert(menu_shell
->children
, data
, pos
); 
 438 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 440     // remove the old item and insert a new one 
 441     wxMenu 
*menuOld 
= Remove(pos
); 
 442     if ( menuOld 
&& !Insert(pos
, menu
, title
) ) 
 444         return (wxMenu
*) NULL
; 
 447     // either Insert() succeeded or Remove() failed and menuOld is NULL 
 451 static wxMenu 
*CopyMenu (wxMenu 
*menu
) 
 453     wxMenu 
*menucopy 
= new wxMenu (); 
 454     wxMenuItemList::Node 
*node 
= menu
->GetMenuItems().GetFirst(); 
 457         wxMenuItem 
*item 
= node
->GetData(); 
 458         int itemid 
= item
->GetId(); 
 459         wxString text 
= item
->GetText(); 
 460         text
.Replace(wxT("_"), wxT("&")); 
 461         wxMenu 
*submenu 
= item
->GetSubMenu(); 
 464             wxMenuItem
* itemcopy 
= new wxMenuItem(menucopy
, 
 466                                         menu
->GetHelpString(itemid
)); 
 467             itemcopy
->SetBitmap(item
->GetBitmap()); 
 468             itemcopy
->SetCheckable(item
->IsCheckable()); 
 469             menucopy
->Append(itemcopy
); 
 472           menucopy
->Append (itemid
, text
, CopyMenu(submenu
), 
 473                             menu
->GetHelpString(itemid
)); 
 475         node 
= node
->GetNext(); 
 481 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
 483     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
 485         return (wxMenu
*) NULL
; 
 488     GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget); 
 490     printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) ); 
 491     printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) ); 
 494     wxMenu 
*menucopy 
= CopyMenu( menu 
); 
 496     // unparent calls unref() and that would delete the widget so we raise 
 497     // the ref count to 2 artificially before invoking unparent. 
 498     gtk_widget_ref( menu
->m_menu 
); 
 499     gtk_widget_unparent( menu
->m_menu 
); 
 501     gtk_widget_destroy( menu
->m_owner 
); 
 506     printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) ); 
 507     printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) ); 
 510     if (m_invokingWindow
) 
 512             // OPTIMISE ME:  see comment in GtkAppend 
 514         wxFrame 
*frame 
= wxDynamicCast( m_invokingWindow
, wxFrame 
); 
 517             frame
->UpdateMenuBarSize(); 
 523 static int FindMenuItemRecursive( const wxMenu 
*menu
, const wxString 
&menuString
, const wxString 
&itemString 
) 
 525     if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
)) 
 527         int res 
= menu
->FindItem( itemString 
); 
 528         if (res 
!= wxNOT_FOUND
) 
 532     wxMenuItemList::Node 
*node 
= menu
->GetMenuItems().GetFirst(); 
 535         wxMenuItem 
*item 
= node
->GetData(); 
 536         if (item
->IsSubMenu()) 
 537             return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
); 
 539         node 
= node
->GetNext(); 
 545 int wxMenuBar::FindMenuItem( const wxString 
&menuString
, const wxString 
&itemString 
) const 
 547     wxMenuList::Node 
*node 
= m_menus
.GetFirst(); 
 550         wxMenu 
*menu 
= node
->GetData(); 
 551         int res 
= FindMenuItemRecursive( menu
, menuString
, itemString
); 
 554         node 
= node
->GetNext(); 
 560 // Find a wxMenuItem using its id. Recurses down into sub-menus 
 561 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
) 
 563     wxMenuItem
* result 
= menu
->FindChildItem(id
); 
 565     wxMenuItemList::Node 
*node 
= menu
->GetMenuItems().GetFirst(); 
 566     while ( node 
&& result 
== NULL 
) 
 568         wxMenuItem 
*item 
= node
->GetData(); 
 569         if (item
->IsSubMenu()) 
 571             result 
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id 
); 
 573         node 
= node
->GetNext(); 
 579 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu 
**menuForItem 
) const 
 581     wxMenuItem
* result 
= 0; 
 582     wxMenuList::Node 
*node 
= m_menus
.GetFirst(); 
 583     while (node 
&& result 
== 0) 
 585         wxMenu 
*menu 
= node
->GetData(); 
 586         result 
= FindMenuItemByIdRecursive( menu
, id 
); 
 587         node 
= node
->GetNext(); 
 592         *menuForItem 
= result 
? result
->GetMenu() : (wxMenu 
*)NULL
; 
 598 void wxMenuBar::EnableTop( size_t pos
, bool flag 
) 
 600     wxMenuList::Node 
*node 
= m_menus
.Item( pos 
); 
 602     wxCHECK_RET( node
, wxT("menu not found") ); 
 604     wxMenu
* menu 
= node
->GetData(); 
 607         gtk_widget_set_sensitive( menu
->m_owner
, flag 
); 
 610 wxString 
wxMenuBar::GetLabelTop( size_t pos 
) const 
 612     wxMenuList::Node 
*node 
= m_menus
.Item( pos 
); 
 614     wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") ); 
 616     wxMenu
* menu 
= node
->GetData(); 
 619     wxString 
text( menu
->GetTitle() ); 
 620     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 622         if ( *pc 
== wxT('_') ) 
 624             // '_' is the escape character for GTK+ 
 628         // don't remove ampersands '&' since if we have them in the menu title 
 629         // it means that they were doubled to indicate "&" instead of accelerator 
 637 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label 
) 
 639     wxMenuList::Node 
*node 
= m_menus
.Item( pos 
); 
 641     wxCHECK_RET( node
, wxT("menu not found") ); 
 643     wxMenu
* menu 
= node
->GetData(); 
 645     wxString 
str( wxReplaceUnderscore( label 
) ); 
 647     menu
->SetTitle( str 
); 
 651         GtkLabel 
*label 
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child 
); 
 654         gtk_label_set( label
, wxGTK_CONV( str 
) ); 
 656         /* reparse key accel */ 
 657         (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( str 
) ); 
 658         gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) ); 
 663 //----------------------------------------------------------------------------- 
 665 //----------------------------------------------------------------------------- 
 667 static void gtk_menu_clicked_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 670         wxapp_install_idle_handler(); 
 672     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 674     /* should find it for normal (not popup) menu */ 
 675     wxASSERT_MSG( (id 
!= -1) || (menu
->GetInvokingWindow() != NULL
), 
 676                   _T("menu item not found in gtk_menu_clicked_callback") ); 
 678     if (!menu
->IsEnabled(id
)) 
 681     wxMenuItem
* item 
= menu
->FindChildItem( id 
); 
 682     wxCHECK_RET( item
, wxT("error in menu item callback") ); 
 684     if (item
->IsCheckable()) 
 686         bool isReallyChecked 
= item
->IsChecked(), 
 687              isInternallyChecked 
= item
->wxMenuItemBase::IsChecked(); 
 689         // ensure that the internal state is always consistent with what is 
 690         // shown on the screen 
 691         item
->wxMenuItemBase::Check(isReallyChecked
); 
 693         // we must not report the events for the radio button going up nor the 
 694         // events resulting from the calls to wxMenuItem::Check() 
 695         if ( (item
->GetKind() == wxITEM_RADIO 
&& !isReallyChecked
) || 
 696              (isInternallyChecked 
== isReallyChecked
) ) 
 701         // the user pressed on the menu item: report the event below 
 704     menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1); 
 707 //----------------------------------------------------------------------------- 
 709 //----------------------------------------------------------------------------- 
 711 static void gtk_menu_hilight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 713     if (g_isIdle
) wxapp_install_idle_handler(); 
 715     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 717     wxASSERT( id 
!= -1 ); // should find it! 
 719     if (!menu
->IsEnabled(id
)) 
 722     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, id 
); 
 723     event
.SetEventObject( menu 
); 
 725     if (menu
->GetEventHandler()->ProcessEvent(event
)) 
 728     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 729     if (win
) win
->GetEventHandler()->ProcessEvent( event 
); 
 732 //----------------------------------------------------------------------------- 
 734 //----------------------------------------------------------------------------- 
 736 static void gtk_menu_nolight_callback( GtkWidget 
*widget
, wxMenu 
*menu 
) 
 738     if (g_isIdle
) wxapp_install_idle_handler(); 
 740     int id 
= menu
->FindMenuIdByMenuItem(widget
); 
 742     wxASSERT( id 
!= -1 ); // should find it! 
 744     if (!menu
->IsEnabled(id
)) 
 747     wxMenuEvent 
event( wxEVT_MENU_HIGHLIGHT
, -1 ); 
 748     event
.SetEventObject( menu 
); 
 750     if (menu
->GetEventHandler()->ProcessEvent(event
)) 
 753     wxWindow 
*win 
= menu
->GetInvokingWindow(); 
 755         win
->GetEventHandler()->ProcessEvent( event 
); 
 758 //----------------------------------------------------------------------------- 
 760 //----------------------------------------------------------------------------- 
 762 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
) 
 764 wxMenuItem 
*wxMenuItemBase::New(wxMenu 
*parentMenu
, 
 766                                 const wxString
& name
, 
 767                                 const wxString
& help
, 
 771     return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
); 
 774 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 776                        const wxString
& text
, 
 777                        const wxString
& help
, 
 780           : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
) 
 785 wxMenuItem::wxMenuItem(wxMenu 
*parentMenu
, 
 787                        const wxString
& text
, 
 788                        const wxString
& help
, 
 791           : wxMenuItemBase(parentMenu
, id
, text
, help
, 
 792                            isCheckable 
? wxITEM_CHECK 
: wxITEM_NORMAL
, subMenu
) 
 797 void wxMenuItem::Init(const wxString
& text
) 
 799     m_labelWidget 
= (GtkWidget 
*) NULL
; 
 800     m_menuItem 
= (GtkWidget 
*) NULL
; 
 805 wxMenuItem::~wxMenuItem() 
 807    // don't delete menu items, the menus take care of that 
 810 // return the menu item text without any menu accels 
 812 wxString 
wxMenuItemBase::GetLabelFromText(const wxString
& text
) 
 816     for ( const wxChar 
*pc 
= text
.c_str(); *pc
; pc
++ ) 
 818         if ( *pc 
== wxT('_') ) 
 820             // GTK 1.2 escapes "xxx_xxx" to "xxx__xxx" 
 826 #if GTK_CHECK_VERSION(2, 0, 0) 
 827         if ( *pc 
== wxT('\\')  ) 
 829             // GTK 2.0 escapes "xxx/xxx" to "xxx\/xxx" 
 836         if ( (*pc 
== wxT('&')) && (*(pc
+1) != wxT('&')) ) 
 839             // "&" is doubled to indicate "&" instead of accelerator 
 848 void wxMenuItem::SetText( const wxString
& str 
) 
 850     // Some optimization to avoid flicker 
 851     wxString oldLabel 
= m_text
; 
 852     oldLabel 
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t')); 
 853     oldLabel
.Replace(wxT("_"), wxT("")); 
 854     wxString label1 
= wxStripMenuCodes(str
.BeforeFirst('\t')); 
 855     if (oldLabel 
== label1
) 
 864             label 
= (GtkLabel
*) m_labelWidget
; 
 866             label 
= GTK_LABEL( GTK_BIN(m_menuItem
)->child 
); 
 868 #if GTK_CHECK_VERSION(2, 0, 0) 
 869         // We have to imitate item_factory_unescape_label here 
 871         for (size_t n 
= 0; n 
< m_text
.Len(); n
++) 
 873             if (m_text
[n
] != wxT('\\')) 
 877         gtk_label_set_text_with_mnemonic( GTK_LABEL(label
), wxGTK_CONV(tmp
) ); 
 880         gtk_label_set( label
, wxGTK_CONV( m_text 
) ); 
 883         (void)gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV(m_text
) ); 
 884         gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) ); 
 889 // it's valid for this function to be called even if m_menuItem == NULL 
 890 void wxMenuItem::DoSetText( const wxString
& str 
) 
 892     // '\t' is the deliminator indicating a hot key 
 894     const wxChar 
*pc 
= str
; 
 895     while ( (*pc 
!= wxT('\0')) && (*pc 
!= wxT('\t')) ) 
 897         if ((*pc 
== wxT('&')) && (*(pc
+1) == wxT('&'))) 
 899             // "&" is doubled to indicate "&" instead of accelerator 
 903         else if (*pc 
== wxT('&')) 
 907 #if GTK_CHECK_VERSION(2, 0, 0) 
 908         else if ( *pc 
== wxT('_') )    // escape underscores 
 910             // m_text << wxT("__");    doesn't work 
 912         else if (*pc 
== wxT('/'))      // we have to escape slashes 
 914             m_text 
<< wxT("\\/"); 
 916         else if (*pc 
== wxT('\\'))     // we have to double backslashes 
 918             m_text 
<< wxT("\\\\"); 
 921         else if ( *pc 
== wxT('_') )    // escape underscores 
 925         else if (*pc 
== wxT('/'))      /* we have to filter out slashes ... */ 
 927             m_text 
<< wxT('\\');  /* ... and replace them with back slashes */ 
 947 wxAcceleratorEntry 
*wxMenuItem::GetAccel() const 
 952         return (wxAcceleratorEntry 
*)NULL
; 
 955     // as wxGetAccelFromString() looks for TAB, insert a dummy one here 
 957     label 
<< wxT('\t') << GetHotKey(); 
 959     return wxGetAccelFromString(label
); 
 962 #endif // wxUSE_ACCEL 
 964 void wxMenuItem::Check( bool check 
) 
 966     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 968     if (check 
== m_isChecked
) 
 971     wxMenuItemBase::Check( check 
); 
 977             gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check 
); 
 981             wxFAIL_MSG( _T("can't check this item") ); 
 985 void wxMenuItem::Enable( bool enable 
) 
 987     wxCHECK_RET( m_menuItem
, wxT("invalid menu item") ); 
 989     gtk_widget_set_sensitive( m_menuItem
, enable 
); 
 990     wxMenuItemBase::Enable( enable 
); 
 993 bool wxMenuItem::IsChecked() const 
 995     wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") ); 
 997     wxCHECK_MSG( IsCheckable(), FALSE
, 
 998                  wxT("can't get state of uncheckable item!") ); 
1000     return ((GtkCheckMenuItem
*)m_menuItem
)->active 
!= 0; 
1003 wxString 
wxMenuItem::GetFactoryPath() const 
1005     /* in order to get the pointer to the item we need the item text 
1006        _without_ underscores */ 
1007     wxString 
path( wxT("<main>/") ); 
1009     for ( const wxChar 
*pc 
= m_text
.c_str(); *pc
; pc
++ ) 
1011         if ( *pc 
== wxT('_') ) 
1013             // remove '_' unconditionally 
1017         // don't remove ampersands '&' since if we have them in the menu item title 
1018         // it means that they were doubled to indicate "&" instead of accelerator 
1026 //----------------------------------------------------------------------------- 
1028 //----------------------------------------------------------------------------- 
1030 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
) 
1034     m_accel 
= gtk_accel_group_new(); 
1035     m_factory 
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel 
); 
1036     m_menu 
= gtk_item_factory_get_widget( m_factory
, "<main>" ); 
1038     m_owner 
= (GtkWidget
*) NULL
; 
1040     /* Tearoffs are entries, just like separators. So if we want this 
1041        menu to be a tear-off one, we just append a tearoff entry 
1043     if(m_style 
& wxMENU_TEAROFF
) 
1045        GtkItemFactoryEntry entry
; 
1046        entry
.path 
= (char *)"/tearoff"; 
1047        entry
.callback 
= (GtkItemFactoryCallback
) NULL
; 
1048        entry
.callback_action 
= 0; 
1049        entry
.item_type 
= (char *)"<Tearoff>"; 
1050        entry
.accelerator 
= (gchar
*) NULL
; 
1051        gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 );  /* what is 2 ? */ 
1052        //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" ); 
1055     // append the title as the very first entry if we have it 
1058         Append(-2, m_title
); 
1067    gtk_widget_destroy( m_menu 
); 
1069    gtk_object_unref( GTK_OBJECT(m_factory
) ); 
1072 bool wxMenu::GtkAppend(wxMenuItem 
*mitem
) 
1074     GtkWidget 
*menuItem
; 
1076 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0) 
1077     bool appended 
= FALSE
; 
1080     // does this item terminate the current radio group? 
1081     bool endOfRadioGroup 
= TRUE
; 
1083     if ( mitem
->IsSeparator() ) 
1085         GtkItemFactoryEntry entry
; 
1086         entry
.path 
= (char *)"/sep"; 
1087         entry
.callback 
= (GtkItemFactoryCallback
) NULL
; 
1088         entry
.callback_action 
= 0; 
1089         entry
.item_type 
= (char *)"<Separator>"; 
1090         entry
.accelerator 
= (gchar
*) NULL
; 
1092         gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 );  /* what is 2 ? */ 
1094         /* this will be wrong for more than one separator. do we care? */ 
1095         menuItem 
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" ); 
1097         // we might have a separator inside a radio group 
1098         endOfRadioGroup 
= FALSE
; 
1100     else if ( mitem
->IsSubMenu() ) 
1102         /* text has "_" instead of "&" after mitem->SetText() */ 
1103         wxString 
text( mitem
->GetText() ); 
1105         /* local buffer in multibyte form */ 
1108         strcat( buf
, wxGTK_CONV( text 
) ); 
1110         GtkItemFactoryEntry entry
; 
1112         entry
.callback 
= (GtkItemFactoryCallback
) 0; 
1113         entry
.callback_action 
= 0; 
1114         entry
.item_type 
= (char *)"<Branch>"; 
1115         entry
.accelerator 
= (gchar
*) NULL
; 
1117         gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 );  /* what is 2 ? */ 
1119         wxString 
path( mitem
->GetFactoryPath() ); 
1120         menuItem 
= gtk_item_factory_get_item( m_factory
, wxGTK_CONV( path 
) ); 
1122         gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu 
); 
1124         // if adding a submenu to a menu already existing in the menu bar, we 
1125         // must set invoking window to allow processing events from this 
1127         if ( m_invokingWindow 
) 
1128             wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
); 
1130 #ifdef USE_MENU_BITMAPS 
1131     else if (mitem
->GetBitmap().Ok()) // An item with bitmap 
1133         wxString 
text( mitem
->GetText() ); 
1134         const wxBitmap 
*bitmap 
= &mitem
->GetBitmap(); 
1136         menuItem 
= gtk_pixmap_menu_item_new (); 
1137         GtkWidget 
*label 
= gtk_accel_label_new ( wxGTK_CONV( text 
) ); 
1138         gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5); 
1139         gtk_container_add (GTK_CONTAINER (menuItem
), label
); 
1140         guint accel_key 
= gtk_label_parse_uline (GTK_LABEL(label
), wxGTK_CONV( text 
) ); 
1141         gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
); 
1142         if (accel_key 
!= GDK_VoidSymbol
) 
1144             gtk_widget_add_accelerator (menuItem
, 
1146                                         gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)), 
1150         gtk_widget_show (label
); 
1152         mitem
->SetLabelWidget(label
); 
1154         GtkWidget
* pixmap 
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
); 
1155         gtk_widget_show(pixmap
); 
1156         gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem 
), pixmap
); 
1158         gtk_signal_connect( GTK_OBJECT(menuItem
), "activate", 
1159                             GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
), 
1162         gtk_menu_append( GTK_MENU(m_menu
), menuItem 
); 
1163         gtk_widget_show( menuItem 
); 
1165         appended 
= TRUE
; // We've done this, don't do it again 
1167 #endif // USE_MENU_BITMAPS 
1168     else // a normal item 
1170         // text has "_" instead of "&" after mitem->SetText() so don't use it 
1171         wxString 
text( mitem
->GetText() ); 
1173         // buffers containing the menu item path and type in multibyte form 
1177         strcpy( bufPath
, "/" ); 
1178         strncat( bufPath
, wxGTK_CONV(text
), WXSIZEOF(bufPath
) - 2 ); 
1179         bufPath
[WXSIZEOF(bufPath
) - 1] = '\0'; 
1181         GtkItemFactoryEntry entry
; 
1182         entry
.path 
= bufPath
; 
1183         entry
.callback 
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
; 
1184         entry
.callback_action 
= 0; 
1187         const char *item_type
; 
1188         switch ( mitem
->GetKind() ) 
1191                 item_type 
= "<CheckItem>"; 
1195                 if ( m_pathLastRadio
.empty() ) 
1197                     // start of a new radio group 
1198                     item_type 
= "<RadioItem>"; 
1199                     wxString 
tmp( wxGTK_CONV_BACK( bufPath 
) ); 
1201                     m_pathLastRadio 
= tmp
; 
1203                 else // continue the radio group 
1205                     pathRadio 
= m_pathLastRadio
; 
1206                     pathRadio
.Replace(wxT("_"), wxT("")); 
1207                     pathRadio
.Prepend(wxT("<main>/")); 
1209                     strncpy(bufType
, wxGTK_CONV(pathRadio
), WXSIZEOF(bufType
)); 
1210                     bufType
[WXSIZEOF(bufType
) - 1] = '\0'; 
1211                     item_type 
= bufType
; 
1214                 // continue the existing radio group, if any 
1215                 endOfRadioGroup 
= FALSE
; 
1219                 wxFAIL_MSG( _T("unexpected menu item kind") ); 
1223                 item_type 
= "<Item>"; 
1227         entry
.item_type 
= (char *)item_type
; // cast needed for GTK+ 
1228         entry
.accelerator 
= (gchar
*) NULL
; 
1231         // due to an apparent bug in GTK+, we have to use a static buffer here - 
1232         // otherwise GTK+ 1.2.2 manages to override the memory we pass to it 
1234         char s_accel
[50]; // should be big enough, we check for overruns 
1235         wxString 
tmp( GetHotKey(*mitem
) ); 
1236         strncpy(s_accel
, wxGTK_CONV( tmp 
), WXSIZEOF(s_accel
)); 
1237         s_accel
[WXSIZEOF(s_accel
) - 1] = '\0'; 
1238         entry
.accelerator 
= s_accel
; 
1239 #else // !wxUSE_ACCEL 
1240         entry
.accelerator 
= (char*) NULL
; 
1241 #endif // wxUSE_ACCEL/!wxUSE_ACCEL 
1243         gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 );  /* what is 2 ? */ 
1245         wxString 
path( mitem
->GetFactoryPath() ); 
1246         menuItem 
= gtk_item_factory_get_widget( m_factory
, wxGTK_CONV( path 
) ); 
1249             wxLogError( wxT("Wrong menu path: %s\n"), path
.c_str() ); 
1252     if ( !mitem
->IsSeparator() ) 
1254         wxASSERT_MSG( menuItem
, wxT("invalid menuitem") ); 
1256         gtk_signal_connect( GTK_OBJECT(menuItem
), "select", 
1257                             GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
), 
1260         gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect", 
1261                             GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
), 
1265     mitem
->SetMenuItem(menuItem
); 
1267     if ( endOfRadioGroup 
) 
1269         m_pathLastRadio
.clear(); 
1275 bool wxMenu::DoAppend(wxMenuItem 
*mitem
) 
1277     return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
); 
1280 bool wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
1282     if ( !wxMenuBase::DoInsert(pos
, item
) ) 
1286     // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as 
1287     // of version 1.2.6), so we first append the item and then change its 
1289     if ( !GtkAppend(item
) ) 
1292     if ( m_style 
& wxMENU_TEAROFF 
) 
1294         // change the position as the first item is the tear-off marker 
1298     GtkMenuShell 
*menu_shell 
= GTK_MENU_SHELL(m_factory
->widget
); 
1299     gpointer data 
= g_list_last(menu_shell
->children
)->data
; 
1300     menu_shell
->children 
= g_list_remove(menu_shell
->children
, data
); 
1301     menu_shell
->children 
= g_list_insert(menu_shell
->children
, data
, pos
); 
1305     // this should be easy to do... 
1306     wxFAIL_MSG( wxT("not implemented") ); 
1309 #endif // GTK 1.2/1.0 
1312 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
1314     if ( !wxMenuBase::DoRemove(item
) ) 
1315         return (wxMenuItem 
*)NULL
; 
1317     // TODO: this code doesn't delete the item factory item and this seems 
1318     //       impossible as of GTK 1.2.6. 
1319     gtk_widget_destroy( item
->GetMenuItem() ); 
1324 int wxMenu::FindMenuIdByMenuItem( GtkWidget 
*menuItem 
) const 
1326     wxNode 
*node 
= m_items
.First(); 
1329         wxMenuItem 
*item 
= (wxMenuItem
*)node
->Data(); 
1330         if (item
->GetMenuItem() == menuItem
) 
1331            return item
->GetId(); 
1332         node 
= node
->Next(); 
1338 // ---------------------------------------------------------------------------- 
1340 // ---------------------------------------------------------------------------- 
1342 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL 
1344 static wxString 
GetHotKey( const wxMenuItem
& item 
) 
1348     wxAcceleratorEntry 
*accel 
= item
.GetAccel(); 
1351         int flags 
= accel
->GetFlags(); 
1352         if ( flags 
& wxACCEL_ALT 
) 
1353             hotkey 
+= wxT("<alt>"); 
1354         if ( flags 
& wxACCEL_CTRL 
) 
1355             hotkey 
+= wxT("<control>"); 
1356         if ( flags 
& wxACCEL_SHIFT 
) 
1357             hotkey 
+= wxT("<shift>"); 
1359         int code 
= accel
->GetKeyCode(); 
1374                 hotkey 
<< wxT('F') << code 
- WXK_F1 
+ 1; 
1377                 // TODO: we should use gdk_keyval_name() (a.k.a. 
1378                 //       XKeysymToString) here as well as hardcoding the keysym 
1379                 //       names this might be not portable 
1380             case WXK_NUMPAD_INSERT
: 
1381                 hotkey 
<< wxT("KP_Insert" ); 
1383             case WXK_NUMPAD_DELETE
: 
1384                 hotkey 
<< wxT("KP_Delete" ); 
1387                 hotkey 
<< wxT("Insert" ); 
1390                 hotkey 
<< wxT("Delete" ); 
1393                 hotkey 
<< wxT("Up" ); 
1396                 hotkey 
<< wxT("Down" ); 
1399                 hotkey 
<< wxT("Prior" ); 
1402                 hotkey 
<< wxT("Next" ); 
1405                 hotkey 
<< wxT("Left" ); 
1408                 hotkey 
<< wxT("Right" ); 
1411                 hotkey 
<< wxT("Home" ); 
1414                 hotkey 
<< wxT("End" ); 
1417                 hotkey 
<< wxT("Return" ); 
1420                 // if there are any other keys wxGetAccelFromString() may 
1421                 // return, we should process them here 
1426                     wxString name 
= wxGTK_CONV_BACK( gdk_keyval_name((guint
)code
) ); 
1434                 wxFAIL_MSG( wxT("unknown keyboard accel") ); 
1443 #endif // wxUSE_ACCEL 
1446 //----------------------------------------------------------------------------- 
1447 // substitute for missing GtkPixmapMenuItem 
1448 //----------------------------------------------------------------------------- 
1450 #ifdef USE_MENU_BITMAPS 
1453  * Copyright (C) 1998, 1999, 2000 Free Software Foundation 
1454  * All rights reserved. 
1456  * This file is part of the Gnome Library. 
1458  * The Gnome Library is free software; you can redistribute it and/or 
1459  * modify it under the terms of the GNU Library General Public License as 
1460  * published by the Free Software Foundation; either version 2 of the 
1461  * License, or (at your option) any later version. 
1463  * The Gnome Library is distributed in the hope that it will be useful, 
1464  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
1465  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
1466  * Library General Public License for more details. 
1468  * You should have received a copy of the GNU Library General Public 
1469  * License along with the Gnome Library; see the file COPYING.LIB.  If not, 
1470  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
1471  * Boston, MA 02111-1307, USA. 
1477 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */ 
1479 #include <gtk/gtkaccellabel.h> 
1480 #include <gtk/gtksignal.h> 
1481 #include <gtk/gtkmenuitem.h> 
1482 #include <gtk/gtkmenu.h> 
1483 #include <gtk/gtkcontainer.h> 
1488 static void gtk_pixmap_menu_item_class_init    (GtkPixmapMenuItemClass 
*klass
); 
1489 static void gtk_pixmap_menu_item_init          (GtkPixmapMenuItem      
*menu_item
); 
1490 static void gtk_pixmap_menu_item_draw          (GtkWidget              
*widget
, 
1491                                                 GdkRectangle           
*area
); 
1492 static gint 
gtk_pixmap_menu_item_expose        (GtkWidget              
*widget
, 
1493                                                 GdkEventExpose         
*event
); 
1495 /* we must override the following functions */ 
1497 static void gtk_pixmap_menu_item_map           (GtkWidget        
*widget
); 
1498 static void gtk_pixmap_menu_item_size_allocate (GtkWidget        
*widget
, 
1499                                                 GtkAllocation    
*allocation
); 
1500 static void gtk_pixmap_menu_item_forall        (GtkContainer    
*container
, 
1501                                                 gboolean         include_internals
, 
1502                                                 GtkCallback      callback
, 
1503                                                 gpointer         callback_data
); 
1504 static void gtk_pixmap_menu_item_size_request  (GtkWidget        
*widget
, 
1505                                                 GtkRequisition   
*requisition
); 
1506 static void gtk_pixmap_menu_item_remove        (GtkContainer 
*container
, 
1509 static void changed_have_pixmap_status         (GtkPixmapMenuItem 
*menu_item
); 
1511 static GtkMenuItemClass 
*parent_class 
= NULL
; 
1515 #define BORDER_SPACING  3 
1516 #define PMAP_WIDTH 20 
1519 gtk_pixmap_menu_item_get_type (void) 
1521   static GtkType pixmap_menu_item_type 
= 0; 
1523   if (!pixmap_menu_item_type
) 
1525       GtkTypeInfo pixmap_menu_item_info 
= 
1527         (char *)"GtkPixmapMenuItem", 
1528         sizeof (GtkPixmapMenuItem
), 
1529         sizeof (GtkPixmapMenuItemClass
), 
1530         (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
, 
1531         (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
, 
1532         /* reserved_1 */ NULL
, 
1533         /* reserved_2 */ NULL
, 
1534         (GtkClassInitFunc
) NULL
, 
1537       pixmap_menu_item_type 
= gtk_type_unique (gtk_menu_item_get_type (), 
1538                                                &pixmap_menu_item_info
); 
1541   return pixmap_menu_item_type
; 
1545  * gtk_pixmap_menu_item_new 
1547  * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap() 
1548  * to set the pixmap wich is displayed at the left side. 
1551  * &GtkWidget pointer to new menu item 
1555 gtk_pixmap_menu_item_new (void) 
1557   return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ())); 
1561 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass 
*klass
) 
1563   GtkObjectClass 
*object_class
; 
1564   GtkWidgetClass 
*widget_class
; 
1565   GtkMenuItemClass 
*menu_item_class
; 
1566   GtkContainerClass 
*container_class
; 
1568   object_class 
= (GtkObjectClass
*) klass
; 
1569   widget_class 
= (GtkWidgetClass
*) klass
; 
1570   menu_item_class 
= (GtkMenuItemClass
*) klass
; 
1571   container_class 
= (GtkContainerClass
*) klass
; 
1573   parent_class 
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ()); 
1575   widget_class
->draw 
= gtk_pixmap_menu_item_draw
; 
1576   widget_class
->expose_event 
= gtk_pixmap_menu_item_expose
; 
1577   widget_class
->map 
= gtk_pixmap_menu_item_map
; 
1578   widget_class
->size_allocate 
= gtk_pixmap_menu_item_size_allocate
; 
1579   widget_class
->size_request 
= gtk_pixmap_menu_item_size_request
; 
1581   container_class
->forall 
= gtk_pixmap_menu_item_forall
; 
1582   container_class
->remove 
= gtk_pixmap_menu_item_remove
; 
1584   klass
->orig_toggle_size 
= menu_item_class
->toggle_size
; 
1585   klass
->have_pixmap_count 
= 0; 
1589 gtk_pixmap_menu_item_init (GtkPixmapMenuItem 
*menu_item
) 
1593   mi 
= GTK_MENU_ITEM (menu_item
); 
1595   menu_item
->pixmap 
= NULL
; 
1599 gtk_pixmap_menu_item_draw (GtkWidget    
*widget
, 
1602   g_return_if_fail (widget 
!= NULL
); 
1603   g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
)); 
1604   g_return_if_fail (area 
!= NULL
); 
1606   if (GTK_WIDGET_CLASS (parent_class
)->draw
) 
1607     (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
); 
1609   if (GTK_WIDGET_DRAWABLE (widget
) && 
1610       GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) { 
1611     gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
); 
1616 gtk_pixmap_menu_item_expose (GtkWidget      
*widget
, 
1617                              GdkEventExpose 
*event
) 
1619   g_return_val_if_fail (widget 
!= NULL
, FALSE
); 
1620   g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
); 
1621   g_return_val_if_fail (event 
!= NULL
, FALSE
); 
1623   if (GTK_WIDGET_CLASS (parent_class
)->expose_event
) 
1624     (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
); 
1626   if (GTK_WIDGET_DRAWABLE (widget
) && 
1627       GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) { 
1628     gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
); 
1635  * gtk_pixmap_menu_item_set_pixmap 
1636  * @menu_item: Pointer to the pixmap menu item 
1637  * @pixmap: Pointer to a pixmap widget 
1639  * Set the pixmap of the menu item. 
1644 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem 
*menu_item
, 
1647   g_return_if_fail (menu_item 
!= NULL
); 
1648   g_return_if_fail (pixmap 
!= NULL
); 
1649   g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
)); 
1650   g_return_if_fail (GTK_IS_WIDGET (pixmap
)); 
1651   g_return_if_fail (menu_item
->pixmap 
== NULL
); 
1653   gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
)); 
1654   menu_item
->pixmap 
= pixmap
; 
1656   if (GTK_WIDGET_REALIZED (pixmap
->parent
) && 
1657       !GTK_WIDGET_REALIZED (pixmap
)) 
1658     gtk_widget_realize (pixmap
); 
1660   if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) { 
1661     if (GTK_WIDGET_MAPPED (pixmap
->parent
) && 
1662         GTK_WIDGET_VISIBLE(pixmap
) && 
1663         !GTK_WIDGET_MAPPED (pixmap
)) 
1664       gtk_widget_map (pixmap
); 
1667   changed_have_pixmap_status(menu_item
); 
1669   if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
)) 
1670     gtk_widget_queue_resize (pixmap
); 
1674 gtk_pixmap_menu_item_map (GtkWidget 
*widget
) 
1676   GtkPixmapMenuItem 
*menu_item
; 
1678   g_return_if_fail (widget 
!= NULL
); 
1679   g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
)); 
1681   menu_item 
= GTK_PIXMAP_MENU_ITEM(widget
); 
1683   GTK_WIDGET_CLASS(parent_class
)->map(widget
); 
1685   if (menu_item
->pixmap 
&& 
1686       GTK_WIDGET_VISIBLE (menu_item
->pixmap
) && 
1687       !GTK_WIDGET_MAPPED (menu_item
->pixmap
)) 
1688     gtk_widget_map (menu_item
->pixmap
); 
1692 gtk_pixmap_menu_item_size_allocate (GtkWidget        
*widget
, 
1693                                     GtkAllocation    
*allocation
) 
1695   GtkPixmapMenuItem 
*pmenu_item
; 
1697   pmenu_item 
= GTK_PIXMAP_MENU_ITEM(widget
); 
1699   if (pmenu_item
->pixmap 
&& GTK_WIDGET_VISIBLE(pmenu_item
)) 
1701       GtkAllocation child_allocation
; 
1704       border_width 
= GTK_CONTAINER (widget
)->border_width
; 
1706       child_allocation
.width 
= pmenu_item
->pixmap
->requisition
.width
; 
1707       child_allocation
.height 
= pmenu_item
->pixmap
->requisition
.height
; 
1708       child_allocation
.x 
= border_width 
+ BORDER_SPACING
; 
1709       child_allocation
.y 
= (border_width 
+ BORDER_SPACING
 
1710                             + (((allocation
->height 
- child_allocation
.height
) - child_allocation
.x
) 
1711                                / 2)); /* center pixmaps vertically */ 
1712       gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
); 
1715   if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
) 
1716     GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
); 
1720 gtk_pixmap_menu_item_forall (GtkContainer    
*container
, 
1721                              gboolean         include_internals
, 
1722                              GtkCallback      callback
, 
1723                              gpointer         callback_data
) 
1725   GtkPixmapMenuItem 
*menu_item
; 
1727   g_return_if_fail (container 
!= NULL
); 
1728   g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
)); 
1729   g_return_if_fail (callback 
!= NULL
); 
1731   menu_item 
= GTK_PIXMAP_MENU_ITEM (container
); 
1733   if (menu_item
->pixmap
) 
1734     (* callback
) (menu_item
->pixmap
, callback_data
); 
1736   GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
, 
1737                                             callback
,callback_data
); 
1741 gtk_pixmap_menu_item_size_request (GtkWidget      
*widget
, 
1742                                    GtkRequisition 
*requisition
) 
1744   GtkPixmapMenuItem 
*menu_item
; 
1745   GtkRequisition req 
= {0, 0}; 
1747   g_return_if_fail (widget 
!= NULL
); 
1748   g_return_if_fail (GTK_IS_MENU_ITEM (widget
)); 
1749   g_return_if_fail (requisition 
!= NULL
); 
1751   GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
); 
1753   menu_item 
= GTK_PIXMAP_MENU_ITEM (widget
); 
1755   if (menu_item
->pixmap
) 
1756     gtk_widget_size_request(menu_item
->pixmap
, &req
); 
1758   requisition
->height 
= MAX(req
.height 
+ GTK_CONTAINER(widget
)->border_width 
+ BORDER_SPACING
, (unsigned int) requisition
->height
); 
1759   requisition
->width 
+= (req
.width 
+ GTK_CONTAINER(widget
)->border_width 
+ BORDER_SPACING
); 
1763 gtk_pixmap_menu_item_remove (GtkContainer 
*container
, 
1767   gboolean widget_was_visible
; 
1769   g_return_if_fail (container 
!= NULL
); 
1770   g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
)); 
1771   g_return_if_fail (child 
!= NULL
); 
1772   g_return_if_fail (GTK_IS_WIDGET (child
)); 
1774   bin 
= GTK_BIN (container
); 
1775   g_return_if_fail ((bin
->child 
== child 
|| 
1776                      (GTK_PIXMAP_MENU_ITEM(container
)->pixmap 
== child
))); 
1778   widget_was_visible 
= GTK_WIDGET_VISIBLE (child
); 
1780   gtk_widget_unparent (child
); 
1781   if (bin
->child 
== child
) 
1784     GTK_PIXMAP_MENU_ITEM(container
)->pixmap 
= NULL
; 
1785     changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
)); 
1788   if (widget_was_visible
) 
1789     gtk_widget_queue_resize (GTK_WIDGET (container
)); 
1793 /* important to only call this if there was actually a _change_ in pixmap == NULL */ 
1795 changed_have_pixmap_status (GtkPixmapMenuItem 
*menu_item
) 
1797   if (menu_item
->pixmap 
!= NULL
) { 
1798     GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count 
+= 1; 
1800     if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count 
== 1) { 
1801       /* Install pixmap toggle size */ 
1802       GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size 
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
); 
1805     GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count 
-= 1; 
1807     if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count 
== 0) { 
1808       /* Install normal toggle size */ 
1809       GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size 
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
; 
1813   /* Note that we actually need to do this for _all_ GtkPixmapMenuItem 
1814      whenever the klass->toggle_size changes; but by doing it anytime 
1815      this function is called, we get the same effect, just because of 
1816      how the preferences option to show pixmaps works. Bogus, broken. 
1818   if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
))) 
1819     gtk_widget_queue_resize(GTK_WIDGET(menu_item
)); 
1822 #endif // USE_MENU_BITMAPS