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 */
116 for ( pc
= title
; *pc
!= wxT('\0'); pc
++ )
120 #if GTK_CHECK_VERSION(1, 2, 0)
124 #if GTK_CHECK_VERSION(2, 0, 0)
125 else if (*pc
== wxT('/'))
129 else if (*pc
== wxT('\\'))
133 #elif GTK_CHECK_VERSION(1, 2, 0)
134 else if (*pc
== wxT('/'))
142 if ( *pc
== wxT('_') )
144 // underscores must be doubled to prevent them from being
145 // interpreted as accelerator character prefix by GTK
156 //-----------------------------------------------------------------------------
158 //-----------------------------------------------------------------------------
160 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
,wxWindow
)
162 wxMenuBar::wxMenuBar( long style
)
164 /* the parent window is known after wxFrame::SetMenu() */
165 m_needParent
= FALSE
;
167 m_invokingWindow
= (wxWindow
*) NULL
;
169 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
170 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, style
, wxDefaultValidator
, wxT("menubar") ))
172 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
176 m_menus
.DeleteContents( TRUE
);
178 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
179 #if GTK_CHECK_VERSION(1, 2, 1)
180 m_accel
= gtk_accel_group_new();
181 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
182 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
184 m_menubar
= gtk_menu_bar_new();
187 if (style
& wxMB_DOCKABLE
)
189 m_widget
= gtk_handle_box_new();
190 gtk_container_add( GTK_CONTAINER(m_widget
), GTK_WIDGET(m_menubar
) );
191 gtk_widget_show( GTK_WIDGET(m_menubar
) );
195 m_widget
= GTK_WIDGET(m_menubar
);
203 wxMenuBar::wxMenuBar()
205 /* the parent window is known after wxFrame::SetMenu() */
206 m_needParent
= FALSE
;
208 m_invokingWindow
= (wxWindow
*) NULL
;
210 if (!PreCreation( (wxWindow
*) NULL
, wxDefaultPosition
, wxDefaultSize
) ||
211 !CreateBase( (wxWindow
*) NULL
, -1, wxDefaultPosition
, wxDefaultSize
, 0, wxDefaultValidator
, wxT("menubar") ))
213 wxFAIL_MSG( wxT("wxMenuBar creation failed") );
217 m_menus
.DeleteContents( TRUE
);
219 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
220 #if GTK_CHECK_VERSION(1, 2, 1)
221 m_accel
= gtk_accel_group_new();
222 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU_BAR
, "<main>", m_accel
);
223 m_menubar
= gtk_item_factory_get_widget( m_factory
, "<main>" );
225 m_menubar
= gtk_menu_bar_new();
228 m_widget
= GTK_WIDGET(m_menubar
);
235 wxMenuBar::~wxMenuBar()
237 // gtk_object_unref( GTK_OBJECT(m_factory) ); why not ?
240 static void wxMenubarUnsetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
242 menu
->SetInvokingWindow( (wxWindow
*) NULL
);
244 #if GTK_CHECK_VERSION(1, 2, 0)
245 wxWindow
*top_frame
= win
;
246 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
247 top_frame
= top_frame
->GetParent();
249 /* support for native hot keys */
250 gtk_accel_group_detach( menu
->m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
253 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
256 wxMenuItem
*menuitem
= node
->GetData();
257 if (menuitem
->IsSubMenu())
258 wxMenubarUnsetInvokingWindow( menuitem
->GetSubMenu(), win
);
259 node
= node
->GetNext();
263 static void wxMenubarSetInvokingWindow( wxMenu
*menu
, wxWindow
*win
)
265 menu
->SetInvokingWindow( win
);
267 #if GTK_CHECK_VERSION(1, 2, 1)
268 wxWindow
*top_frame
= win
;
269 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
270 top_frame
= top_frame
->GetParent();
272 /* support for native hot keys */
273 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
274 if ( !g_slist_find( ACCEL_OBJECTS(menu
->m_accel
), obj
) )
275 gtk_accel_group_attach( menu
->m_accel
, obj
);
276 #endif // GTK+ 1.2.1+
278 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
281 wxMenuItem
*menuitem
= node
->GetData();
282 if (menuitem
->IsSubMenu())
283 wxMenubarSetInvokingWindow( menuitem
->GetSubMenu(), win
);
284 node
= node
->GetNext();
288 void wxMenuBar::SetInvokingWindow( wxWindow
*win
)
290 m_invokingWindow
= win
;
291 #if GTK_CHECK_VERSION(1, 2, 1)
292 wxWindow
*top_frame
= win
;
293 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
294 top_frame
= top_frame
->GetParent();
296 /* support for native key accelerators indicated by underscroes */
297 ACCEL_OBJECT
*obj
= ACCEL_OBJ_CAST(top_frame
->m_widget
);
298 if ( !g_slist_find( ACCEL_OBJECTS(m_accel
), obj
) )
299 gtk_accel_group_attach( m_accel
, obj
);
300 #endif // GTK+ 1.2.1+
302 wxMenuList::Node
*node
= m_menus
.GetFirst();
305 wxMenu
*menu
= node
->GetData();
306 wxMenubarSetInvokingWindow( menu
, win
);
307 node
= node
->GetNext();
311 void wxMenuBar::UnsetInvokingWindow( wxWindow
*win
)
313 m_invokingWindow
= (wxWindow
*) NULL
;
314 #if GTK_CHECK_VERSION(1, 2, 1)
315 wxWindow
*top_frame
= win
;
316 while (top_frame
->GetParent() && !(top_frame
->IsTopLevel()))
317 top_frame
= top_frame
->GetParent();
319 /* support for native key accelerators indicated by underscroes */
320 gtk_accel_group_detach( m_accel
, ACCEL_OBJ_CAST(top_frame
->m_widget
) );
321 #endif // GTK+ 1.2.1+
323 wxMenuList::Node
*node
= m_menus
.GetFirst();
326 wxMenu
*menu
= node
->GetData();
327 wxMenubarUnsetInvokingWindow( menu
, win
);
328 node
= node
->GetNext();
332 bool wxMenuBar::Append( wxMenu
*menu
, const wxString
&title
)
334 if ( !wxMenuBarBase::Append( menu
, title
) )
337 return GtkAppend(menu
, title
);
340 bool wxMenuBar::GtkAppend(wxMenu
*menu
, const wxString
& title
)
342 wxString
str( wxReplaceUnderscore( title
) );
344 /* this doesn't have much effect right now */
345 menu
->SetTitle( str
);
347 /* GTK 1.2.0 doesn't have gtk_item_factory_get_item(), but GTK 1.2.1 has. */
348 #if GTK_CHECK_VERSION(1, 2, 1)
350 /* local buffer in multibyte form */
352 buf
<< wxT('/') << str
.c_str();
354 char *cbuf
= new char[buf
.Length()+1];
355 strcpy(cbuf
, buf
.mbc_str());
357 GtkItemFactoryEntry entry
;
358 entry
.path
= (gchar
*)cbuf
; // const_cast
359 entry
.accelerator
= (gchar
*) NULL
;
360 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
361 entry
.callback_action
= 0;
362 entry
.item_type
= (char *)"<Branch>";
364 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
365 /* in order to get the pointer to the item we need the item text _without_ underscores */
366 wxString tmp
= wxT("<main>/");
368 for ( pc
= str
; *pc
!= wxT('\0'); pc
++ )
370 // contrary to the common sense, we must throw out _all_ underscores,
371 // (i.e. "Hello__World" => "HelloWorld" and not "Hello_World" as we
372 // might naively think). IMHO it's a bug in GTK+ (VZ)
373 while (*pc
== wxT('_'))
377 menu
->m_owner
= gtk_item_factory_get_item( m_factory
, tmp
.mb_str() );
378 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
382 menu
->m_owner
= gtk_menu_item_new_with_label( str
.mb_str() );
383 gtk_widget_show( menu
->m_owner
);
384 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menu
->m_owner
), menu
->m_menu
);
386 gtk_menu_bar_append( GTK_MENU_BAR(m_menubar
), menu
->m_owner
);
390 // m_invokingWindow is set after wxFrame::SetMenuBar(). This call enables
391 // adding menu later on.
392 if (m_invokingWindow
)
393 wxMenubarSetInvokingWindow( menu
, m_invokingWindow
);
398 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
400 if ( !wxMenuBarBase::Insert(pos
, menu
, title
) )
404 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
405 // of version 1.2.6), so we first append the item and then change its
407 if ( !GtkAppend(menu
, title
) )
410 if (pos
+1 >= m_menus
.GetCount())
413 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
414 gpointer data
= g_list_last(menu_shell
->children
)->data
;
415 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
416 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
420 // this should be easy to do with GTK 1.0 - can use standard functions for
421 // this and don't need any hacks like above, but as I don't have GTK 1.0
422 // any more I can't do it
423 wxFAIL_MSG( wxT("TODO") );
426 #endif // GTK 1.2/1.0
429 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
431 // remove the old item and insert a new one
432 wxMenu
*menuOld
= Remove(pos
);
433 if ( menuOld
&& !Insert(pos
, menu
, title
) )
435 return (wxMenu
*) NULL
;
438 // either Insert() succeeded or Remove() failed and menuOld is NULL
442 wxMenu
*wxMenuBar::Remove(size_t pos
)
444 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
446 return (wxMenu
*) NULL
;
449 GtkMenuShell *menu_shell = GTK_MENU_SHELL(m_factory->widget);
451 printf( "factory entries before %d\n", (int)g_slist_length(m_factory->items) );
452 printf( "menu shell entries before %d\n", (int)g_list_length( menu_shell->children ) );
455 // unparent calls unref() and that would delete the widget so we raise
456 // the ref count to 2 artificially before invoking unparent.
457 gtk_widget_ref( menu
->m_menu
);
458 gtk_widget_unparent( menu
->m_menu
);
460 gtk_widget_destroy( menu
->m_owner
);
463 printf( "factory entries after %d\n", (int)g_slist_length(m_factory->items) );
464 printf( "menu shell entries after %d\n", (int)g_list_length( menu_shell->children ) );
470 static int FindMenuItemRecursive( const wxMenu
*menu
, const wxString
&menuString
, const wxString
&itemString
)
472 if (wxMenuItem::GetLabelFromText(menu
->GetTitle()) == wxMenuItem::GetLabelFromText(menuString
))
474 int res
= menu
->FindItem( itemString
);
475 if (res
!= wxNOT_FOUND
)
479 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
482 wxMenuItem
*item
= node
->GetData();
483 if (item
->IsSubMenu())
484 return FindMenuItemRecursive(item
->GetSubMenu(), menuString
, itemString
);
486 node
= node
->GetNext();
492 int wxMenuBar::FindMenuItem( const wxString
&menuString
, const wxString
&itemString
) const
494 wxMenuList::Node
*node
= m_menus
.GetFirst();
497 wxMenu
*menu
= node
->GetData();
498 int res
= FindMenuItemRecursive( menu
, menuString
, itemString
);
501 node
= node
->GetNext();
507 // Find a wxMenuItem using its id. Recurses down into sub-menus
508 static wxMenuItem
* FindMenuItemByIdRecursive(const wxMenu
* menu
, int id
)
510 wxMenuItem
* result
= menu
->FindChildItem(id
);
512 wxMenuItemList::Node
*node
= menu
->GetMenuItems().GetFirst();
513 while ( node
&& result
== NULL
)
515 wxMenuItem
*item
= node
->GetData();
516 if (item
->IsSubMenu())
518 result
= FindMenuItemByIdRecursive( item
->GetSubMenu(), id
);
520 node
= node
->GetNext();
526 wxMenuItem
* wxMenuBar::FindItem( int id
, wxMenu
**menuForItem
) const
528 wxMenuItem
* result
= 0;
529 wxMenuList::Node
*node
= m_menus
.GetFirst();
530 while (node
&& result
== 0)
532 wxMenu
*menu
= node
->GetData();
533 result
= FindMenuItemByIdRecursive( menu
, id
);
534 node
= node
->GetNext();
539 *menuForItem
= result
? result
->GetMenu() : (wxMenu
*)NULL
;
545 void wxMenuBar::EnableTop( size_t pos
, bool flag
)
547 wxMenuList::Node
*node
= m_menus
.Item( pos
);
549 wxCHECK_RET( node
, wxT("menu not found") );
551 wxMenu
* menu
= node
->GetData();
554 gtk_widget_set_sensitive( menu
->m_owner
, flag
);
557 wxString
wxMenuBar::GetLabelTop( size_t pos
) const
559 wxMenuList::Node
*node
= m_menus
.Item( pos
);
561 wxCHECK_MSG( node
, wxT("invalid"), wxT("menu not found") );
563 wxMenu
* menu
= node
->GetData();
566 wxString
text( menu
->GetTitle() );
567 #if GTK_CHECK_VERSION(1, 2, 0)
568 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
570 if ( *pc
== wxT('_') || *pc
== wxT('&') )
572 // '_' is the escape character for GTK+ and '&' is the one for
573 // wxWindows - skip both of them
581 #endif // GTK+ 1.2/1.0
586 void wxMenuBar::SetLabelTop( size_t pos
, const wxString
& label
)
588 wxMenuList::Node
*node
= m_menus
.Item( pos
);
590 wxCHECK_RET( node
, wxT("menu not found") );
592 wxMenu
* menu
= node
->GetData();
594 wxString
str( wxReplaceUnderscore( label
) );
596 menu
->SetTitle( str
);
600 GtkLabel
*label
= GTK_LABEL( GTK_BIN(menu
->m_owner
)->child
);
603 gtk_label_set( label
, str
.mb_str());
605 /* reparse key accel */
606 (void)gtk_label_parse_uline (GTK_LABEL(label
), str
.mb_str() );
607 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
612 //-----------------------------------------------------------------------------
614 //-----------------------------------------------------------------------------
616 static void gtk_menu_clicked_callback( GtkWidget
*widget
, wxMenu
*menu
)
619 wxapp_install_idle_handler();
621 int id
= menu
->FindMenuIdByMenuItem(widget
);
623 /* should find it for normal (not popup) menu */
624 wxASSERT_MSG( (id
!= -1) || (menu
->GetInvokingWindow() != NULL
),
625 _T("menu item not found in gtk_menu_clicked_callback") );
627 if (!menu
->IsEnabled(id
))
630 wxMenuItem
* item
= menu
->FindChildItem( id
);
631 wxCHECK_RET( item
, wxT("error in menu item callback") );
633 if (item
->IsCheckable())
635 bool isReallyChecked
= item
->IsChecked(),
636 isInternallyChecked
= item
->wxMenuItemBase::IsChecked();
638 // ensure that the internal state is always consistent with what is
639 // shown on the screen
640 item
->wxMenuItemBase::Check(isReallyChecked
);
642 // we must not report the events for the radio button going up nor the
643 // events resulting from the calls to wxMenuItem::Check()
644 if ( (item
->GetKind() == wxITEM_RADIO
&& !isReallyChecked
) ||
645 (isInternallyChecked
== isReallyChecked
) )
650 // the user pressed on the menu item: report the event below
653 menu
->SendEvent(id
, item
->IsCheckable() ? item
->IsChecked() : -1);
656 //-----------------------------------------------------------------------------
658 //-----------------------------------------------------------------------------
660 static void gtk_menu_hilight_callback( GtkWidget
*widget
, wxMenu
*menu
)
662 if (g_isIdle
) wxapp_install_idle_handler();
664 int id
= menu
->FindMenuIdByMenuItem(widget
);
666 wxASSERT( id
!= -1 ); // should find it!
668 if (!menu
->IsEnabled(id
))
671 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, id
);
672 event
.SetEventObject( menu
);
674 if (menu
->GetEventHandler()->ProcessEvent(event
))
677 wxWindow
*win
= menu
->GetInvokingWindow();
678 if (win
) win
->GetEventHandler()->ProcessEvent( event
);
681 //-----------------------------------------------------------------------------
683 //-----------------------------------------------------------------------------
685 static void gtk_menu_nolight_callback( GtkWidget
*widget
, wxMenu
*menu
)
687 if (g_isIdle
) wxapp_install_idle_handler();
689 int id
= menu
->FindMenuIdByMenuItem(widget
);
691 wxASSERT( id
!= -1 ); // should find it!
693 if (!menu
->IsEnabled(id
))
696 wxMenuEvent
event( wxEVT_MENU_HIGHLIGHT
, -1 );
697 event
.SetEventObject( menu
);
699 if (menu
->GetEventHandler()->ProcessEvent(event
))
702 wxWindow
*win
= menu
->GetInvokingWindow();
704 win
->GetEventHandler()->ProcessEvent( event
);
707 //-----------------------------------------------------------------------------
709 //-----------------------------------------------------------------------------
711 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem
, wxObject
)
713 wxMenuItem
*wxMenuItemBase::New(wxMenu
*parentMenu
,
715 const wxString
& name
,
716 const wxString
& help
,
720 return new wxMenuItem(parentMenu
, id
, name
, help
, kind
, subMenu
);
723 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
725 const wxString
& text
,
726 const wxString
& help
,
729 : wxMenuItemBase(parentMenu
, id
, text
, help
, kind
, subMenu
)
734 wxMenuItem::wxMenuItem(wxMenu
*parentMenu
,
736 const wxString
& text
,
737 const wxString
& help
,
740 : wxMenuItemBase(parentMenu
, id
, text
, help
,
741 isCheckable
? wxITEM_CHECK
: wxITEM_NORMAL
, subMenu
)
746 void wxMenuItem::Init(const wxString
& text
)
748 m_labelWidget
= (GtkWidget
*) NULL
;
749 m_menuItem
= (GtkWidget
*) NULL
;
754 wxMenuItem::~wxMenuItem()
756 // don't delete menu items, the menus take care of that
759 // return the menu item text without any menu accels
761 wxString
wxMenuItemBase::GetLabelFromText(const wxString
& text
)
765 for ( const wxChar
*pc
= text
.c_str(); *pc
; pc
++ )
767 if ( *pc
== wxT('_') )
769 // wxGTK escapes "xxx_xxx" to "xxx__xxx"
775 if ( *pc
== wxT('&') )
787 void wxMenuItem::SetText( const wxString
& str
)
789 // Some optimization to avoid flicker
790 wxString oldLabel
= m_text
;
791 oldLabel
= wxStripMenuCodes(oldLabel
.BeforeFirst('\t'));
792 oldLabel
.Replace(wxT("_"), wxT(""));
793 wxString label1
= wxStripMenuCodes(str
.BeforeFirst('\t'));
794 if (oldLabel
== label1
)
803 label
= (GtkLabel
*) m_labelWidget
;
805 label
= GTK_LABEL( GTK_BIN(m_menuItem
)->child
);
808 gtk_label_set( label
, m_text
.mb_str());
810 /* reparse key accel */
811 (void)gtk_label_parse_uline (GTK_LABEL(label
), m_text
.mb_str() );
812 gtk_accel_label_refetch( GTK_ACCEL_LABEL(label
) );
816 // it's valid for this function to be called even if m_menuItem == NULL
817 void wxMenuItem::DoSetText( const wxString
& str
)
819 /* '\t' is the deliminator indicating a hot key */
821 const wxChar
*pc
= str
;
822 for (; (*pc
!= wxT('\0')) && (*pc
!= wxT('\t')); pc
++ )
824 #if GTK_CHECK_VERSION(1, 2, 0)
829 else if ( *pc
== wxT('_') ) // escape underscores
833 #else // GTK+ < 1.2.0
838 #if GTK_CHECK_VERSION(2, 0, 0)
839 else if (*pc
== wxT('/')) // we have to escape slashes
841 m_text
<< wxT("\\/");
843 else if (*pc
== wxT('\\')) // we have to double backslashes
845 m_text
<< wxT("\\\\");
847 #elif GTK_CHECK_VERSION(1, 2, 0)
848 else if (*pc
== wxT('/')) /* we have to filter out slashes ... */
850 m_text
<< wxT('\\'); /* ... and replace them with back slashes */
857 /* only GTK 1.2 knows about hot keys */
860 #if GTK_CHECK_VERSION(1, 2, 0)
866 #endif // GTK+ 1.2.0+
871 wxAcceleratorEntry
*wxMenuItem::GetAccel() const
876 return (wxAcceleratorEntry
*)NULL
;
879 // as wxGetAccelFromString() looks for TAB, insert a dummy one here
881 label
<< wxT('\t') << GetHotKey();
883 return wxGetAccelFromString(label
);
886 #endif // wxUSE_ACCEL
888 void wxMenuItem::Check( bool check
)
890 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
892 if (check
== m_isChecked
)
895 wxMenuItemBase::Check( check
);
901 gtk_check_menu_item_set_state( (GtkCheckMenuItem
*)m_menuItem
, (gint
)check
);
905 wxFAIL_MSG( _T("can't check this item") );
909 void wxMenuItem::Enable( bool enable
)
911 wxCHECK_RET( m_menuItem
, wxT("invalid menu item") );
913 gtk_widget_set_sensitive( m_menuItem
, enable
);
914 wxMenuItemBase::Enable( enable
);
917 bool wxMenuItem::IsChecked() const
919 wxCHECK_MSG( m_menuItem
, FALSE
, wxT("invalid menu item") );
921 wxCHECK_MSG( IsCheckable(), FALSE
,
922 wxT("can't get state of uncheckable item!") );
924 return ((GtkCheckMenuItem
*)m_menuItem
)->active
!= 0;
927 wxString
wxMenuItem::GetFactoryPath() const
929 /* in order to get the pointer to the item we need the item text
930 _without_ underscores */
931 wxString
path( wxT("<main>/") );
933 for ( const wxChar
*pc
= m_text
.c_str(); *pc
; pc
++ )
935 if ( *pc
== wxT('_') || *pc
== wxT('&') )
937 // remove '_' and '&' unconditionally
947 //-----------------------------------------------------------------------------
949 //-----------------------------------------------------------------------------
951 IMPLEMENT_DYNAMIC_CLASS(wxMenu
,wxEvtHandler
)
955 #if GTK_CHECK_VERSION(1, 2, 0)
956 m_accel
= gtk_accel_group_new();
957 m_factory
= gtk_item_factory_new( GTK_TYPE_MENU
, "<main>", m_accel
);
958 m_menu
= gtk_item_factory_get_widget( m_factory
, "<main>" );
960 m_menu
= gtk_menu_new(); // Do not show!
963 m_owner
= (GtkWidget
*) NULL
;
965 #if GTK_CHECK_VERSION(1, 2, 0)
966 /* Tearoffs are entries, just like separators. So if we want this
967 menu to be a tear-off one, we just append a tearoff entry
969 if(m_style
& wxMENU_TEAROFF
)
971 GtkItemFactoryEntry entry
;
972 entry
.path
= (char *)"/tearoff";
973 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
974 entry
.callback_action
= 0;
975 entry
.item_type
= (char *)"<Tearoff>";
976 entry
.accelerator
= (gchar
*) NULL
;
977 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
978 //GtkWidget *menuItem = gtk_item_factory_get_widget( m_factory, "<main>/tearoff" );
980 #endif // GTK+ 1.2.0+
982 // append the title as the very first entry if we have it
994 gtk_widget_destroy( m_menu
);
996 gtk_object_unref( GTK_OBJECT(m_factory
) );
999 bool wxMenu::GtkAppend(wxMenuItem
*mitem
)
1001 GtkWidget
*menuItem
;
1003 #if defined(USE_MENU_BITMAPS) || !GTK_CHECK_VERSION(1, 2, 0)
1004 bool appended
= FALSE
;
1007 #if GTK_CHECK_VERSION(1, 2, 0)
1008 // does this item terminate the current radio group?
1009 bool endOfRadioGroup
= TRUE
;
1010 #endif // GTK+ >= 1.2
1012 if ( mitem
->IsSeparator() )
1014 #if GTK_CHECK_VERSION(1, 2, 0)
1015 GtkItemFactoryEntry entry
;
1016 entry
.path
= (char *)"/sep";
1017 entry
.callback
= (GtkItemFactoryCallback
) NULL
;
1018 entry
.callback_action
= 0;
1019 entry
.item_type
= (char *)"<Separator>";
1020 entry
.accelerator
= (gchar
*) NULL
;
1022 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1024 /* this will be wrong for more than one separator. do we care? */
1025 menuItem
= gtk_item_factory_get_widget( m_factory
, "<main>/sep" );
1027 // we might have a separator inside a radio group
1028 endOfRadioGroup
= FALSE
;
1030 menuItem
= gtk_menu_item_new();
1031 #endif // GTK 1.2/1.0
1033 else if ( mitem
->IsSubMenu() )
1035 #if GTK_CHECK_VERSION(1, 2, 0)
1036 /* text has "_" instead of "&" after mitem->SetText() */
1037 wxString
text( mitem
->GetText() );
1039 /* local buffer in multibyte form */
1042 strcat( buf
, text
.mb_str() );
1044 GtkItemFactoryEntry entry
;
1046 entry
.callback
= (GtkItemFactoryCallback
) 0;
1047 entry
.callback_action
= 0;
1048 entry
.item_type
= (char *)"<Branch>";
1049 entry
.accelerator
= (gchar
*) NULL
;
1051 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1053 wxString
path( mitem
->GetFactoryPath() );
1054 menuItem
= gtk_item_factory_get_item( m_factory
, path
.mb_str() );
1056 menuItem
= gtk_menu_item_new_with_label(mitem
->GetText().mbc_str());
1057 #endif // GTK 1.2/1.0
1059 gtk_menu_item_set_submenu( GTK_MENU_ITEM(menuItem
), 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
);
1067 #ifdef USE_MENU_BITMAPS
1068 else if (mitem
->GetBitmap().Ok()) // An item with bitmap
1070 wxString
text( mitem
->GetText() );
1071 const wxBitmap
*bitmap
= &mitem
->GetBitmap();
1073 menuItem
= gtk_pixmap_menu_item_new ();
1074 GtkWidget
*label
= gtk_accel_label_new (text
.mb_str());
1075 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
1076 gtk_container_add (GTK_CONTAINER (menuItem
), label
);
1077 guint accel_key
= gtk_label_parse_uline (GTK_LABEL(label
), text
.mb_str() );
1078 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label
), menuItem
);
1079 if (accel_key
!= GDK_VoidSymbol
)
1081 gtk_widget_add_accelerator (menuItem
,
1083 gtk_menu_ensure_uline_accel_group (GTK_MENU (m_menu
)),
1087 gtk_widget_show (label
);
1089 mitem
->SetLabelWidget(label
);
1091 GtkWidget
* pixmap
= gtk_pixmap_new( bitmap
->GetPixmap(), bitmap
->GetMask() ? bitmap
->GetMask()->GetBitmap() : (GdkBitmap
* )NULL
);
1092 gtk_widget_show(pixmap
);
1093 gtk_pixmap_menu_item_set_pixmap(GTK_PIXMAP_MENU_ITEM( menuItem
), pixmap
);
1095 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1096 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1098 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1099 gtk_widget_show( menuItem
);
1101 appended
= TRUE
; // We've done this, don't do it again
1103 #endif // USE_MENU_BITMAPS
1104 else // a normal item
1106 #if GTK_CHECK_VERSION(1, 2, 0)
1107 /* text has "_" instead of "&" after mitem->SetText() */
1108 wxString
text( mitem
->GetText() );
1110 /* local buffer in multibyte form */
1113 strncat( buf
, text
.mb_str(), WXSIZEOF(buf
) - 2 );
1114 buf
[WXSIZEOF(buf
) - 1] = '\0';
1116 GtkItemFactoryEntry entry
;
1118 entry
.callback
= (GtkItemFactoryCallback
) gtk_menu_clicked_callback
;
1119 entry
.callback_action
= 0;
1122 const char *item_type
;
1123 switch ( mitem
->GetKind() )
1126 item_type
= "<CheckItem>";
1130 if ( m_pathLastRadio
.empty() )
1132 // start of a new radio group
1133 item_type
= "<RadioItem>";
1134 m_pathLastRadio
= buf
+ 1;
1136 else // continue the radio group
1138 pathRadio
= m_pathLastRadio
;
1139 pathRadio
.Replace("_", "");
1140 pathRadio
.Prepend("<main>/");
1141 item_type
= pathRadio
;
1144 // continue the existing radio group, if any
1145 endOfRadioGroup
= FALSE
;
1149 wxFAIL_MSG( _T("unexpected menu item kind") );
1153 item_type
= "<Item>";
1157 entry
.item_type
= (char *)item_type
; // cast needed for GTK+
1158 entry
.accelerator
= (gchar
*) NULL
;
1161 // due to an apparent bug in GTK+, we have to use a static buffer here -
1162 // otherwise GTK+ 1.2.2 manages to override the memory we pass to it
1164 static char s_accel
[50]; // must be big enougg
1165 wxString
tmp( GetHotKey(*mitem
) );
1166 strncpy(s_accel
, tmp
.mb_str(), WXSIZEOF(s_accel
));
1167 entry
.accelerator
= s_accel
;
1168 #else // !wxUSE_ACCEL
1169 entry
.accelerator
= (char*) NULL
;
1170 #endif // wxUSE_ACCEL/!wxUSE_ACCEL
1172 gtk_item_factory_create_item( m_factory
, &entry
, (gpointer
) this, 2 ); /* what is 2 ? */
1174 wxString
path( mitem
->GetFactoryPath() );
1175 menuItem
= gtk_item_factory_get_widget( m_factory
, path
.mb_str() );
1177 menuItem
= checkable
? gtk_check_menu_item_new_with_label( mitem
->GetText().mb_str() )
1178 : gtk_menu_item_new_with_label( mitem
->GetText().mb_str() );
1180 gtk_signal_connect( GTK_OBJECT(menuItem
), "activate",
1181 GTK_SIGNAL_FUNC(gtk_menu_clicked_callback
),
1183 #endif // GTK+ 1.2/1.0
1186 if ( !mitem
->IsSeparator() )
1188 gtk_signal_connect( GTK_OBJECT(menuItem
), "select",
1189 GTK_SIGNAL_FUNC(gtk_menu_hilight_callback
),
1192 gtk_signal_connect( GTK_OBJECT(menuItem
), "deselect",
1193 GTK_SIGNAL_FUNC(gtk_menu_nolight_callback
),
1197 #if !GTK_CHECK_VERSION(1, 2, 0)
1200 gtk_menu_append( GTK_MENU(m_menu
), menuItem
);
1201 gtk_widget_show( menuItem
);
1205 mitem
->SetMenuItem(menuItem
);
1207 #if GTK_CHECK_VERSION(1, 2, 0)
1208 if ( endOfRadioGroup
)
1210 m_pathLastRadio
.clear();
1212 #endif // GTK+ >= 1.2
1217 bool wxMenu::DoAppend(wxMenuItem
*mitem
)
1219 return GtkAppend(mitem
) && wxMenuBase::DoAppend(mitem
);
1222 bool wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
1224 if ( !wxMenuBase::DoInsert(pos
, item
) )
1228 // GTK+ doesn't have a function to insert a menu using GtkItemFactory (as
1229 // of version 1.2.6), so we first append the item and then change its
1231 if ( !GtkAppend(item
) )
1234 if ( m_style
& wxMENU_TEAROFF
)
1236 // change the position as the first item is the tear-off marker
1240 GtkMenuShell
*menu_shell
= GTK_MENU_SHELL(m_factory
->widget
);
1241 gpointer data
= g_list_last(menu_shell
->children
)->data
;
1242 menu_shell
->children
= g_list_remove(menu_shell
->children
, data
);
1243 menu_shell
->children
= g_list_insert(menu_shell
->children
, data
, pos
);
1247 // this should be easy to do...
1248 wxFAIL_MSG( wxT("not implemented") );
1251 #endif // GTK 1.2/1.0
1254 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
1256 if ( !wxMenuBase::DoRemove(item
) )
1257 return (wxMenuItem
*)NULL
;
1259 // TODO: this code doesn't delete the item factory item and this seems
1260 // impossible as of GTK 1.2.6.
1261 gtk_widget_destroy( item
->GetMenuItem() );
1266 int wxMenu::FindMenuIdByMenuItem( GtkWidget
*menuItem
) const
1268 wxNode
*node
= m_items
.First();
1271 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
1272 if (item
->GetMenuItem() == menuItem
)
1273 return item
->GetId();
1274 node
= node
->Next();
1280 // ----------------------------------------------------------------------------
1282 // ----------------------------------------------------------------------------
1284 #if GTK_CHECK_VERSION(1, 2, 0) && wxUSE_ACCEL
1286 static wxString
GetHotKey( const wxMenuItem
& item
)
1290 wxAcceleratorEntry
*accel
= item
.GetAccel();
1293 int flags
= accel
->GetFlags();
1294 if ( flags
& wxACCEL_ALT
)
1295 hotkey
+= wxT("<alt>");
1296 if ( flags
& wxACCEL_CTRL
)
1297 hotkey
+= wxT("<control>");
1298 if ( flags
& wxACCEL_SHIFT
)
1299 hotkey
+= wxT("<shift>");
1301 int code
= accel
->GetKeyCode();
1316 hotkey
<< wxT('F') << code
- WXK_F1
+ 1;
1319 // TODO: we should use gdk_keyval_name() (a.k.a.
1320 // XKeysymToString) here as well as hardcoding the keysym
1321 // names this might be not portable
1322 case WXK_NUMPAD_INSERT
:
1323 hotkey
<< wxT("KP_Insert" );
1325 case WXK_NUMPAD_DELETE
:
1326 hotkey
<< wxT("KP_Delete" );
1329 hotkey
<< wxT("Insert" );
1332 hotkey
<< wxT("Delete" );
1335 // if there are any other keys wxGetAccelFromString() may
1336 // return, we should process them here
1341 gchar
*name
= gdk_keyval_name((guint
)code
);
1349 wxFAIL_MSG( wxT("unknown keyboard accel") );
1358 #endif // wxUSE_ACCEL
1361 //-----------------------------------------------------------------------------
1362 // substitute for missing GtkPixmapMenuItem
1363 //-----------------------------------------------------------------------------
1365 #ifdef USE_MENU_BITMAPS
1368 * Copyright (C) 1998, 1999, 2000 Free Software Foundation
1369 * All rights reserved.
1371 * This file is part of the Gnome Library.
1373 * The Gnome Library is free software; you can redistribute it and/or
1374 * modify it under the terms of the GNU Library General Public License as
1375 * published by the Free Software Foundation; either version 2 of the
1376 * License, or (at your option) any later version.
1378 * The Gnome Library is distributed in the hope that it will be useful,
1379 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1380 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1381 * Library General Public License for more details.
1383 * You should have received a copy of the GNU Library General Public
1384 * License along with the Gnome Library; see the file COPYING.LIB. If not,
1385 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1386 * Boston, MA 02111-1307, USA.
1392 /* Author: Dietmar Maurer <dm@vlsivie.tuwien.ac.at> */
1394 #include <gtk/gtkaccellabel.h>
1395 #include <gtk/gtksignal.h>
1396 #include <gtk/gtkmenuitem.h>
1397 #include <gtk/gtkmenu.h>
1398 #include <gtk/gtkcontainer.h>
1403 static void gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
);
1404 static void gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
);
1405 static void gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1406 GdkRectangle
*area
);
1407 static gint
gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1408 GdkEventExpose
*event
);
1410 /* we must override the following functions */
1412 static void gtk_pixmap_menu_item_map (GtkWidget
*widget
);
1413 static void gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1414 GtkAllocation
*allocation
);
1415 static void gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1416 gboolean include_internals
,
1417 GtkCallback callback
,
1418 gpointer callback_data
);
1419 static void gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1420 GtkRequisition
*requisition
);
1421 static void gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1424 static void changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
);
1426 static GtkMenuItemClass
*parent_class
= NULL
;
1430 #define BORDER_SPACING 3
1431 #define PMAP_WIDTH 20
1434 gtk_pixmap_menu_item_get_type (void)
1436 static GtkType pixmap_menu_item_type
= 0;
1438 if (!pixmap_menu_item_type
)
1440 GtkTypeInfo pixmap_menu_item_info
=
1442 (char *)"GtkPixmapMenuItem",
1443 sizeof (GtkPixmapMenuItem
),
1444 sizeof (GtkPixmapMenuItemClass
),
1445 (GtkClassInitFunc
) gtk_pixmap_menu_item_class_init
,
1446 (GtkObjectInitFunc
) gtk_pixmap_menu_item_init
,
1447 /* reserved_1 */ NULL
,
1448 /* reserved_2 */ NULL
,
1449 (GtkClassInitFunc
) NULL
,
1452 pixmap_menu_item_type
= gtk_type_unique (gtk_menu_item_get_type (),
1453 &pixmap_menu_item_info
);
1456 return pixmap_menu_item_type
;
1460 * gtk_pixmap_menu_item_new
1462 * Creates a new pixmap menu item. Use gtk_pixmap_menu_item_set_pixmap()
1463 * to set the pixmap wich is displayed at the left side.
1466 * &GtkWidget pointer to new menu item
1470 gtk_pixmap_menu_item_new (void)
1472 return GTK_WIDGET (gtk_type_new (gtk_pixmap_menu_item_get_type ()));
1476 gtk_pixmap_menu_item_class_init (GtkPixmapMenuItemClass
*klass
)
1478 GtkObjectClass
*object_class
;
1479 GtkWidgetClass
*widget_class
;
1480 GtkMenuItemClass
*menu_item_class
;
1481 GtkContainerClass
*container_class
;
1483 object_class
= (GtkObjectClass
*) klass
;
1484 widget_class
= (GtkWidgetClass
*) klass
;
1485 menu_item_class
= (GtkMenuItemClass
*) klass
;
1486 container_class
= (GtkContainerClass
*) klass
;
1488 parent_class
= (GtkMenuItemClass
*) gtk_type_class (gtk_menu_item_get_type ());
1490 widget_class
->draw
= gtk_pixmap_menu_item_draw
;
1491 widget_class
->expose_event
= gtk_pixmap_menu_item_expose
;
1492 widget_class
->map
= gtk_pixmap_menu_item_map
;
1493 widget_class
->size_allocate
= gtk_pixmap_menu_item_size_allocate
;
1494 widget_class
->size_request
= gtk_pixmap_menu_item_size_request
;
1496 container_class
->forall
= gtk_pixmap_menu_item_forall
;
1497 container_class
->remove
= gtk_pixmap_menu_item_remove
;
1499 klass
->orig_toggle_size
= menu_item_class
->toggle_size
;
1500 klass
->have_pixmap_count
= 0;
1504 gtk_pixmap_menu_item_init (GtkPixmapMenuItem
*menu_item
)
1508 mi
= GTK_MENU_ITEM (menu_item
);
1510 menu_item
->pixmap
= NULL
;
1514 gtk_pixmap_menu_item_draw (GtkWidget
*widget
,
1517 g_return_if_fail (widget
!= NULL
);
1518 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1519 g_return_if_fail (area
!= NULL
);
1521 if (GTK_WIDGET_CLASS (parent_class
)->draw
)
1522 (* GTK_WIDGET_CLASS (parent_class
)->draw
) (widget
, area
);
1524 if (GTK_WIDGET_DRAWABLE (widget
) &&
1525 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1526 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1531 gtk_pixmap_menu_item_expose (GtkWidget
*widget
,
1532 GdkEventExpose
*event
)
1534 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1535 g_return_val_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
), FALSE
);
1536 g_return_val_if_fail (event
!= NULL
, FALSE
);
1538 if (GTK_WIDGET_CLASS (parent_class
)->expose_event
)
1539 (* GTK_WIDGET_CLASS (parent_class
)->expose_event
) (widget
, event
);
1541 if (GTK_WIDGET_DRAWABLE (widget
) &&
1542 GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
) {
1543 gtk_widget_draw(GTK_WIDGET(GTK_PIXMAP_MENU_ITEM(widget
)->pixmap
),NULL
);
1550 * gtk_pixmap_menu_item_set_pixmap
1551 * @menu_item: Pointer to the pixmap menu item
1552 * @pixmap: Pointer to a pixmap widget
1554 * Set the pixmap of the menu item.
1559 gtk_pixmap_menu_item_set_pixmap (GtkPixmapMenuItem
*menu_item
,
1562 g_return_if_fail (menu_item
!= NULL
);
1563 g_return_if_fail (pixmap
!= NULL
);
1564 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (menu_item
));
1565 g_return_if_fail (GTK_IS_WIDGET (pixmap
));
1566 g_return_if_fail (menu_item
->pixmap
== NULL
);
1568 gtk_widget_set_parent (pixmap
, GTK_WIDGET (menu_item
));
1569 menu_item
->pixmap
= pixmap
;
1571 if (GTK_WIDGET_REALIZED (pixmap
->parent
) &&
1572 !GTK_WIDGET_REALIZED (pixmap
))
1573 gtk_widget_realize (pixmap
);
1575 if (GTK_WIDGET_VISIBLE (pixmap
->parent
)) {
1576 if (GTK_WIDGET_MAPPED (pixmap
->parent
) &&
1577 GTK_WIDGET_VISIBLE(pixmap
) &&
1578 !GTK_WIDGET_MAPPED (pixmap
))
1579 gtk_widget_map (pixmap
);
1582 changed_have_pixmap_status(menu_item
);
1584 if (GTK_WIDGET_VISIBLE (pixmap
) && GTK_WIDGET_VISIBLE (menu_item
))
1585 gtk_widget_queue_resize (pixmap
);
1589 gtk_pixmap_menu_item_map (GtkWidget
*widget
)
1591 GtkPixmapMenuItem
*menu_item
;
1593 g_return_if_fail (widget
!= NULL
);
1594 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (widget
));
1596 menu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1598 GTK_WIDGET_CLASS(parent_class
)->map(widget
);
1600 if (menu_item
->pixmap
&&
1601 GTK_WIDGET_VISIBLE (menu_item
->pixmap
) &&
1602 !GTK_WIDGET_MAPPED (menu_item
->pixmap
))
1603 gtk_widget_map (menu_item
->pixmap
);
1607 gtk_pixmap_menu_item_size_allocate (GtkWidget
*widget
,
1608 GtkAllocation
*allocation
)
1610 GtkPixmapMenuItem
*pmenu_item
;
1612 pmenu_item
= GTK_PIXMAP_MENU_ITEM(widget
);
1614 if (pmenu_item
->pixmap
&& GTK_WIDGET_VISIBLE(pmenu_item
))
1616 GtkAllocation child_allocation
;
1619 border_width
= GTK_CONTAINER (widget
)->border_width
;
1621 child_allocation
.width
= pmenu_item
->pixmap
->requisition
.width
;
1622 child_allocation
.height
= pmenu_item
->pixmap
->requisition
.height
;
1623 child_allocation
.x
= border_width
+ BORDER_SPACING
;
1624 child_allocation
.y
= (border_width
+ BORDER_SPACING
1625 + (((allocation
->height
- child_allocation
.height
) - child_allocation
.x
)
1626 / 2)); /* center pixmaps vertically */
1627 gtk_widget_size_allocate (pmenu_item
->pixmap
, &child_allocation
);
1630 if (GTK_WIDGET_CLASS (parent_class
)->size_allocate
)
1631 GTK_WIDGET_CLASS(parent_class
)->size_allocate (widget
, allocation
);
1635 gtk_pixmap_menu_item_forall (GtkContainer
*container
,
1636 gboolean include_internals
,
1637 GtkCallback callback
,
1638 gpointer callback_data
)
1640 GtkPixmapMenuItem
*menu_item
;
1642 g_return_if_fail (container
!= NULL
);
1643 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1644 g_return_if_fail (callback
!= NULL
);
1646 menu_item
= GTK_PIXMAP_MENU_ITEM (container
);
1648 if (menu_item
->pixmap
)
1649 (* callback
) (menu_item
->pixmap
, callback_data
);
1651 GTK_CONTAINER_CLASS(parent_class
)->forall(container
,include_internals
,
1652 callback
,callback_data
);
1656 gtk_pixmap_menu_item_size_request (GtkWidget
*widget
,
1657 GtkRequisition
*requisition
)
1659 GtkPixmapMenuItem
*menu_item
;
1660 GtkRequisition req
= {0, 0};
1662 g_return_if_fail (widget
!= NULL
);
1663 g_return_if_fail (GTK_IS_MENU_ITEM (widget
));
1664 g_return_if_fail (requisition
!= NULL
);
1666 GTK_WIDGET_CLASS(parent_class
)->size_request(widget
,requisition
);
1668 menu_item
= GTK_PIXMAP_MENU_ITEM (widget
);
1670 if (menu_item
->pixmap
)
1671 gtk_widget_size_request(menu_item
->pixmap
, &req
);
1673 requisition
->height
= MAX(req
.height
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
, (unsigned int) requisition
->height
);
1674 requisition
->width
+= (req
.width
+ GTK_CONTAINER(widget
)->border_width
+ BORDER_SPACING
);
1678 gtk_pixmap_menu_item_remove (GtkContainer
*container
,
1682 gboolean widget_was_visible
;
1684 g_return_if_fail (container
!= NULL
);
1685 g_return_if_fail (GTK_IS_PIXMAP_MENU_ITEM (container
));
1686 g_return_if_fail (child
!= NULL
);
1687 g_return_if_fail (GTK_IS_WIDGET (child
));
1689 bin
= GTK_BIN (container
);
1690 g_return_if_fail ((bin
->child
== child
||
1691 (GTK_PIXMAP_MENU_ITEM(container
)->pixmap
== child
)));
1693 widget_was_visible
= GTK_WIDGET_VISIBLE (child
);
1695 gtk_widget_unparent (child
);
1696 if (bin
->child
== child
)
1699 GTK_PIXMAP_MENU_ITEM(container
)->pixmap
= NULL
;
1700 changed_have_pixmap_status(GTK_PIXMAP_MENU_ITEM(container
));
1703 if (widget_was_visible
)
1704 gtk_widget_queue_resize (GTK_WIDGET (container
));
1708 /* important to only call this if there was actually a _change_ in pixmap == NULL */
1710 changed_have_pixmap_status (GtkPixmapMenuItem
*menu_item
)
1712 if (menu_item
->pixmap
!= NULL
) {
1713 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
+= 1;
1715 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 1) {
1716 /* Install pixmap toggle size */
1717 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= MAX(GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
, PMAP_WIDTH
);
1720 GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
-= 1;
1722 if (GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->have_pixmap_count
== 0) {
1723 /* Install normal toggle size */
1724 GTK_MENU_ITEM_GET_CLASS(menu_item
)->toggle_size
= GTK_PIXMAP_MENU_ITEM_GET_CLASS(menu_item
)->orig_toggle_size
;
1728 /* Note that we actually need to do this for _all_ GtkPixmapMenuItem
1729 whenever the klass->toggle_size changes; but by doing it anytime
1730 this function is called, we get the same effect, just because of
1731 how the preferences option to show pixmaps works. Bogus, broken.
1733 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(menu_item
)))
1734 gtk_widget_queue_resize(GTK_WIDGET(menu_item
));
1737 #endif // USE_MENU_BITMAPS