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"
27 //-----------------------------------------------------------------------------
29 //-----------------------------------------------------------------------------
31 extern void wxapp_install_idle_handler();
34 #if (GTK_MINOR_VERSION > 0) && wxUSE_ACCEL
35 static wxString
GetHotKey( const wxMenuItem
& item
);
38 //-----------------------------------------------------------------------------
40 //-----------------------------------------------------------------------------
42 static wxString
wxReplaceUnderscore( const wxString
& title
)
46 /* GTK 1.2 wants to have "_" instead of "&" for accelerators */
48 for ( pc
= title
; *pc
!= wxT('\0'); pc
++ )
52 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
55 else if (*pc
== wxT('/'))
63 if ( *pc
== wxT('_') )
65 // underscores must be doubled to prevent them from being
66 // interpreted as accelerator character prefix by GTK
77 //-----------------------------------------------------------------------------
79 //-----------------------------------------------------------------------------
81 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
83 wxMenuBar::wxMenuBar( long style
)
85 /* the parent window is known after wxFrame::SetMenu() */
88 m_invokingWindow
= (wxWindow
*) NULL
;
90 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
91 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
93 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
97 m_menus
.DeleteContents( TRUE
);
99 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
100 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
101 m_accel
= gtk_accel_group_new();
102 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
103 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
105 m_menubar
= gtk_menu_bar_new();
108 if (style
& wxMB_DOCKABLE
)
110 m_widget
= gtk_handle_box_new();
111 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
112 gtk_widget_show( GTK_WIDGET(m_menubar
) );
116 m_widget
= GTK_WIDGET(m_menubar
);
124 wxMenuBar::wxMenuBar()
126 /* the parent window is known after wxFrame::SetMenu() */
127 m_needParent
= FALSE
;
129 m_invokingWindow
= (wxWindow
*) NULL
;
131 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
132 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
134 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
138 m_menus
.DeleteContents( TRUE
);
140 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
141 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
142 m_accel
= gtk_accel_group_new();
143 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
144 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
146 m_menubar
= gtk_menu_bar_new();
149 m_widget
= GTK_WIDGET(m_menubar
);
156 wxMenuBar::~wxMenuBar()
158 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
161 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
163 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
165 #if (GTK_MINOR_VERSION > 0)
166 wxWindow
*top_frame
= win
;
167 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
168 top_frame
= top_frame
->GetParent();
170 /* support for native hot keys */
171 gtk_accel_group_detach( menu
->m_accel
, GTK_OBJECT(top_frame
->m_widget
) );
174 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
177 wxMenuItem
*menuitem
= node
->GetData();
178 if (menuitem
->IsSubMenu())
179 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
180 node
= node
->GetNext();
184 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
186 menu
->SetInvokingWindow( win
);
188 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
189 wxWindow
*top_frame
= win
;
190 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
191 top_frame
= top_frame
->GetParent();
193 /* support for native hot keys */
194 GtkObject
*obj
= GTK_OBJECT(top_frame
->m_widget
);
195 if ( !g_slist_find( menu
->m_accel
->attach_objects
, obj
) )
196 gtk_accel_group_attach( menu
->m_accel
, obj
);
199 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
202 wxMenuItem
*menuitem
= node
->GetData();
203 if (menuitem
->IsSubMenu())
204 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
205 node
= node
->GetNext();
209 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
211 m_invokingWindow
= win
;
212 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
213 wxWindow
*top_frame
= win
;
214 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
215 top_frame
= top_frame
->GetParent();
217 /* support for native key accelerators indicated by underscroes */
218 GtkObject
*obj
= GTK_OBJECT(top_frame
->m_widget
);
219 if ( !g_slist_find( m_accel
->attach_objects
, obj
) )
220 gtk_accel_group_attach( m_accel
, obj
);
223 wxMenuList::Node
*node
= m_menus
.GetFirst();
226 wxMenu
*menu
= node
->GetData();
227 wxMenubarSetInvokingWindow( menu
, win
);
228 node
= node
->GetNext();
232 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
234 m_invokingWindow
= (wxWindow
*) NULL
;
235 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
236 wxWindow
*top_frame
= win
;
237 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
238 top_frame
= top_frame
->GetParent();
240 /* support for native key accelerators indicated by underscroes */
241 gtk_accel_group_detach( m_accel
, GTK_OBJECT(top_frame
->m_widget
) );
244 wxMenuList::Node
*node
= m_menus
.GetFirst();
247 wxMenu
*menu
= node
->GetData();
248 wxMenubarUnsetInvokingWindow( menu
, win
);
249 node
= node
->GetNext();
253 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
255 if ( !wxMenuBarBase::Append( menu
, title
) )
258 return GtkAppend(menu
, title
);
261 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
263 wxString
str( wxReplaceUnderscore( title
) );
265 /* this doesn't have much effect right now */
266 menu
->SetTitle( str
);
268 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
269 #if (GTK_MINOR_VERSION > 0) && (GTK_MICRO_VERSION > 0)
271 /* local buffer in multibyte form */
273 buf
<< wxT('/') << str
.c_str();
275 char *cbuf
= new char[buf
.Length()+1];
276 strcpy(cbuf
, buf
.mbc_str());
278 GtkItemFactoryEntry entry
;
279 entry
.path
= (gchar
*)cbuf
; // const_cast
280 entry
.accelerator
= (gchar
*) NULL
;
281 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
282 entry
.callback_action
= 0;
283 entry
.item_type
= "<Branch>";
285 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
286 /* in order to get the pointer to the item we need the item text _without_ underscores */
287 wxString tmp
= wxT("<main>/");
289 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
291 // contrary to the common sense, we must throw out _all_ underscores,
292 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
293 // might naively think). IMHO it's a bug in GTK+ (VZ)
294 while (*pc
== wxT('_'))
298 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, tmp
.mb_str() );
299 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
303 menu
->m_owner
= gtk_menu_item_new_with_label( str
.mb_str() );
304 gtk_widget_show( menu
->m_owner
);
305 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
307 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
311 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
312 // adding menu later on.
313 if (m_invokingWindow
)
314 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
319 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
321 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
325 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
326 // of version 1.2.6), so we first append the item and then change its
328 if ( !GtkAppend(menu
, title
) )
331 if (pos
+1 >= m_menus
.GetCount())
334 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
335 gpointer data
= g_list_last(menu_shell
->children
)->data
;
336 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
337 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
341 // this should be easy to do with GTK 1.0 - can use standard functions for
342 // this and don't need any hacks like above, but as I don't have GTK 1.0
343 // any more I can't do it
344 wxFAIL_MSG( wxT("TODO") );
347 #endif // GTK 1.2/1.0
350 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
352 // remove the old item and insert a new one
353 wxMenu
*menuOld
= Remove(pos
);
354 if ( menuOld
&& !Insert(pos
, menu
, title
) )
356 return (wxMenu
*) NULL
;
359 // either Insert() succeeded or Remove() failed and menuOld is NULL
363 wxMenu
*wxMenuBar::Remove(size_t pos
)
365 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
367 return (wxMenu
*) NULL
;
370 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
372 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
373 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
376 // unparent calls unref() and that would delete the widget so we raise
377 // the ref count to 2 artificially before invoking unparent.
378 gtk_widget_ref( menu
->m_menu
);
379 gtk_widget_unparent( menu
->m_menu
);
381 gtk_widget_destroy( menu
->m_owner
);
384 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
385 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
391 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
393 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
395 int res
= menu
->FindItem( itemString
);
396 if (res
!= wxNOT_FOUND
)
400 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
403 wxMenuItem
*item
= node
->GetData();
404 if (item
->IsSubMenu())
405 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
407 node
= node
->GetNext();
413 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
415 wxMenuList::Node
*node
= m_menus
.GetFirst();
418 wxMenu
*menu
= node
->GetData();
419 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
422 node
= node
->GetNext();
428 // Find a wxMenuItem using its id. Recurses down into sub-menus
429 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
431 wxMenuItem
* result
= menu
->FindChildItem(id
);
433 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
434 while ( node
&& result
== NULL
)
436 wxMenuItem
*item
= node
->GetData();
437 if (item
->IsSubMenu())
439 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
441 node
= node
->GetNext();
447 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
449 wxMenuItem
* result
= 0;
450 wxMenuList::Node
*node
= m_menus
.GetFirst();
451 while (node
&& result
== 0)
453 wxMenu
*menu
= node
->GetData();
454 result
= FindMenuItemByIdRecursive( menu
, id
);
455 node
= node
->GetNext();
460 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
466 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
468 wxMenuList::Node
*node
= m_menus
.Item( pos
);
470 wxCHECK_RET( node
, wxT("menu not found") );
472 wxMenu
* menu
= node
->GetData();
475 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
478 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
480 wxMenuList::Node
*node
= m_menus
.Item( pos
);
482 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
484 wxMenu
* menu
= node
->GetData();
487 wxString
text( menu
->GetTitle() );
488 #if (GTK_MINOR_VERSION > 0)
489 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
491 if ( *pc
== wxT('_') || *pc
== wxT('&') )
493 // '_' is the escape character for GTK+ and '&' is the one for
494 // wxWindows - skip both of them
502 #endif // GTK+ 1.2/1.0
507 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
509 wxMenuList::Node
*node
= m_menus
.Item( pos
);
511 wxCHECK_RET( node
, wxT("menu not found") );
513 wxMenu
* menu
= node
->GetData();
515 wxString
str( wxReplaceUnderscore( label
) );
517 menu
->SetTitle( str
);
521 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
524 gtk_label_set( label
, str
.mb_str());
526 /* reparse key accel */
527 (void)gtk_label_parse_uline (GTK_LABEL(label
), str
.mb_str() );
528 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
533 //-----------------------------------------------------------------------------
535 //-----------------------------------------------------------------------------
537 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
539 if (g_isIdle
) wxapp_install_idle_handler();
541 int id
= menu
->FindMenuIdByMenuItem(widget
);
543 /* should find it for normal (not popup) menu */
544 wxASSERT( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
) );
546 if (!menu
->IsEnabled(id
))
549 wxMenuItem
* item
= menu
->FindChildItem( id
);
550 wxCHECK_RET( item
, wxT("error in menu item callback") );
552 if (item
->IsCheckable())
554 bool isReallyChecked
= item
->IsChecked();
555 if ( item
->wxMenuItemBase::IsChecked() == isReallyChecked
)
557 /* the menu item has been checked by calling wxMenuItem->Check() */
562 /* the user pressed on the menu item -> report and make consistent
564 item
->wxMenuItemBase::Check(isReallyChecked
);
568 wxCommandEvent
event( wxEVT_COMMAND_MENU_SELECTED
, id
);
569 event
.SetEventObject( menu
);
570 if (item
->IsCheckable())
571 event
.SetInt( item
->IsChecked() );
573 #if wxUSE_MENU_CALLBACK
574 if (menu
->GetCallback())
576 (void) (*(menu
->GetCallback())) (*menu
, event
);
579 #endif // wxUSE_MENU_CALLBACK
581 if (menu
->GetEventHandler()->ProcessEvent(event
))
584 wxWindow
*win
= menu
->GetInvokingWindow();
586 win
->GetEventHandler()->ProcessEvent( event
);
589 //-----------------------------------------------------------------------------
591 //-----------------------------------------------------------------------------
593 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
595 if (g_isIdle
) wxapp_install_idle_handler();
597 int id
= menu
->FindMenuIdByMenuItem(widget
);
599 wxASSERT( id
!= -1 ); // should find it!
601 if (!menu
->IsEnabled(id
))
604 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
605 event
.SetEventObject( menu
);
607 if (menu
->GetEventHandler()->ProcessEvent(event
))
610 wxWindow
*win
= menu
->GetInvokingWindow();
611 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
614 //-----------------------------------------------------------------------------
616 //-----------------------------------------------------------------------------
618 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
620 if (g_isIdle
) wxapp_install_idle_handler();
622 int id
= menu
->FindMenuIdByMenuItem(widget
);
624 wxASSERT( id
!= -1 ); // should find it!
626 if (!menu
->IsEnabled(id
))
629 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
630 event
.SetEventObject( menu
);
632 if (menu
->GetEventHandler()->ProcessEvent(event
))
635 wxWindow
*win
= menu
->GetInvokingWindow();
637 win
->GetEventHandler()->ProcessEvent( event
);
640 //-----------------------------------------------------------------------------
642 //-----------------------------------------------------------------------------
644 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxMenuItemBase
)
646 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
648 const wxString
& name
,
649 const wxString
& help
,
653 return new wxMenuItem(parentMenu
, id
, name
, help
, isCheckable
, subMenu
);
656 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
658 const wxString
& text
,
659 const wxString
& help
,
664 m_isCheckable
= isCheckable
;
668 m_parentMenu
= parentMenu
;
671 m_menuItem
= (GtkWidget
*) NULL
;
676 wxMenuItem::~wxMenuItem()
678 // don't delete menu items, the menus take care of that
681 // return the menu item text without any menu accels
683 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
686 #if (GTK_MINOR_VERSION > 0)
687 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
689 if ( *pc
== wxT('_') || *pc
== wxT('&') )
691 // '_' is the escape character for GTK+ and '&' is the one for
692 // wxWindows - skip both of them
700 #endif // GTK+ 1.2/1.0
705 void wxMenuItem::SetText( const wxString
& str
)
711 GtkLabel
*label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
714 gtk_label_set( label
, m_text
.mb_str());
716 /* reparse key accel */
717 (void)gtk_label_parse_uline (GTK_LABEL(label
), m_text
.mb_str() );
718 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
722 // it's valid for this function to be called even if m_menuItem == NULL
723 void wxMenuItem::DoSetText( const wxString
& str
)
725 /* '\t' is the deliminator indicating a hot key */
727 const wxChar
*pc
= str
;
728 for (; (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')); pc
++ )
732 #if (GTK_MINOR_VERSION > 0)
735 else if ( *pc
== wxT('_') ) // escape underscores
739 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
741 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
748 /* only GTK 1.2 knows about hot keys */
750 #if (GTK_MINOR_VERSION > 0)
761 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
766 return (wxAcceleratorEntry
*)NULL
;
769 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
771 label
<< wxT('\t') << GetHotKey();
773 return wxGetAccelFromString(label
);
776 #endif // wxUSE_ACCEL
778 void wxMenuItem::Check( bool check
)
780 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
782 wxCHECK_RET( IsCheckable(), wxT("Can't check uncheckable item!") )
784 if (check
== m_isChecked
)
787 wxMenuItemBase::Check( check
);
788 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
791 void wxMenuItem::Enable( bool enable
)
793 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
795 gtk_widget_set_sensitive( m_menuItem
, enable
);
796 wxMenuItemBase::Enable( enable
);
799 bool wxMenuItem::IsChecked() const
801 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
803 wxCHECK_MSG( IsCheckable(), FALSE
,
804 wxT("can't get state of uncheckable item!") );
806 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
809 wxString
wxMenuItem::GetFactoryPath() const
811 /* in order to get the pointer to the item we need the item text _without_
813 wxString
path( wxT("<main>/") );
819 //-----------------------------------------------------------------------------
821 //-----------------------------------------------------------------------------
823 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
827 #if (GTK_MINOR_VERSION > 0)
828 m_accel
= gtk_accel_group_new();
829 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
830 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
832 m_menu
= gtk_menu_new(); // Do not show!
835 m_owner
= (GtkWidget
*) NULL
;
837 #if (GTK_MINOR_VERSION > 0)
838 /* Tearoffs are entries, just like separators. So if we want this
839 menu to be a tear-off one, we just append a tearoff entry
841 if(m_style
& wxMENU_TEAROFF
)
843 GtkItemFactoryEntry entry
;
844 entry
.path
= "/tearoff";
845 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
846 entry
.callback_action
= 0;
847 entry
.item_type
= "<Tearoff>";
848 entry
.accelerator
= (gchar
*) NULL
;
849 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
850 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
854 // append the title as the very first entry if we have it
866 gtk_widget_destroy( m_menu
);
868 gtk_object_unref( GTK_OBJECT(m_factory
) );
871 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
875 if ( mitem
->IsSeparator() )
877 #if (GTK_MINOR_VERSION > 0)
878 GtkItemFactoryEntry entry
;
880 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
881 entry
.callback_action
= 0;
882 entry
.item_type
= "<Separator>";
883 entry
.accelerator
= (gchar
*) NULL
;
885 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
887 /* this will be wrong for more than one separator. do we care? */
888 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
890 menuItem
= gtk_menu_item_new();
891 #endif // GTK 1.2/1.0
893 else if ( mitem
->IsSubMenu() )
895 #if (GTK_MINOR_VERSION > 0)
896 /* text has "_" instead of "&" after mitem->SetText() */
897 wxString
text( mitem
->GetText() );
899 /* local buffer in multibyte form */
902 strcat( buf
, text
.mb_str() );
904 GtkItemFactoryEntry entry
;
906 entry
.callback
= (GtkItemFactoryCallback
) 0;
907 entry
.callback_action
= 0;
908 entry
.item_type
= "<Branch>";
909 entry
.accelerator
= (gchar
*) NULL
;
911 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
913 wxString
path( mitem
->GetFactoryPath() );
914 menuItem
= gtk_item_factory_get_item( m_factory
, path
.mb_str() );
916 menuItem
= gtk_menu_item_new_with_label(mitem
->GetText().mbc_str());
917 #endif // GTK 1.2/1.0
919 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), mitem
->GetSubMenu()->m_menu
);
921 // if adding a submenu to a menu already existing in the menu bar, we
922 // must set invoking window to allow processing events from this
924 if ( m_invokingWindow
)
925 wxMenubarSetInvokingWindow(mitem
->GetSubMenu(), m_invokingWindow
);
927 else // a normal item
929 #if (GTK_MINOR_VERSION > 0)
930 /* text has "_" instead of "&" after mitem->SetText() */
931 wxString
text( mitem
->GetText() );
933 /* local buffer in multibyte form */
936 strcat( buf
, text
.mb_str() );
938 GtkItemFactoryEntry entry
;
940 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
941 entry
.callback_action
= 0;
942 if ( mitem
->IsCheckable() )
943 entry
.item_type
= "<CheckItem>";
945 entry
.item_type
= "<Item>";
946 entry
.accelerator
= (gchar
*) NULL
;
949 // due to an apparent bug in GTK+, we have to use a static buffer here -
950 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
952 static char s_accel
[50]; // must be big enougg
953 wxString
tmp( GetHotKey(*mitem
) );
954 strncpy(s_accel
, tmp
.mb_str(), WXSIZEOF(s_accel
));
955 entry
.accelerator
= s_accel
;
956 #else // !wxUSE_ACCEL
957 entry
.accelerator
= (char*) NULL
;
958 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
960 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
962 wxString
path( mitem
->GetFactoryPath() );
963 menuItem
= gtk_item_factory_get_widget( m_factory
, path
.mb_str() );
965 menuItem
= checkable
? gtk_check_menu_item_new_with_label( mitem
->GetText().mb_str() )
966 : gtk_menu_item_new_with_label( mitem
->GetText().mb_str() );
968 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
969 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
971 #endif // GTK+ 1.2/1.0
974 if ( !mitem
->IsSeparator() )
976 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
977 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
980 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
981 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
985 #if GTK_MINOR_VERSION == 0
986 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
987 gtk_widget_show( menuItem
);
990 mitem
->SetMenuItem(menuItem
);
995 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
997 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1000 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1002 if ( !wxMenuBase::DoInsert(pos
, item
) )
1006 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1007 // of version 1.2.6), so we first append the item and then change its
1009 if ( !GtkAppend(item
) )
1012 if ( m_style
& wxMENU_TEAROFF
)
1014 // change the position as the first item is the tear-off marker
1018 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1019 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1020 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1021 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1025 // this should be easy to do...
1026 wxFAIL_MSG( wxT("not implemented") );
1029 #endif // GTK 1.2/1.0
1032 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1034 if ( !wxMenuBase::DoRemove(item
) )
1035 return (wxMenuItem
*)NULL
;
1037 // TODO: this code doesn't delete the item factory item and this seems
1038 // impossible as of GTK 1.2.6.
1039 gtk_widget_destroy( item
->GetMenuItem() );
1044 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1046 wxNode
*node
= m_items
.First();
1049 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
1050 if (item
->GetMenuItem() == menuItem
)
1051 return item
->GetId();
1052 node
= node
->Next();
1058 // ----------------------------------------------------------------------------
1060 // ----------------------------------------------------------------------------
1062 #if (GTK_MINOR_VERSION > 0) && wxUSE_ACCEL
1063 static wxString
GetHotKey( const wxMenuItem
& item
)
1067 wxAcceleratorEntry
*accel
= item
.GetAccel();
1070 int flags
= accel
->GetFlags();
1071 if ( flags
& wxACCEL_ALT
)
1072 hotkey
+= wxT("<alt>");
1073 if ( flags
& wxACCEL_CTRL
)
1074 hotkey
+= wxT("<control>");
1075 if ( flags
& wxACCEL_SHIFT
)
1076 hotkey
+= wxT("<shift>");
1078 int code
= accel
->GetKeyCode();
1093 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1096 // GTK seems to use XStringToKeySym here
1097 case WXK_NUMPAD_INSERT
:
1098 hotkey
<< wxT("KP_Insert" );
1100 case WXK_NUMPAD_DELETE
:
1101 hotkey
<< wxT("KP_Delete" );
1104 hotkey
<< wxT("Insert" );
1107 hotkey
<< wxT("Delete" );
1110 // if there are any other keys wxGetAccelFromString() may return,
1111 // we should process them here
1114 if ( wxIsalnum(code
) )
1116 hotkey
<< (wxChar
)code
;
1121 wxFAIL_MSG( wxT("unknown keyboard accel") );
1129 #endif // wxUSE_ACCEL